但是,完全匹配的案例不会出现在搜索结果中:
只需使用 两个 查询而不是一个:
编辑:您还需要为自动完成和“精确”匹配设置两个单独的字段;在底部查看我的编辑。
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 (yet) 不支持此功能,因此您基本上必须自己构建查询。我已经看到的一个 hack 是这样的:
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,这样可以更好地忽略非常频繁的术语。见here for the setting。这应该会提高使用相同分析器的评分。
其次,您可以尝试设置两个字段而不是一个:一个用于模糊自动完成,一个用于非模糊、完整匹配。这可能会提高精确匹配的分数,因为用于精确匹配的字段索引的无意义术语将减少。只需这样做:
@Field(name = "name", analyzer = @Analyzer(definition = "text")
@Field(name = "name_autocomplete", analyzer = @Analyzer(definition = "edgeNgram")
private String name;
分析器“文本”只是来自answer you linked 的分析器“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);
当然,不要忘记在这些更改之后重新索引。