如何在hibernate搜索中使用多个查询字符串检索精确的搜索结果

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在hibernate搜索中使用多个查询字符串检索精确的搜索结果相关的知识,希望对你有一定的参考价值。

我们正在使用hibernate search orm 5.9.2,并希望获得如下的确切搜索结果:

如果用户以

John -> all data with John should display
John Murphy -> all data with John murphy should display
John murphy Columbia -> Only data with John murphy Columbia should display 
John murphy Columbia SC -> Only data with John murphy Columbia should display  
John murphy Columbia SC 29201 -> Only data with John murphy Columbia SC 29201

29201 -> Only data with 29201 as zipcode should be displayed.
and so on...

基本上我们试图在索引上的多个字段中搜索确切的记录。

我们在名称,地址1,地址2,城市,邮政编码,州等字段中包含此数据的实体。

我们尝试过bool()(带有should / must)查询,但由于我们不确定用户首先输入什么数据,因此可能是zipcode,state,city在文本搜索中的任何位置。

请分享您关于分析器/策略的知识/逻辑,我们可以使用hibernate search / lucene来完成此任务。

以下是索引结构:

> {
>         "_index" : "client_master_index_0300",
>         "_type" : "com.csc.pt.svc.data.to.Basclt0300TO",
>         "_id" : "518,1",
>         "_score" : 4.0615783,
>         "_source" : {
>           "id" : "518,1",
>           "cltseqnum" : 518,
>           "addrseqnum" : "1",
>           "addrln1" : "Dba",
>           "addrln2" : "Betsy Evans",
>           "city" : "SDA",
>           "state" : "SC",
>           "zipcode" : "89756-4531",
>           "country" : "USA",
>           "basclt0100to" : {
>             "cltseqnum" : 518,
>             "clientname" : "Betsy Evans",
>             "longname" : "Betsy Evans",
>             "id" : "518"
>           },
>           "basclt0900to" : {
>             "cltseqnum" : 518,
>             "id" : "518"
>           }
>         }
>       }

以下是输入

Akash Agrawal 29021

响应包含与akash,agrwal,29,2,1,01等匹配的所有记录...

我们想要实现的是确切的搜索结果,对于上面的搜索输入,结果应该只包含Akash Agrawal 29201的数据而不包含其他数据。

我们基本上搜索basclt0100to.longname,addrln1,addrln2,city,state,zipcode,country。

索引定义如下

> {
>   "client_master_index_0300" : {
>     "aliases" : { },
>     "mappings" : {
>       "com.csc.pt.svc.data.to.Basclt0300TO" : {
>         "dynamic" : "strict",
>         "properties" : {
>           "addrln1" : {
>             "type" : "text",
>             "store" : true
>           },
>           "addrln2" : {
>             "type" : "text",
>             "store" : true
>           },
>           "addrln3" : {
>             "type" : "text",
>             "store" : true
>           },
>           "addrseqnum" : {
>             "type" : "text",
>             "store" : true
>           },
>           "basclt0100to" : {
>             "properties" : {
>               "clientname" : {
>                 "type" : "text",
>                 "store" : true
>               },
>               "cltseqnum" : {
>                 "type" : "long",
>                 "store" : true
>               },
>               "firstname" : {
>                 "type" : "text",
>                 "store" : true
>               },
>               "id" : {
>                 "type" : "keyword",
>                 "store" : true,
>                 "norms" : true
>               },
>               "longname" : {
>                 "type" : "text",
>                 "store" : true
>               },
>               "midname" : {
>                 "type" : "text",
>                 "store" : true
>               }
>             }
>           },
>           "basclt0900to" : {
>             "properties" : {
>               "cltseqnum" : {
>                 "type" : "long",
>                 "store" : true
>               },
>               "email1" : {
>                 "type" : "text",
>                 "store" : true
>               },
>               "id" : {
>                 "type" : "keyword",
>                 "store" : true,
>                 "norms" : true
>               }
>             }
>           },
>           "city" : {
>             "type" : "text",
>             "store" : true
>           },
>           "cltseqnum" : {
>             "type" : "long",
>             "store" : true
>           },
>           "country" : {
>             "type" : "text",
>             "store" : true
>           },
>           "id" : {
>             "type" : "keyword",
>             "store" : true
>           },
>           "state" : {
>             "type" : "text",
>             "store" : true
>           },
>           "zipcode" : {
>             "type" : "text",
>             "store" : true
>           }
>         }
>       }
>     },
>     "settings" : {
>       "index" : {
>         "creation_date" : "1535607176216",
>         "number_of_shards" : "5",
>         "number_of_replicas" : "1",
>         "uuid" : "x4R71LNCTBSyO9Taf8siOw",
>         "version" : {
>           "created" : "6030299"
>         },
>         "provided_name" : "client_master_index_0300"
>       }
>     }
>   }
> }

