Java 具有自动完成和模糊功能的Hibernate搜索
我正在尝试创建StingUtils方法的一个Hibernate搜索表示,以及模糊搜索匹配 假设用户写入字母“p”,他们将获得包含字母“p”的所有匹配项(无论字母位于各个匹配项的开头、中间还是结尾) 当他们形成诸如“彼得”这样的单词时,他们还应该接受模糊匹配,例如“彼特”、“彼特”和“彼德” 我正在使用great Response中提供的自定义查询和索引分析器,因为我需要Java 具有自动完成和模糊功能的Hibernate搜索,java,hibernate,lucene,hibernate-search,Java,Hibernate,Lucene,Hibernate Search,我正在尝试创建StingUtils方法的一个Hibernate搜索表示,以及模糊搜索匹配 假设用户写入字母“p”,他们将获得包含字母“p”的所有匹配项(无论字母位于各个匹配项的开头、中间还是结尾) 当他们形成诸如“彼得”这样的单词时,他们还应该接受模糊匹配,例如“彼特”、“彼特”和“彼德” 我正在使用great Response中提供的自定义查询和索引分析器,因为我需要minGramSizeat 1以允许自动完成功能,同时我还希望多词用户输入由空格分隔,例如“Peter的欧元账户”,可以在不同的
minGramSize
at 1以允许自动完成功能,同时我还希望多词用户输入由空格分隔,例如“Peter的欧元账户”,可以在不同的情况下(较低或较高)
因此,用户应该能够键入“AND”,并将上面的示例作为匹配项接收
目前,我正在使用以下查询:
org.apache.lucene.search.Query fuzzySearchByName = qb.keyword().fuzzy()
.withEditDistanceUpTo(1).onField("name")
.matching(userInput).createQuery();
booleanQuery.add(fuzzySearchByName, BooleanClause.Occur.MUST);
但是,精确匹配案例不会在搜索结果中显示:
如果我们输入“petar”,我们将得到以下结果:
minGramSize
为1和WhitespaceTokenizerFactory
相反
但是,精确匹配案例在搜索结果中不存在相关性:
只需使用两个查询,而不是一个查询:
编辑:您还需要为自动完成和“精确”匹配设置两个单独的字段;请参见底部的“我的编辑”
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
(即无标记化)
如果您想匹配以任何顺序包含所有单词的文档,那么。。。这不太明显。HibernateSearch()中不支持这一点,因此您必须自己构建查询。我已经看到的一个黑客是这样的:
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的自定义子类中的某些方法。我没有尝试过这个,但它可能会起作用:
编辑:minGramSize为1时,您将得到许多非常常见的术语:从单词开头提取的单个或两个字符的术语。这很可能会导致许多不必要的比赛,这些比赛的分数很高(因为这些术语很频繁),并且可能会淹没精确的比赛
首先,您可以尝试将相似度(~评分公式)设置为org.apache.lucene.search.complications.BM25Similarity
,这样可以更好地忽略非常频繁的术语。看见使用相同的分析器,这将提高评分
其次,您可以尝试设置两个字段而不是一个:一个字段用于模糊自动完成,另一个字段用于非模糊完全匹配。这可能会提高精确匹配的分数,因为对于用于精确匹配的字段,索引的无意义术语会更少。只要这样做:
@Field(name = "name", analyzer = @Analyzer(definition = "text")
@Field(name = "name_autocomplete", analyzer = @Analyzer(definition = "edgeNgram")
private String name;
分析器“文本”只是来自的分析器“Edengram_查询”;改名吧
继续编写两个查询,而不是如上所述的一个查询,但确保针对两个不同的字段:
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);
当然,不要忘了在这些更改之后重新编制索引。谢谢您的精彩回答,但不幸的是,精确匹配没有收到任何提示,即使在增强时也是如此。然而,一个更严重的问题是,“来自bo”的查询返回名为“BBS”和“EUR负载测试”的文档。导致此问题的原因是什么?是否与我的原始设置有关?“加载”以“Lo”开头,与“Bo”的编辑距离在1以内,因此匹配。与“BBS”=>“BB”=>匹配的“Bo”相同。这是对你的模糊搜索。。。关于得分,我更新了我的答案。
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);