Однако случаи точного соответствия не получают присутствия в результатах поиска:
Просто используйте два запроса вместо одного:
РЕДАКТИРОВАТЬ : вам также нужно будет установить два отдельных поля для автозаполнения и «точного» соответствия; см. мои правки внизу.
org.apache.lucene.search.Query exactSearchByName = qb.keyword().onField("name")
.matching(userInput).createQuery();
org.apache.lucene.search.Query fuzzySearchByName = qb.keyword().fuzzy()
.withEditDistanceUpTo(1).onField("name")
.matching(userInput).createQuery();
org.apache.lucene.search.Query searchByName = qb.boolean().should(exactSearchByName).should(fuzzySearchByName).createQuery();
booleanQuery.add(searchByName, BooleanClause.Occur.MUST);
Это будет соответствовать документам, которые содержат пользовательский ввод точно или приблизительно, так что это будет соответствовать тем же документам, что и ваш пример. Однако документы, которые содержат пользовательский ввод, точно будут соответствовать обоим запросам, в то время как документы, которые содержат только что-то подобное, будут соответствовать только нечеткому запросу. В результате точные совпадения будут иметь более высокий балл и в итоге окажутся выше в списке результатов.
Если точные совпадения недостаточно высоки, попробуйте добавить повышение к запросу exactSearchByName
:
org.apache.lucene.search.Query exactSearchByName = qb.keyword().onField("name")
.matching(userInput)
.boostedTo(4.0f)
.createQuery();
Я полагаю, однако, что это противоречит minGramSize = 1 и WhitespaceTokenizerFactory?
Если вы хотите сопоставить документы, которые содержат любое слово (но не обязательно все слова), появляющиеся в пользовательском вводе и чтобы поместить документы, содержащие больше слов, выше в списке результатов, выполните то, что я объяснил выше.
Если вы хотите сопоставить документы, которые содержат все слова в том же порядке, используйте KeywordTokenizerFactory
(т. е. без лексем).
Если вы хотите сопоставить документы, содержащие все слова в любом порядке, ну ... это менее очевидно. В Hibernate Search такой поддержки нет (пока ), поэтому вам, по сути, придется создавать запрос самостоятельно. Один хак, который я уже видел, выглядит примерно так:
Analyzer analyzer = fullTextSession.getSearchFactory().getAnalyzer( "myAnalyzer" );
QueryParser queryParser = new QueryParser( "name", analyzer );
queryParser.setOperator( Operator.AND ); // Match *all* terms
Query luceneQuery = queryParser.parse( userInput );
... но это не будет генерировать нечеткие запросы. Если вам нужны нечеткие запросы, вы можете попробовать переопределить некоторые методы в пользовательском подклассе QueryParser. Я не пробовал это, но это может работать:
public final class FuzzyQueryParser extends QueryParser {
private final int maxEditDistance;
private final int prefixLength;
public FuzzyQueryBuilder(String fieldName, Analyzer analyzer, int maxEditDistance, int prefixLength) {
super( fieldName, analyzer );
this.maxEditDistance = maxEditDistance;
this.prefixLength = prefixLength;
}
@Override
protected Query newTermQuery(Term term) {
return new FuzzyQuery( term, maxEditDistance, prefixLength );
}
}
РЕДАКТИРОВАТЬ : с minGramSize 1, вы получите много очень частых терминов: одно- или двухсимвольные термины, извлеченные из начала слов. Скорее всего, это приведет к множеству нежелательных совпадений, которые будут набраны с высокой оценкой (поскольку термины являются частыми) и, вероятно, будут затоплены точные совпадения.
Сначала вы можете попробовать установить сходство (~ формула оценки) на org.apache.lucene.search.similarities.BM25Similarity
, который лучше игнорировать очень частые термины. См. здесь для настройки . Это должно улучшить оценку с помощью тех же анализаторов.
Во-вторых, вы можете попробовать настроить два поля вместо одного: одно поле для нечеткого автозаполнения и одно для нечетких, полных совпадений. Это может улучшить счет точных совпадений, поскольку для поля, используемого для точных совпадений, будет меньше бессмысленных терминов, индексируемых. Просто сделайте это:
@Field(name = "name", analyzer = @Analyzer(definition = "text")
@Field(name = "name_autocomplete", analyzer = @Analyzer(definition = "edgeNgram")
private String name;
Анализатор "text" - это просто анализатор "edgeNGram_query" из ответа , который вы связали ; просто переименуйте его.
Продолжайте писать два запроса вместо одного, как описано выше, но убедитесь, что вы указали два разных поля:
org.apache.lucene.search.Query exactSearchByName = qb.keyword().onField("name")
.matching(userInput).createQuery();
org.apache.lucene.search.Query fuzzySearchByName = qb.keyword().fuzzy()
.withEditDistanceUpTo(1).onField("name_autocomplete")
.matching(userInput).createQuery();
org.apache.lucene.search.Query searchByName = qb.boolean().should(exactSearchByName).should(fuzzySearchByName).createQuery();
booleanQuery.add(searchByName, BooleanClause.Occur.MUST);
Не забудьте переиндексировать после этих изменений конечно.