Java 具有自动完成和模糊功能的Hibernate搜索

Java 具有自动完成和模糊功能的Hibernate搜索,java,hibernate,lucene,hibernate-search,Java,Hibernate,Lucene,Hibernate Search,我正在尝试创建StingUtils方法的一个Hibernate搜索表示,以及模糊搜索匹配 假设用户写入字母“p”,他们将获得包含字母“p”的所有匹配项(无论字母位于各个匹配项的开头、中间还是结尾) 当他们形成诸如“彼得”这样的单词时,他们还应该接受模糊匹配,例如“彼特”、“彼特”和“彼德” 我正在使用great Response中提供的自定义查询和索引分析器,因为我需要minGramSizeat 1以允许自动完成功能,同时我还希望多词用户输入由空格分隔,例如“Peter的欧元账户”,可以在不同的

我正在尝试创建StingUtils方法的一个Hibernate搜索表示,以及模糊搜索匹配

假设用户写入字母“p”,他们将获得包含字母“p”的所有匹配项(无论字母位于各个匹配项的开头、中间还是结尾)

当他们形成诸如“彼得”这样的单词时,他们还应该接受模糊匹配,例如“彼特”、“彼特”和“彼德”

我正在使用great Response中提供的自定义查询和索引分析器,因为我需要
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”,我们将得到以下结果:

  • Petarr(非精确匹配)
  • peter(非精确匹配)
  • 。。。4.PETAR(精确匹配

    同样适用于用户输入的“peter”,其中第一个结果是“Petero”,第二个结果是“peter”(第二个应该是第一个)

    我还需要在多词查询中只包含精确匹配项-例如,如果我开始写“Account for…”,我希望所有匹配结果都包含短语“Account for”,并最终包含基于该短语的模糊相关术语(基本上与containsIgnoreCase()相同)方法,只是尝试添加模糊支持)

    然而,我想这与
    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);