我们正在使用休眠搜索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()
(应该/必须)查询,但是由于我们不确定用户将首先输入哪些数据,因此可以在文本搜索中的任意位置输入邮政编码,州或城市。
请分享您在分析器/策略方面的知识/逻辑,我们可以使用这些知识/逻辑通过休眠搜索/ 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,城市,州,邮政编码,国家/地区。
索引定义在下面
> {
> "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号,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) }*/)
希望这些详细信息有帮助。
答案 0 :(得分:0)
最简单的解决方案,仅需更改一点代码,便是在布尔查询中使用Occur.MUST
而不是Occur.SHOULD
。这样一来,您将只获得与每个关键词 匹配的文档,而不是当前与至少一个 关键词匹配的文档。
但是,这并不是最正确的解决方案。尝试一下,然后查看下面是否要了解正在发生的事情。
首先,您不需要自己分割输入字符串;这就是Lucene(和Elasticsearch)的工作,即所谓的“文本分析”。在开始使用Hibernate Search之前,您确实应该了解文本分析。
简而言之,文本分析是将单个字符串转换为可在全文索引中使用的“令牌”(单词)的过程。
文本分析在两种情况下进行(我正在简化,但这或多或少会发生):
文本分析包括三个步骤:
此文本分析的目的是允许比“等于字符串”更多的细微匹配。特别是,它允许在文档中查找单词,以执行不区分大小写的搜索,还可以进行更细微的搜索,例如“以给定字符串开头的单词”(使用EdgeNgramTokenFilter
)或看似无关的单词匹配,例如作为“ wifi”和“ wi fi”。
搜索的行为方式全部取决于您在建立索引时和在查询时所应用的分析器。通常,在索引编制时间和查询时间都应用相同的分析,但是在某些非常特定的高级用例中(例如,在使用EdgeNGramTokenFilter
时),查询时将需要使用略有不同的分析器。
如您所见,Lucene / Elasticsearch已经完成了您想要的工作,即将输入字符串拆分为多个“单词”。另外,如果您使用正确的分析器,则无需自己小写输入字符串,因为令牌过滤器会解决这一问题。
仅此而已,这是您开始使用Hibernate Search之前真正需要了解的基础知识。
现在要提供具体信息:问题是,当您仅使用.keyword()
查询时,该字符串确实会拆分为多个单词,但是Hibernate Search会搜索与 any 这些词。这不是您想要的:您想搜索与所有所有单词匹配的文档。
为此,我建议您使用"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);