[英]How to handle synonyms and stop words when building a fuzzy query with Hibernate Search Query DSL
使用Hibernate Search(5.8.2.Final)将DSL查询到Elasticsearch服务器。
给定一个执行小写,标准停用词的字段分析器,然后使用以下自定义同义词:
company => co
最后是一个自定义停用词:
co
并且我们已经索引了一个供应商名称: Great Spaulding Company
,该名称在同义词和停用词之后的Elasticsearch中简化为2个词: great
和spaulding
。
我正在尝试建立查询,以便每个词“必须”匹配,模糊或精确,具体取决于词长。
我得到除非条款1恰好是一个同义词或停用词和足够长的时间,我的代码将模糊它,就像我想要的结果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()
);
就像我在上面说的,我发现只要不对可能的同义词或停用词添加任何模糊性,查询就会找到匹配项。 因此,这些短语返回一个匹配项: "Great Spaulding~1"
或"Great Spaulding~1 Co"
或"Spaulding Co"
但是由于我的代码不知道什么术语是同义词或停用词,所以它盲目地查看术语长度并说,哦,“公司”大于5个字符,我会使其模糊,它会构建此类短语不返回匹配的结果: "Great Spaulding~1 Company~1"
或"Great Company~1"
Company~1
的代名词? [编辑]标点符号也会发生同样的问题,我的分析仪通常会删除标点符号。 我无法在查询b / c的模糊搜索字符串中包含任何标点符号,ES分析器似乎并未将其视为模糊不清,并且没有得到匹配结果。
基于上述搜索字符串的示例: Great Spaulding Company.,
在我的代码中内置了词组Great Spaulding~1 Company.,~1
,ES并未删除标点符号或识别同义词Company
我将尝试调用ES _analyze REST api的技巧,以告诉我应该在查询中包括哪些令牌,尽管这会增加我构建的每个查询的开销。 类似于http://localhost:9200/myEntity/_analyze?analyzer=vendorNameAnalyzer&text=Great Spaulding Company.,
产生3个令牌: great
, spaulding
和company
。
为什么Elasticsearch不将Company〜1作为同义词处理?
我猜这是因为模糊查询是“术语级”查询 ,这意味着它们以确切的术语而不是分析的文本进行操作。 如果您的术语经过分析后可以解析为多个标记,则我认为为模糊查询定义可接受的行为并不容易。
有一个更详细的解释有 (我相信它仍然适用于Elasticsearch 5.6使用Lucene的版本)。
关于如何使它与simpleQueryString或另一个DSL查询一起工作的任何想法吗? 每个人如何处理可能包含停用词的文本的模糊搜索?
您可以尝试颠倒您的同义词:使用co => company
代替company => co
,这样即使未分析“ compayn”,诸如compayn~1
的查询也将匹配。 但这当然不是令人满意的解决方案,因为其他需要分析的示例仍然无法正常工作,例如Company~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();
有关上述示例中“ AUTO”的含义的详细信息,请参见此页面 。
请注意,在发布Hibernate Search 6之前,您无法将如上所示的本机查询与Hibernate Search DSL混合使用。 您可以使用DSL或本机查询,但不能在同一查询中同时使用两者。
我认为,当查询来自您的用户并且这些用户不是Lucene专家时,您最好的选择是避免完全解析查询。 查询分析涉及(至少部分地)涉及文本分析,而文本分析最好留给Lucene / Elasticsearch。
然后,您只需配置分析仪即可。
使用这些工具添加“模糊性”的一种方法是使用NGram过滤器 。 例如,对于min_gram = 3
和max_gram = 3
:
["com", "omp", "mpa", "pan", "any"]
com OR omp OR mpa OR pay OR ayn
在示例中,我使用了参数值min_gram = 3
和max_gram = 3
,但在实际应用中, min_gram = 3
和max_gram = 5
会更好,因为添加的更长的ngrams会为匹配的词提供更好的分数索引词的较长部分。
当然,如果您不能按分数排序,或者如果您不能在结果中接受太多的尾随部分匹配,那么此解决方案将不适合您。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.