我到现在为止尝试使用edgengraanalyzer,lucene查询的标准分析器。我尝试过Bool()查询,关键字查询,短语,尝试了文档下可用的所有内容。

但我确信我错过了我们应该使用的策略/逻辑。

下面是我正在使用的当前查询,并提供附加的快照结果

 Query finalQuery = queryBuilder.simpleQueryString()
            .onFields("basclt0100to.longname", "addrln1", "addrln2" 
                ,"city","state","zipcode", "country")
            .withAndAsDefaultOperator()
            .matching(lowerCasedSearchTerm)
            .createQuery();

        FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(finalQuery, Basclt0300TO.class);
        fullTextQuery.setMaxResults(this.data.getPageSize()).setFirstResult(this.data.getPageSize());

        List<String> projectedFields = new ArrayList<String>();
        for (String fieldName : projections)
                projectedFields.add(fieldName);

        @SuppressWarnings("unchecked")
        List<Cltj001ElasticSearchResponseTO> results = fullTextQuery.
        setProjection(projectedFields.toArray(new String[projectedFields.size()]))
        .setResultTransformer( new BasicTransformerAdapter() {
            @Override
            public Cltj001ElasticSearchResponseTO transformTuple(Object[] tuple, String[] aliases) {
                return   new Cltj001ElasticSearchResponseTO((String) tuple[0], (long) tuple[1],
                            (String) tuple[2], (String) tuple[3], (String) tuple[4],
                            (String) tuple[5],(String) tuple[6], (String) tuple[7], (String) tuple[8]);

            }
        })
        .getResultList();
        resultsClt0300MasterIndexList = results;

searched for: akash 29201 searched for : akash 1 main

在这里你可以看到我们拥有包含akash,sh,29,292,29201的所有数据。

预期成绩:

Akash Agrawal - 29201 Akash Agrawal - 1条主要街道,SC,29201

基本上只有包含/匹配输入字符串的精确数据。

使用的分析仪:指数时间

    @AnalyzerDef(name = "autocompleteEdgeAnalyzer",

//Split input into tokens according to tokenizer
                tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
                         filters = {
                                   @TokenFilterDef(factory = LowerCaseFilterFactory.class),
                                    @TokenFilterDef(factory = StopFilterFactory.class),
                                    @TokenFilterDef(
                                            factory = EdgeNGramFilterFactory.class, // Generate prefix tokens
                                            params = {
                                                    @Parameter(name = "minGramSize", value = "3"),
                                                    @Parameter(name = "maxGramSize", value = "3")
                                            }
                                    )
                            })

查询时间覆盖:

    @AnalyzerDef(name = "withoutEdgeAnalyzerFactory",

// Split input into tokens according to tokenizer
                tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
                         filters = {
                                    @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
                                    @TokenFilterDef(factory = LowerCaseFilterFactory.class),


                            }
                /*filters = {
                        // Normalize token text to lowercase, as the user is unlikely to
                        // care about casing when searching for matches
                        @TokenFilterDef(factory = PatternReplaceFilterFactory.class, params = {
                                @Parameter(name = "pattern", value = "([^a-zA-Z0-9\\.])"),
                                @Parameter(name = "replacement", value = " "),
                                @Parameter(name = "replace", value = "all") }),
                        @TokenFilterDef(factory = LowerCaseFilterFactory.class),
                        @TokenFilterDef(factory = StopFilterFactory.class) }*/)

