elasticsearch 如何在hibernate搜索中使用多个查询字符串检索精确的搜索结果
我们正在使用hibernate search orm 5.9.2,希望获得准确的搜索结果,如: 如果用户从
elasticsearch 如何在hibernate搜索中使用多个查询字符串检索精确的搜索结果,
elasticsearch,lucene,hibernate-search,
elasticsearch,Lucene,Hibernate Search,我们正在使用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 C
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、城市、zipcode、州等字段中有包含此数据的实体
我们尝试了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"
> }
> }
> }
下面是输入
阿卡什阿格拉瓦尔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;
查询时间覆盖为:
@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) }*/)
希望这些详细信息能有所帮助。最简单的解决方案是在布尔查询中使用
occure.MUST
而不是occure.SHOULD
。然后,您将只获得匹配每个关键字的文档,而不是当前至少匹配一个关键字的文档
然而,这并不是最正确的解决方案。尝试一下,然后看看下面,如果你想了解发生了什么
首先,您不需要自己拆分输入字符串;这就是Lucene(和Elasticsearch)的工作,在所谓的“文本分析”中。在开始使用Hibernate搜索之前,您确实应该了解文本分析 简而言之,文本分析是将单个字符串转换为可在全文索引中使用的“标记”(单词)的过程 文本分析在两种情况下执行(我正在简化,但这或多或少是会发生的):
- 为文档编制索引时,将分析每个字段的内容,Elasticsearch将分析结果(标记列表)存储在索引中
- 查询时,将分析查询字符串,全文引擎将在索引中查找每个生成的标记
- 字符过滤,我不会详细描述它,因为它通常被跳过,所以您可能不需要它
- 标记化,将单个字符串拆分为多个部分,称为“标记”。简而言之,它从字符串中提取单词
- 代币过滤,对代币应用转换,例如将其转换为小写,用更简单的等价物(“é”=>“e”,“á”=>“a”,…)替换变音符号,将代币进一步拆分为ngram(“word”=>[“w”,“wo”,“wor”,“word”]),等等
EdgeNgramTokenFilter
)或看似不相关的单词的匹配,如“wifi”和“wi-fi”
搜索的行为方式完全取决于您在索引时和查询时应用的分析器。通常在索引时和查询时应用相同的分析,但在某些非常特定的高级用例中(例如使用EdgeNGramTokenFilter
时),在查询时需要使用稍微不同的分析器
如您所见,Lucene/Elasticsearch已经完成了您想要的工作,即将输入字符串拆分为多个“单词”。此外,如果您使用正确的分析器,您不需要自己将输入字符串小写,因为令牌过滤器会处理这个问题
这些都是基础知识,在开始使用Hibernate搜索之前,您确实需要了解这些知识
现在来了解具体情况:问题是,当您仅使用.keyword()
查询时,字符串确实会被拆分为多个单词,但Hibernate Search将搜索与这些单词中任何一个匹配的文档。这不是您想要的:您想要搜索匹配所有这些单词的文档
为了做到这一点,我建议您使用。您将或多或少地创建一个类似于关键字()
查询的查询,但它有一些很好的附加功能,使它更适合于您正在构建的web界面。特别是,它允许您通过将默认运算符设置为“and”来要求查询中的所有“单词”匹配
例如:
Query finalQuery = queryBuilder.simpleQueryString()
.onFields("basclt0100to.longname", "addrln1", "addrln2"
,"city","state","zipcode", "country")
.withAndAsDefaultOperator()
.matching(searchTerms)
.createQuery();
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(finalQuery, Basclt0300TO.class);
@Yoann Rodière,请分享你的意见。要理解你的问题到底是什么有点困难。请至少发布您的实体模型和当前查询代码,并解释当前行为的错误,最好使用示例(一组文档、用户输入、实际结果和预期结果)。这将是一个开始。@Yoann Rodière,我已经在我的问题中提供了细节。我会再次尝试:请发布您当前的查询代码,并解释当前行为的错误。最好是举例说明(一套文档、用户输入、实际结果、预期结果)@
Query finalQuery = queryBuilder.simpleQueryString()
.onFields("basclt0100to.longname", "addrln1", "addrln2"
,"city","state","zipcode", "country")
.withAndAsDefaultOperator()
.matching(searchTerms)
.createQuery();
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(finalQuery, Basclt0300TO.class);