Нечеткий поиск по фразе Lucene (FuzzyQuery + SpanQuery)
Я ищу способ кодирования нечеткого запроса lucene, который ищет все документы, которые имеют отношение к конкретной фразе. Если я выполню поиск "Оценки сотрудника Мозаики", в результате будет возвращен документ, содержащий "Оценки большинства сотрудников".
Я пытался использовать:
FuzzyQeury = new FuzzyQuery(new Term("contents","mosa employee appreicata"))
К сожалению, эмпирически это не работает. FuzzyQuery использует расстояние редактора, теоретически, "оценка сотрудника мозаики" должна совпадать с "оценкой большинства сотрудников", если дано соответствующее расстояние. Это кажется немного странным.
Есть какие-нибудь подсказки? Спасибо.
2 ответа
Ответ от femtoRgon великолепен! Спасибо.
Есть еще один способ решить эту проблему.
//declare a mutilphrasequery
MultiPhraseQuery childrenInOrder = new MultiPhraseQuery();
//user fuzzytermenum to enumerate your query string
FuzzyTermEnum fuzzyEnumeratedTerms1 = new FuzzyTermEnum(reader, new Term(searchField,"mosa"));
FuzzyTermEnum fuzzyEnumeratedTerms2 = new FuzzyTermEnum(reader, new Term(searchField,"employee"));
FuzzyTermEnum fuzzyEnumeratedTerms3 = new FuzzyTermEnum(reader, new Term(searchField,"appreicata"));
//this basically pull out the possbile terms from the index
Term termHolder1 = fuzzyEnumeratedTerms1.term();
Term termHolder2 = fuzzyEnumeratedTerms2.term();
Term termHolder3 = fuzzyEnumeratedTerms3.term();
//put the possible terms into multiphrasequery
if (termHolder1==null){
childrenInOrder.add(new Term(searchField,"mosa"));
}else{
childrenInOrder.add(fuzzyEnumeratedTerms1.term());
}
if (termHolder2==null){
childrenInOrder.add(new Term(searchField,"employee"));
}else{
childrenInOrder.add(fuzzyEnumeratedTerms2.term());
}
if (termHolder3==null){
childrenInOrder.add(new Term(searchField,"appreicata"));
}else{
childrenInOrder.add(fuzzyEnumeratedTerms3.term());
}
//close it - it is important to close it
fuzzyEnumeratedTerms1.close();
fuzzyEnumeratedTerms2.close();
fuzzyEnumeratedTerms3.close();
Здесь есть две вероятные проблемы. Во-первых: я предполагаю, что поле "содержание" анализируется таким образом, что "большинство сотрудников ценят" - это не термин, а три термина. Определение в качестве одного термина не подходит в этом случае.
Однако, даже если в указанном контенте содержится один термин, вторая вероятная проблема заключается в том, что между терминами слишком большое расстояние, чтобы найти совпадение. Расстояние Дамерау-Левенштейн между mosa employee appreicata
а также most employees appreciate
4 (приблизительное расстояние, между прочим, между моим средним первым выстрелом в правописании "Дамерау-Левенштейн" и правильным правописанием). Fuzzy Query, начиная с 4.0, обрабатывает расстояния редактирования не более 2 из-за ограничений производительности и предположения, что большие расстояния обычно не особенно актуальны.
Если вам нужно выполнить запрос фразы с нечеткими терминами, вы должны посмотреть либо MultiPhraseQuery
или объединить набор SpanQueries
(особенно SpanMultiTermQueryWrapper
а также SpanNearQuery
) для удовлетворения ваших потребностей.
SpanQuery[] clauses = new SpanQuery[3];
clauses[0] = new SpanMultiTermQueryWrapper(new FuzzyQuery(new Term("contents", "mosa")));
clauses[1] = new SpanMultiTermQueryWrapper(new FuzzyQuery(new Term("contents", "employee")));
clauses[2] = new SpanMultiTermQueryWrapper(new FuzzyQuery(new Term("contents", "appreicata")));
SpanNearQuery query = new SpanNearQuery(clauses, 0, true)
И поскольку ни один из отдельных терминов не имеет расстояния редактирования больше 2, это должно быть более эффективным.
ComplexPhraseQueryParser
обрабатывает нечеткий поиск по словосочетанию - то есть указывает слова, которые следует искать нечетко, а те, которые не следует. Работает следующим образом
Query query = new ComplexPhraseQueryParser("content", analyzer)
.parse("some test~ query~ blah blah");
Кажется, работает хорошо. Однако не уверен насчет производительности, но, похоже, хорошо работает с небольшими наборами данных.
У меня было немного (очень маленькое) смешивание со следующим:
String[] searchTerms = searchString.split(" ");
FuzzyLikeThisQuery fltw = new FuzzyLikeThisQuery(searchTerms.length, new StandardAnalyzer());
Arrays.stream(searchTerms)
.forEach(term -> fltq.addTerms(term, FIELD, SIMILARITY_IN_EDITS, PREFIX_LENGTH);
Этот запрос сопоставляет слишком отдаленные строки с индексом. Строки, которые не совпадают, - это строки, в которых каждое из терминов удалено более чем на 2 редактирования от терминов, используемых в проиндексированном контенте.