使用Hibernate Search(5.8.2.Final)查询DSL到Elasticsearch服务器 .
给定一个小写,标准停用词的字段分析器,然后自定义同义词:
company => co
最后,一个自定义的停止词:
co
我们已经将供应商名称编入索引: Great Spaulding Company
,在同义词和停用词之后归结为Elasticsearch中的2个术语: great
和 spaulding
.
我正在尝试构建我的查询,以便每个术语“必须”匹配,模糊或精确,具体取决于术语长度 .
我得到了我想要的结果,除非其中一个术语恰好是同义词或停用词并且足够长以至于我的代码会增加模糊性,例如 company~1
,在这种情况下,它不再被视为同义词或停止 - 单词和我的查询返回不匹配,因为'company'从未存储在第一位b / c它变为'co'然后作为停用词删除 .
一些代码的时间 . 它可能看起来有点hacky,但我已经尝试了很多方法并使用 simpleQueryString
和 withAndAsDefaultOperator
并且构建我自己的短语似乎让我最接近我需要的结果(但我做了类似的事情:'m open to suggestions). I'
// 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"
但是由于我的代码没有公司' is greater than 5 characters, I' ll使它模糊,它会构建这些不返回匹配的短语: "Great Spaulding~1 Company~1"
或 "Great Company~1"
-
为什么Elasticsearch不处理
Company~1
作为同义词? -
有关如何使用simpleQueryString或其他DSL查询进行此操作的任何想法?
-
每个人如何处理可能包含停用词的文本的模糊搜索?
[编辑]同样的问题发生在我的分析仪通常会删除的标点符号上 . 我不能在我的查询中的模糊搜索字符串中包含任何标点符号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
.
1 回答
因为模糊查询是"term-level" queries,这意味着它们按照精确的术语而不是分析的文本进行操作 . 如果您的术语在分析后解析为多个令牌,我认为为模糊查询定义可接受的行为并不容易 .
有一个更详细的解释there(我相信它仍然适用于Elasticsearch 5.6中使用的Lucene版本) .
您可以尝试撤消同义词:使用
co => company
而不是company => co
,这样即使"compayn"未分析,查询如compayn~1
也会匹配 . 但那's not a satisfying solution, of course, since other example requiring analysis still won'工作,如Company~1
.以下是替代解决方案 .
解决方案1:“匹配”查询与模糊
This article描述了一种执行模糊搜索的方法,特别是解释了几种类型的模糊查询之间的区别 .
不幸的是,似乎“简单查询字符串”查询中的模糊查询被转换为不执行分析的查询类型 .
但是,根据您的要求,"match" query可能就足够了 . 要访问Elasticsearch提供的所有设置,您必须回退到本机查询构建:
有关"AUTO"在上例中的含义的详细信息,请参阅this page .
请注意,在Hibernate Search 6发布之前,您不能将上面显示的本机查询与Hibernate Search DSL混合使用 . 您可以使用DSL或本机查询,但不能同时使用同一查询 .
解决方案2:ngrams
在我看来,当查询来自您的用户,而这些用户不是Lucene专家时,最好的选择是避免完全解析查询 . 查询解析涉及(至少部分)文本分析,文本分析最好留给Lucene / Elasticsearch .
那么你所能做的就是配置分析仪 .
使用这些工具添加"fuzziness"的一种方法是使用NGram filter . 使用
min_gram = 3
和max_gram = 3
,例如:索引字符串(例如"company")将被编入索引
["com", "omp", "mpa", "pan", "any"]
一旦分析了"compayn"这样的查询,就会被翻译成(基本上是
com OR omp OR mpa OR pay OR ayn
这样的查询可能会匹配很多文档,但是当按分数排序时,"Great Spaulding Company"的文档会出现在顶部,因为它几乎匹配所有ngrams .
我使用参数值
min_gram = 3
和max_gram = 3
作为示例,但在现实世界中,min_gram = 3
和max_gram = 5
这样的应用程序可以更好地工作,因为添加的更长的ngrams会为搜索与索引条件的较长部分匹配的术语提供更好的分数 .当然,如果你不能按分数排序,如果你不能在结果中接受过多的尾随部分匹配,那么这个解决方案对你不起作用 .