match查询
PUT zhifou/doc/1 { "name":"顾老二", "age":30, "from": "gu", "desc": "皮肤黑、武器长、性格直", "tags": ["黑", "长", "直"] } PUT zhifou/doc/2 { "name":"大娘子", "age":18, "from":"sheng", "desc":"肤白貌美,娇憨可爱", "tags":["白", "富","美"] } PUT zhifou/doc/3 { "name":"龙套偏房", "age":22, "from":"gu", "desc":"mmp,没怎么看,不知道怎么形容", "tags":["造数据", "真","难"] } PUT zhifou/doc/4 { "name":"石头", "age":29, "from":"gu", "desc":"粗中有细,狐假虎威", "tags":["粗", "大","猛"] } PUT zhifou/doc/5 { "name":"魏行首", "age":25, "from":"广云台", "desc":"仿佛兮若轻云之蔽月,飘飘兮若流风之回雪,mmp,最后竟然没有嫁给顾老二!", "tags":["闭月","羞花"] }数据
一 match系列之match(按条件查询)
GET zhifou/doc/_search { "query": { "match": { "from": "gu" } } }
二 match系列之match_all(查询全部)
GET zhifou/doc/_search { "query": { "match_all": {} } }
三 match系列之match_phrase(短语查询)
准备点数据
PUT t1/doc/1 { "title": "中国是世界上人口最多的国家" } PUT t1/doc/2 { "title": "美国是世界上军事实力最强大的国家" } PUT t1/doc/3 { "title": "北京是中国的首都" }数据
第一步 当我们以中国
作为搜索条件,我们希望只返回和中国
相关的文档。我们首先来使用match
查询:
GET t1/doc/_search { "query": { "match": { "title": "中国" } } }
但是结果看看
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 3, "max_score" : 0.68324494, "hits" : [ { "_index" : "t1", "_type" : "doc", "_id" : "1", "_score" : 0.68324494, "_source" : { "title" : "中国是世界上人口最多的国家" } }, { "_index" : "t1", "_type" : "doc", "_id" : "3", "_score" : 0.5753642, "_source" : { "title" : "北京是中国的首都" } }, { "_index" : "t1", "_type" : "doc", "_id" : "2", "_score" : 0.39556286, "_source" : { "title" : "美国是世界上军事实力最强大的国家" } } ] } }
看上面的结果 居然连美国的那条信息也给返回了
是怎么回事呢?因为这是elasticsearch在内部对文档做分词的时候,对于中文来说,就是一个字一个字分的,所以,我们搜中国
,中
和国
都符合条件,返回,而美国的国
也符合。
而我们认为中国
是个短语,是一个有具体含义的词。所以elasticsearch在处理中文分词方面比较弱势。后面会讲针对中文的插件。
但目前我们还有办法解决,那就是使用短语查询:
GET t1/doc/_search { "query": { "match_phrase": { "title": { "query": "中国" } } } }
# 这里match_phrase
是在文档中搜索指定的词组,而中国
则正是一个词组,所以愉快的返回了。
slop的用法
我们搜索中国
和世界
这两个指定词组时,但又不清楚两个词组之间有多少别的词间隔。那么在搜的时候就要留有一些余地。这时就要用到了slop
了。相当于正则中的中国.*?世界
。这个间隔默认为0,导致我们刚才没有搜到,现在我们指定一个间隔。
GET t1/doc/_search { "query": { "match_phrase": { "title": { "query": "中国世界", "slop": 2 } } } }
结果
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : 0.7445889, "hits" : [ { "_index" : "t1", "_type" : "doc", "_id" : "1", "_score" : 0.7445889, "_source" : { "title" : "中国是世界上人口最多的国家" } } ] } }View Code
四 match系列之match_phrase_prefix(最左前缀查询)
我们在输入一些英语单词的时候,只知道前面几个字母,后面的我们不记得了 例如:beautiful 我们只记得bea后面的不记得
PUT t3/doc/1 { "title": "maggie", "desc": "beautiful girl you are beautiful so" } PUT t3/doc/2 { "title": "sun and beach", "desc": "I like basking on the beach" }数据
因为我们输入的单词不完整,我们就得使用 match_phrase_prefix
GET t3/doc/_search { "query": { "match_phrase_prefix": { "desc": "bea" } } }
前缀查询是短语查询类似,但前缀查询可以更进一步的搜索词组,只不过它是和词组中最后一个词条进行前缀匹配(如搜这样的you are bea
)。应用也非常的广泛,比如搜索框的提示信息,当使用这种行为进行搜索时,最好通过max_expansions
来设置最大的前缀扩展数量,因为产生的结果会是一个很大的集合,不加限制的话,影响查询性能。
GET t3/doc/_search { "query": { "match_phrase_prefix": { "desc": { "query": "bea", "max_expansions": 1 } } } }
但是,如果此时你去尝试加上max_expansions
测试后,你会发现并没有如你想想的一样,仅返回一条数据,而是返回了多条数据。max_expansions
执行的是搜索的编辑(Levenshtein)距离。那什么是编辑距离呢?编辑距离是一种计算两个字符串间的差异程度的字符串度量(string metric)。我们可以认为编辑距离就是从一个字符串修改到另一个字符串时,其中编辑单个字符(比如修改、插入、删除)所需要的最少次数。俄罗斯科学家Vladimir Levenshtein于1965年提出了这一概念。
我们再引用elasticsearch官网的一段话:该max_expansions设置定义了在停止搜索之前模糊查询将匹配的最大术语数,也可以对模糊查询的性能产生显着影响。但是,减少查询字词会产生负面影响,因为查询提前终止可能无法找到某些有效结果。重要的是要理解max_expansions查询限制在分片级别工作,这意味着即使设置为1,多个术语可能匹配,所有术语都来自不同的分片。此行为可能使其看起来好像max_expansions没有生效,因此请注意,计算返回的唯一术语不是确定是否有效的有效方法max_expansions。。
我想你也没看懂这句话是啥意思,但我们只需知道该参数工作于分片层,也就是Lucene部分,超出我们的研究范围了。
我们快刀斩乱麻的记住,使用前缀查询会非常的影响性能,要对结果集进行限制,就加上这个参数。
五 match系列之multi_match(多字段查询)
现在,我们有一个50个字段的索引,我们要在多个字段中查询同一个关键字,该怎么做呢?
PUT t3/doc/1 { "title": "maggie is beautiful girl", "desc": "beautiful girl you are beautiful so" } PUT t3/doc/2 { "title": "beautiful beach", "desc": "I like basking on the beach,and you? beautiful girl" }数据
我们先用原来的方法查询:
GET t3/doc/_search { "query": { "bool": { "must": [ { "match": { "title": "beautiful" } }, { "match": { "desc": "beautiful" } } ] } } }
使用must
来限制两个字段(值)中必须同时含有关键字。这样虽然能达到目的,但是当有很多的字段呢,我们可以用multi_match
来做:
GET t3/doc/_search { "query": { "multi_match": { "query": "beautiful", "fields": ["title", "desc"] } } }
我们将多个字段放到fields
列表中即可。以达到匹配多个字段的目的。
除此之外,multi_match
甚至可以当做match_phrase
和match_phrase_prefix
使用,只需要指定type
类型即可:
GET t3/doc/_search { "query": { "multi_match": { "query": "gi", "fields": ["title"], "type": "phrase_prefix" } } } GET t3/doc/_search { "query": { "multi_match": { "query": "girl", "fields": ["title"], "type": "phrase" } } }
总结:
- match:返回所有匹配的分词。
- match_all:查询全部。
- match_phrase:短语查询,在match的基础上进一步查询词组,可以指定
slop
分词间隔。 - match_phrase_prefix:前缀查询,根据短语中最后一个词组做前缀匹配,可以应用于搜索提示,但注意和
max_expanions
搭配。其实默认是50....... - multi_match:多字段查询,使用相当的灵活,可以完成
match_phrase
和match_phrase_prefix
的工作。