昨天是感恩节,上幼儿园的女儿在老师的叮嘱下,晚上为我和老婆洗了脚(形式上的^_^),还给咱们每人端了一杯水。看着孩子一每天的长大,懂事,感受很开心,话说我们程序员这么辛苦是为了什么?不就是为了老婆,孩子,热炕头,有一个温暖幸福的家庭,再捎带着用代码改变一下世界吗?想到这里,顿时以为学习,创做博客的劲头也的更足了。哈哈,扯远了,书归正传,今天咱们来聊聊 Match Query。html
Match Query 是最经常使用的 Full Text Query 。不管须要查询什么字段, match
查询都应该会是首选的查询方式。它既能处理全文字段,又能处理精确字段。程序员
为了可以在后面能深刻理解 Match Query 中的各个属性的意义,咱们先构建一个 index 示例(有兴趣的同窗只要将下面字段粘贴到 sense 中就能够建立)。json
PUT matchtest { } PUT matchtest/_mapping/people { "properties": { "age": { "type": "integer" }, "hobbies": { "type": "text" }, "name": { "type": "keyword" } } } PUT matchtest/people/1 { "name" : "Jim", "age": 10, "hobbies": "football, basketball, pingpang" } PUT matchtest/people/2 { "name" : "Tom", "age": 12, "hobbies": "swimming, football" }
match
查询是一种 bool
类型的查询。什么意思呢?举个例子,查询 people type 的 hobbies 为 football basketball
app
GET matchtest/people/_search { "query": { "match": { "hobbies": "football basketball" } } }
会将上面的两个文档都搜索出来。为何?上面的查询其实隐藏了一个默认参数operator
, 它的默认值是 or
,也就是说上面的查询也能够写成这种形式elasticsearch
GET matchtest/people/_search { "query": { "match": { "hobbies": { "query": "football basketball", "operator": "or" } } } }
这样就比较容易理解了,既然是 or
操做符,就表示只要查询的文档的 hobbies
字段中含有 football
和 basketball
任意一个,就能够被匹配到。ide
若是将 operator
操做符的值改成 and
,则表示须要同时包含 football
和 basketball
, 获得的结果就只能是 文档 1 Jim 小朋友了。学习
analyzer
属性是指在对查询文本分析时的分析器优化
这里咱们也没有指定,就会使用默认的,就不举例了,在后面文章讲解 analyzer 时再拓展。ui
默认值是 false
, 表示用来在查询时若是数据类型不匹配且没法转换时会报错。若是设置成 true
会忽略错误。spa
例如, 例子中的 age
是 integer
类型的,若是查询 age=xxy
,就会致使没法转换而报错。
GET matchtest/_search { "query": { "match": { "age" : { "query": "xxx" } } } }
而若是将 lenient
参数设置为 true
,就会忽略这个错误
GET matchtest/_search { "query": { "match": { "age" : { "query": "xxx", "lenient": true } } } }
注意,若是将 age
字段的值设置为字符串 "10", 来查询,因为可以转换成整数,这时 elastic 内部会将 字符串先转换成整数再作查询,不会报错。
fuzziness
参数能够是查询的字段具备模糊搜索的特性。来先了解下什么是模糊搜索。
模糊搜索是指系统容许被搜索信息和搜索提问之间存在必定的差别,这种差别就是“模糊”在搜索中的含义。例如,查找名字Smith时,就会找出与之类似的Smithe, Smythe, Smyth, Smitt等。——百度百科
经过模糊搜索能够查询出存在必定类似度的单词,那么怎么计算两个单词是否有类似度以及类似度的大小呢?这就要了解下另一个概念:Levenshtein Edit Distance
Levenshtein Edit Distance 叫作莱文斯坦距离**,是 编辑距离的一种。指两个 字串之间,由一个转成另外一个所需的最少编辑操做次数。容许的编辑操做包括将一个字符替换成另外一个字符,插入一个 字符,删除一个字符。
例如,单词 "god" 只须要插入一个 'o' 字符就能够变为 "good",所以它们之间的编辑距离为 1。
了解了上面两个概念,回过头再来看下 fuzziness
参数。
在查询 text
或者 keyword
类型的字段时, fuzziness
能够看作是莱文斯坦距离。
fuzziness
参数的取值以下
0,1,2
表示最大可容许的莱文斯坦距离AUTO
会根据词项的长度来产生可编辑距离,它还有两个可选参数,形式为AUTO:[low],[high]
, 分别表示短距离参数和长距离参数;若是没有指定,默认值是 AUTO:3,6
表示的意义以下
0..2
单词长度为 0 到 2 之间时必需要精确匹配,这其实很好理解,单词长度过短是没有类似度可言的,例如 'a' 和 'b'。
3..5
单词长度 3 到 5 个字母时,最大编辑距离为 1
>5
单词长度大于 5 个字母时,最大编辑距离为 2
最佳实践: fuzziness
在绝大多数场合都应该设置成 AUTO
若是不设置 fuziness
参数,查询是精确匹配的。
来看例子,上面建立了一个 doc
PUT matchtest/people/1 { "name" : "Jim", "age": 10, "hobbies": "football, basketball, pingpang" }
设置 fuzziness
为 AUTO
,
hobbies
字段的值 football
长度 > 5, 此时咱们找一个编辑距离为 2 的单词 footba22
来查询,应该匹配到name
字段的值 jim
长度在 3 和 5 之间,此时找一个编辑距离为 1 的单词 jiO
应匹配到,而编辑距离为 2 的 jOO
就不该匹配到。来,验证下
GET matchtest/_search { "query": { "match": { "hobbies": { "query": "footba22", "fuzziness": "AUTO" } } } } GET matchtest/_search { "query": { "match": { "name": { "query": "jiO", "fuzziness": "AUTO" } } } } GET matchtest/_search { "query": { "match": { "name": { "query": "jOO", "fuzziness": "AUTO" } } } }
prefix_length
表示不能没模糊化的初始字符数。因为大部分的拼写错误发生在词的结尾,而不是词的开始,使用 prefix_length
就能够完成优化。注意 prefix_length
必须结合 fuzziness
参数使用。
例如,在查询 hobbies
中的 football
时,将 prefix_length
参数设置为 3,这时 foatball
将不能被匹配。
GET matchtest/_search { "query": { "match": { "hobbies": { "query": "foatball", "fuzziness": "AUTO", "prefix_length": 3 } } } }
TODO(max_expansions 参数对于 match 查询而言,没理解表示的意义,若是您知道这个参数的用法,请给我留言告知,不胜感谢! )
先看例子, 先建立一个文档 zero_terms_query_test
其中 message
字段使用 stop
分析器,这个分析器会将 stop words 停用词在索引时全都去掉。
PUT matchtest1 PUT matchtest1/_mapping/zero_terms_query_test { "properties": { "message": { "type": "text", "analyzer": "stop" } } } PUT matchtest1/zero_terms_query_test/1 { "message": "to be or not to be" } GET matchtest1/_search { "query": { "match": { "message": { "query": "to be or not to be", "operator": "and", "zero_terms_query": "none" } } } }
那么就像 message 字段中的 to be or not to be
这个短语中所有都是中止词,一过滤,就什么也没有了,得不到任何 tokens, 那搜索时岂不什么都搜不到。
POST _analyze { "analyzer": "stop", "text": "to be or not to be" }
zero_terms_query
就是为了解决这个问题而生的。它的默认值是 none
,就是搜不到中止词(对 stop 分析器字段而言),若是设置成 all
,它的效果就和 match_all
相似,就能够搜到了。
GET matchtest1/_search { "query": { "match": { "message": { "query": "to be or not to be", "operator": "and", "zero_terms_query": "all" } } } }
查询字符串时的词项会分红低频词(更重要)和高频词(次重要)两类,像前面所说的停用词 (stop word)就属于高频词,它虽然出现频率较高,但在匹配时可能并不太相关。实际上,咱们每每是想要文档能尽量的匹配那些低频词,也就是更重要的词项。
要实现这个需求,只要在查询时配置 cutoff_frequency
参数就能够了。假设咱们将 cutoff_frequency
设置成 0.01
就表示
从而将高频词(次重要的词)挪到可选子查询中,让它们只参与评分,而不参与匹配;高频词(更重要的词)参与匹配和评分。
这样一来,就再也不须要 stopwords 停用词文件了,从而变成了动态生成停用词: 高频词就会被看作是停用词。这种配置只是对于词项比较多的场合如 email body,文章等适用,文字太少, cutoff_frequency
选项设置的意义就不大了。
cutoff_frequency
配置有两种形式
0.01
)表示出现频率5
)则表示出现次数下面给个例子, 在建立的 3 个文档中都包含 "be " 的单词,在查询时将 cutoff_frequency
参数设置为 2, 表示 "be" 就是高频词,只会参与评分,但在匹配时不作考虑。
此时查询的内容为 "to be key" ,因为 "be" 词项是高频词,由于在文档中必需要存在 "to" 或者 "key" 才能匹配,所以文档 3 不能匹配。
PUT /matchtest2 PUT matchtest2/_mapping/cutoff_frequency_test { "properties": { "message": { "type": "text" } } } PUT matchtest2/cutoff_frequency_test/1 { "message": "to be or not to be to be or" } PUT matchtest2/cutoff_frequency_test/2 { "message": "be key or abc" } PUT matchtest2/cutoff_frequency_test/3 { "message": "or to be or to to be or" } GET matchtest2/_search { "query": { "match": { "message": { "query": "to be key", "cutoff_frequency": 2 } } } }
synonyms 是指同义词,只要索引和字段中配置了同义词过滤器,match 查询是支持多词条的同义词扩展的。在应用过滤器后,解析器会对每一个屡次条同义词建立一个语句查询。
例如,同义词 USA, united states of America
就会构建出 (USA OR ("united states of America"))
。看下面例子:
PUT /matchtest4 { "settings": { "index" : { "analysis" : { "analyzer" : { "synonym" : { "tokenizer" : "whitespace", "filter" : ["synonym"] } }, "filter" : { "synonym" : { "type" : "synonym", "synonyms" : [ "USA, united states of America" ] } } } } } } PUT /matchtest4/_mapping/synonyms_test { "properties": { "message": { "type": "text", "analyzer": "synonym" } } } PUT /matchtest4/synonyms_test/1 { "message": "united states of America people" } GET /matchtest4/_search { "query": { "match": { "message": { "query": "USA" } } } }
本文以代码实例的方式完整的讲解了 Match Query 的各类使用场景和参数意义。下篇会讲解 Match Phrase Query 敬请期待。