希望这些细节有所帮助

答案

最简单的解决方案,只需更改一点代码,就是在布尔查询中使用Occur.MUST而不是Occur.SHOULD。然后,您只能获得与每个关键字匹配的文档,而不是当前与至少一个关键字匹配的文档。

但是,它并不是最正确的解决方案。试试看,如果你想了解发生了什么,请参阅下文。


首先,您不需要自己拆分输入字符串;这就是Lucene(和Elasticsearch)的工作,在所谓的“文本分析”中。在开始使用Hibernate Search之前,你真的应该理解文本分析。

简而言之,文本分析是将单个字符串转换为可以在全文索引中使用的“标记”(单词)的过程。

文本分析在两种情况下进行(我正在简化,但这或多或少会发生什么):

  • 索引文档时,会分析每个字段的内容,Elasticsearch会在索引中存储分析结果(标记列表)。
  • 查询时,将分析查询字符串,全文引擎将查找索引中的每个结果标记。

文本分析包括三个步骤:

  • 字符过滤,我不会详细描述,因为它通常被跳过,所以你可能不需要它。
  • 标记化,将单个字符串拆分为多个部分,称为“标记”。简而言之,它从字符串中提取单词。
  • 令牌过滤,它将变换应用于令牌,例如将它们变为小写,用更简单的等价物替换变音符号(“é”=>“e”,“à”=>“a”,...),将令牌分成更多ngrams(“word”=> [“w”,“wo”,“wor”,“word”])等等。

本文分析的目的是允许比“相等的字符串”更精细的匹配。特别是它允许在文档中查找单词,执行不区分大小写的搜索,还可以进行更精细的搜索,例如“以给定字符串开头的单词”(使用EdgeNgramTokenFilter)或看似无关的单词的匹配,例如“wifi”和“无线上网”。

搜索的行为方式取决于您在索引时和查询时应用的分析器。通常,在索引时和查询时应用相同的分析,但在某些非常具体的高级用例中(例如使用EdgeNGramTokenFilter时),在查询时需要使用稍微不同的分析器。

正如您所看到的,Lucene / Elasticsearch已经完成了您想要的操作,即将输入字符串拆分为多个“单词”。此外,如果您使用正确的分析器,您将不需要自己小写输入字符串,因为令牌过滤器将负责这一点。

这些都是基础知识,在开始使用Hibernate Search之前,您真正需要了解它们。

现在具体来说:问题是,当你只使用.keyword()查询时,字符串确实会被分成多个单词,但Hibernate Search会搜索与这些单词匹配的文档。这不是您想要的:您想要搜索与所有这些单词匹配的文档。

为了做到这一点,我建议你使用"Simple Query String" query。您将或多或少地像keyword()查询一样创建它,但它具有很好的附加功能,使其更适合于您正在构建的Web界面。特别是,它允许您通过将默认运算符设置为“和”来要求查询中的所有“单词”匹配。

例如:

Query finalQuery = queryBuilder.simpleQueryString()
         .onFields("basclt0100to.longname", "addrln1", "addrln2" 
             ,"city","state","zipcode", "country")
         .withAndAsDefaultOperator()
         .matching(searchTerms)
         .createQuery();

FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(finalQuery, Basclt0300TO.class);

以上是关于如何在hibernate搜索中使用多个查询字符串检索精确的搜索结果的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Hibernate Search查询DSL匹配多个术语?

如何使用带有通配符查询的 Hibernate Search 并输出结果对象列表

在 Hibernate 中运行多个查询的模型类..!

hibernate检索方式(HQL 检索方式,QBC 检索方式,本地 SQL 检索方式)

Hibernate:如何从具有多个类的查询中获取结果

hibernate查询不搜索超过2个字符的某些字符串,而不是在多字搜索上工作