Hibernate поиск нечетких более 2 - PullRequest
0 голосов
/ 13 апреля 2020

У меня есть Java бэкэнд с hibernate, lucene и hibernate-search. Теперь я хочу сделать нечеткий запрос, НО вместо 0, 1 или 2, я хочу разрешить больше «различий» между запросом и ожидаемым результатом (чтобы компенсировать, например, опечатку в длинных словах). Есть ли способ добиться этого? Максимум допустимых различий будет позже рассчитан по длине запроса.
Для чего я хочу, это автозаполнение поиска с исправлением неправильных букв. Это автозаполнение должно искать только пропущенные символы позади данного запроса, а не перед ним. Если символы перед запросом по сравнению с записью отсутствуют, они должны учитываться как разница.

Примеры. Максимально допустимое количество различных символов в этом примере - 2. fooo должно соответствовать

fooo       (no difference)
fooobar    (only characters added -> autocomplete)
fouubar    (characters added and misspelled -> autocomplete and spelling correction)

fooo НЕ должно совпадать

barfooo    (we only allow additional characters behind the query, but this example is less important)
fuuu       (more than 2 differences)

Это мой текущий код для запроса SQL:

FullTextEntityManager fullTextEntityManager = this.sqlService.getFullTextEntityManager();
QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(MY_CLASS.class).overridesForField("name", "foo").get();
Query query = queryBuilder.keyword().fuzzy().withEditDistanceUpTo(2).onField("name").matching("QUERY_TO_MATCH").createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(query, MY_CLASS.class);
List<MY_CLASS> results = fullTextQuery.getResultList();

Примечания:
1. Я использую org.apache.lucene.analysis.ngram.EdgeNGramFilterFactory для индексации, но это не должно вносить никаких изменений.
2. Это использование пользовательской среды, которая не является открытым исходным кодом. Вы можете просто проигнорировать sqlService, он предоставляет только FullTextEntityManager и обрабатывает все, что находится в спящем режиме, для которого каждый раз не требуется настраиваемый код.
3. Этот код уже работает, но только с withEditDistanceUpTo(2), что означает максимум 2 «различия» между QUERY_TO_MATCH и соответствующей записью в базе данных или индексе. Пропущенные символы также учитываются как различия.
4. withEditDistanceUpTo(2) не принимает значения больше 2.

У кого-нибудь есть идеи для достижения этой цели?

Ответы [ 2 ]

0 голосов
/ 03 мая 2020

Хорошо, мы с другом нашли решение. Мы нашли вопрос в журнале изменений lucene, который запрашивает ту же функцию, и мы реализовали решение : в версии lucene для песочницы есть SlowFuzzyQuery. Это медленнее (очевидно), но поддерживает editDistance больше, чем 2.

0 голосов
/ 14 апреля 2020

Мне неизвестно о каком-либо решении, в котором вы бы указали точное количество допустимых изменений.

У этого подхода, во всяком случае, есть серьезные недостатки: что означает совпадение слова "foo" с числом до 3 изменения? Просто сопоставить что-нибудь? Как видите, решение, которое работает с различными длинами сроков, может быть лучше.

Одним из решений является индексирование n-грамм. Я говорю не о ребро-нграммах, как вы уже делаете, а о реальных нграммах, извлеченных из всего термина, а не только из ребер. Таким образом, при индексации 2 граммов foooo вы должны индексировать:

  • fo
  • oo (встречается несколько раз)

И при запросе термин fouuu будет преобразован в:

  • fo
  • ou
  • uu

... и он будет соответствовать индексируемому документу, поскольку у них есть хотя бы один общий термин (fo).

Очевидно, что есть некоторые недостатки. С 2 граммами термин fuuuu не будет соответствовать foooo, но термин barfooo будет соответствовать, потому что они имеют общий 2 грамма. Таким образом, вы получите ложные срабатывания. Чем дольше граммы, тем меньше вероятность получения ложных срабатываний, но тем менее нечетким будет ваш поиск.

Эти ложные срабатывания можно убрать go, полагаясь на оценку и сортировку по баллам. разместить лучшие совпадения первыми в списке результатов. Например, вы можете настроить фильтр ngram для сохранения исходного термина, чтобы fooo преобразовывалось в [fooo, fo, oo] вместо просто [fo, oo], и, таким образом, точный поиск fooo будет иметь лучший результат для документа, содержащего fooo, чем для документа, содержащего barfooo (так как совпадений больше). Вы также можете настроить несколько отдельных полей: одно без ngram, одно с 3-граммами, одно с 2-граммами и создать логический запрос с предложением should для каждого поля: чем больше совпадений, тем выше будет оценка да, и чем выше вы найдете документ в хитах.

Кроме того, я бы сказал, что fooo и аналогичные являются действительно искусственными примерами, и вы вряд ли будете иметь эти термины в реальном мире. набор данных; Вы должны попробовать любое решение с реальным набором данных и посмотреть, работает ли оно достаточно хорошо. Если вам нужен нечеткий поиск, вам придется принять некоторые ложных срабатываний: вопрос не в том, существуют ли они, а в том, достаточно ли они редки, чтобы пользователи все еще могли легко найти то, что искали.

Чтобы использовать нграммы, примените фильтр n-грамм, используя org.apache.lucene.analysis.ngram.NGramFilterFactory. Применяйте его как при индексировании, так и при запросах. Используйте параметры minGramSize / maxGramSize, чтобы настроить размер нграмм, и keepShortTerm (true / false), чтобы указать, сохранять ли исходный термин или нет.

Вы можете сохранить фильтр грани-нграмм или нет; посмотреть, если это улучшает актуальность ваших результатов? Я подозреваю, что это может немного улучшить релевантность, если вы используете keepShortTerm = true. В любом случае, обязательно примените фильтр ребра-ngram перед фильтром ngram.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...