ElasticSearch的match|ElasticSearch的match fuzzy查询参数详解
fuzzy在es中可以理解为模糊查询,搜索本身很多时候是不精确的,很多时候我们需要在用户的查询词中有部分错误的情况下也能召回正确的结果,但是计算机无法理解自然语言,因此我们只能通过一些算法替代语言理解能力实现类似的事情,前缀查询的实现比较简单但效果很难令人满意,就模糊查询而言es的fuzzy实现了一种复杂度和效果比较折中的查询能力。
字符的相似度-编辑距离
编辑距离是对两个字符串差异长度的量化,及一个字符至少需要处理多少次才能变成另一个字符,比如lucene和lucece只差了一个字符他们的编辑距离是1。
- 莱文斯坦距离(Levenshtein distance)
允许的编辑包括:
- 将一个字符替换成另一个字符
- 插入一个字符
- 删除一个字符
- Damerau–Levenshtein distance
ElasticSearch支持经典的Levenshtein距离和Damerau-Levenshtein距离,在es中对模糊查询的支持有两种方式match query和fuzzy query。
match query
使用方式如下所示:
GET index_name/_search
{
"query": {
"match": {
"name": {
"query": "elastic search",
"fuzziness": 0,
"prefix_length": 0,
"max_expansions": 50,
"transpositions": true
}
}
}
}
下面对他支持的参数进行一些介绍:
- fuzziness
支持的格式
- 可以是数字(
0、1、2
)代表固定的最大编辑距离
- 自动模式,
AUTO:[low],[high]
的格式,含义为:
- 查询词长度在[0-low)范围内编辑距离为0(即强匹配)
- [low, high)范围内允许编辑一次
- >high允许编辑2次
AUTO
代表默认的自动模式,相当于AUTO:3,6
- prefix_length
- max_expansions
- Lucene的fuzzy 查询是通过query改写实现的,查询时会将倒排索引词典中的term集合和查询term做编辑距离计算,获取topN个距离最小的term作为query改写的词,这N个词会组成一组或查询,即改写为boolean query (改写词作为term query放在should clause)
-
max_expansions
影响的是这里的或查询的元素数目,实际上这个数目会被两个参数影响,另外一个是indices.query.bool.max_clause_count
,最终取indices.query.bool.max_clause_count
和max_expansions
的最小值 - 倒排中的term总数会影响fuzzy查询的性能,term越多性能越差,文档总数不是影响fuzzy查询性能的关键因素。
- 值得注意的是分词后的term本身也算一次
max_expansions
计数,也就是说max_expansions=1
时相当于不扩展(分词term级精确查询) -
max_expansions
是作用在分片级参数,如果分片数比较多的话,查询结果看起来和max_expansions
设置数目不一致也是正常的
- transpositions
transpositions=false
将使用经典莱文斯坦距离算法。- minimum_should_match 的作用机制
-
minimum_should_match
作用在分词后的term级,即分词后的term无论是通过精确查找或者模糊查找命中算且算一次计数,对于同一个term扩展出来的term1、term2不做重复计数。 - 当operator 为 and 时,
minimum_should_match >0
时会导致查不到结果,这是因为minimum_should_match
的计算方法是should clause命中的个数,operator为and时无should clause命中数永远不会 >0,所以无结果。参见BooleanWeight.java#L390 - minimum_should_match为百分比时ES的计算方法
float calc = (shouldClauseCount * percent) * (1 / 100f);
// shouldClauseCount为分词后的词根个数,percent为传参的百分比
result = calc < 0 ? shouldClauseCount + (int) calc : (int) calc;
//
Fuzzy query
fuzzy query用法和match基本一致,参数也包含
fuzziness
、prefix_length
、max_expansions
、transpositions
,唯一的不同点是Fuzzy query的查询不分词。使用方式如下:GET /test-mapping/_search
{
"query": {
"fuzzy": {
"name": {
"value": "elastic",
"fuzziness": 0,
"prefix_length": 0,
"max_expansions": 50,
"transpositions": true
}
}
}
}
排序 默认为相关性算分倒序,fuzzy查询过程会改写模糊词查询term权重,编辑距离越大权重越小
query改写过程权重调整细节为:
- 强匹配权重为1.0
- 非强匹配权重算法为
1.0 - (编辑距离 / min(倒排term长度, 查询term长度))
相关性算法默认为bm25,内建几种可供选择的算法及自定义,参见similarity和Similarity module
【ElasticSearch的match|ElasticSearch的match fuzzy查询参数详解】一个调试小技巧
可以通过将字段的mapping设置为similarity= boolean来查看模糊查询的扩展词的权重,这样查询结果中的打分就是命中该词扩展词的权重
PUT my_index
{
"mappings": {
"properties": {
"fuzzy_field": {
"type": "text",
"similarity": "boolean"
}
}
}
}
参考文档
- 编辑距离算法实现 http://blog.notdot.net/2010/07/Damn-Cool-Algorithms-Levenshtein-Automata
- lucene fuzzy查询优化 http://blog.mikemccandless.com/2011/03/lucenes-fuzzyquery-is-100-times-faster.html
- ES模糊搜索介绍 fuzzy search
- https://elastic-search-in-action.medcl.com/3.site_search/3.3.search_box/fuzzy_query/
- https://livebook.manning.com/book/taming-text/chapter-4/3
- 用Lucene实现fuzzy search
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量