Java 使用Hibernate Search query DSL构建模糊查询时如何处理同义词和停止词
使用Hibernate搜索(5.8.2.Final)将DSL查询到Elasticsearch服务器 给定一个执行小写标准停止词的字段分析器,然后自定义同义词:Java 使用Hibernate Search query DSL构建模糊查询时如何处理同义词和停止词,java,
elasticsearch,lucene,hibernate-search,Java,
elasticsearch,Lucene,Hibernate Search,使用Hibernate搜索(5.8.2.Final)将DSL查询到Elasticsearch服务器 给定一个执行小写标准停止词的字段分析器,然后自定义同义词: company => co 最后,还有一个自定义的停止词: co 我们还为供应商名称编制了索引:Great spulding Company,在同义词和停止词之后的弹性搜索中,它归结为两个术语:Great和spulding 我试图构建我的查询,使每个术语“必须”匹配,模糊或精确,取决于术语长度 我得到了我想要的结果,除非其中一个
company => co
最后,还有一个自定义的停止词:
co
我们还为供应商名称编制了索引:Great spulding Company
,在同义词和停止词之后的弹性搜索中,它归结为两个术语:Great
和spulding
我试图构建我的查询,使每个术语“必须”匹配,模糊或精确,取决于术语长度
我得到了我想要的结果,除非其中一个术语恰好是同义词或停止词,并且足够长,我的代码会给它增加模糊性,如company~1
,在这种情况下,它不再被视为同义词或停止词,并且我的查询返回不匹配,由于“company”从未存储在b/c的第一个位置,因此它变为“co”,然后作为停止词删除
是时候写点代码了。这似乎有点老套,但我已经尝试了多种方法,使用simpleQueryString
和withAndAsDefaultOperator
并构建我自己的短语似乎能让我最接近我需要的结果(但我愿意接受建议)。我在做一些事情,比如:
// assume passed in search String of "Great Spaulding Company"
String vendorName = "Great Spaulding Company";
List<String> vendorNameTerms = Arrays.asList(vendorName.split(" "));
List<String> qualifiedTerms = Lists.newArrayList();
vendorNameTerms.forEach(term -> {
int editDistance = getEditDistance(term); // 1..5 = 0, 6..10 = 1, > 10 = 2
int prefixLength = getPrefixLength(term); //appears of no use with simpleQueryString
String fuzzyMarker = editDistance > 0 ? "~" + editDistance : "";
qualifiedTerms.add(String.format("%s%s", term, fuzzyMarker));
});
// join my terms back together with their optional fuzziness marker
String phrase = qualifiedTerms.stream().collect(Collectors.joining(" "));
bool.should(
qb.simpleQueryString()
.onField("vendorNames.vendorName")
.withAndAsDefaultOperator()
.matching(phrase)
.createQuery()
);
//假设传入了“大斯波尔公司”的搜索字符串
String vendorName=“伟大的Spaulding公司”;
List vendorNameTerms=Arrays.asList(vendorName.split(“”);
List qualifiedTerms=Lists.newArrayList();
vendorNameTerms.forEach(期限->{
int editDistance=getEditDistance(term);/1..5=0,6..10=1,>10=2
int prefixLength=getPrefixLength(term);//似乎对simpleQueryString没有用处
字符串fuzzyMarker=editDistance>0?~“+editDistance:”;
qualifiedTerms.add(String.format(“%s%s”,term,fuzzyMarker));
});
//将我的术语与它们可选的模糊标记结合起来
字符串短语=qualifiedTerms.stream().collect(collector.joining(“”);
应该(
qb.simpleQueryString()
.onField(“vendorNames.vendorName”)
.WithAndAndDefaultOperator()
.匹配(短语)
.createQuery()
);
正如我上面所说的,我发现只要我不对可能的同义词或停止词添加任何模糊性,查询就会找到匹配项。因此,这些短语返回一个匹配项:
“大斯普林1号”
或“大斯普林1号公司”
或“斯普林1号公司”
但由于我的代码不知道什么术语是同义词或停止词,它会盲目地查看术语长度并说,“哦,'公司'大于5个字符,我会将其模糊化,它构建了这些不返回匹配项的短语:
“大公司1号”
或“大公司1号”
公司~1
作为同义词处理Great spauding Company.,
在我的代码中生成短语Great spauding~1 Company.,~1
,ES不会删除标点符号或识别同义词Company
我将尝试调用ES_analyze restapi,让它告诉我应该在查询中包含哪些标记,尽管这会增加我构建的每个查询的开销。类似于http://localhost:9200/myEntity/_analyze?analyzer=vendorNameAnalyzer&text=Great Spaulding Company.,
生产3种代币:great
,Spaulding
和Company
为什么Elasticsearch不是将Company~1作为同义词处理
我猜这是因为模糊查询是,这意味着它们根据精确的术语而不是分析的文本进行操作。如果一经分析,您的术语解析为多个标记,我认为为模糊查询定义可接受的行为并不容易
还有更详细的解释(我相信它仍然适用于ElasticSearch5.6中使用的Lucene版本)
您知道如何使用simpleQueryString或其他DSL查询来实现这一点吗?
每个人如何处理可能包含停止词的文本的模糊搜索
您可以尝试反转同义词:使用co=>company
而不是company=>co
,这样即使未分析“compayn”,诸如compayn~1
之类的查询也会匹配。但这当然不是一个令人满意的解决方案,因为其他需要分析的示例仍然不起作用,例如公司~1
以下是替代方案
解决方案1:具有模糊性的“匹配”查询
描述执行模糊搜索的方法,并特别说明几种类型的模糊查询之间的差异
不幸的是,“简单查询字符串”查询中的模糊查询似乎被转换为不执行分析的查询类型
但是,根据您的要求,这可能就足够了。为了访问Elasticsearch提供的所有设置,您必须返回到本机查询构建:
QueryDescriptor query = ElasticsearchQueries.fromJson(
"{ 'query': {"
+ "'match' : {"
+ "'vendorNames.vendorName': {"
// Not that using a proper JSON framework would be better here, to avoid problems with quotes in the terms
+ "'query': '" + userProvidedTerms + "',"
+ "'operator': 'and',"
+ "'fuzziness': 'AUTO'"
+ "}"
+ "}"
+ " } }"
);
List<?> result = session.createFullTextQuery( query ).list();
QueryDescriptor query=elasticsearchquerys.fromJson(
“{‘查询’:{”
+“'match':{”
+“'vendorNames.vendorName':{”
//并不是说在这里使用合适的JSON框架会更好,以避免术语中引用的问题
+“'query':'”+userProvidedTerms+“,”
+“‘运算符’:‘和’,”
+“‘模糊’:‘自动’”