ElasticSearch之-文档操作
#新增一个id为1的书籍(POST和PUT都可以)
PUT lqz/_doc/1 #POST lqz/_doc/1/_create POST lqz/_doc/1 POST lqz/_doc 会自动创建id,必须用Post
{
"title":"红楼梦",
"price":12,
"publish_addr":{
"province":"黑龙江",
"city":"鹤岗"
},
"publish_date":"2013-11-11",
"read_num":199,
"tag":["古典","名著"]
}
#查询lqz索引下id为7的文档
GET lqz/_doc/7
#查询lqz索引下id为7的文档,只要title字段
GET lqz/_doc/7?_source=title
#查询lqz索引下id为7的文档,只要title和price字段
GET lqz/_doc/7?_source=title,price
#查询lqz索引下id为7的文档,要全部字段
GET lqz/_doc/7?_source
#修改文档(覆盖修改) PUT命令,在做修改操作时,如果未指定其他的属性,则按照指定的属性进行修改操作;只修改了"title"属性,其他的属性并没有一起添加进去
PUT lqz/_doc/1
{
"title":"金品梅",
}
#修改文档,增量修改,修改了"title",原来其他的属性还在
#POST命令,这里可用来执行修改操作(还有其他的功能),`POST`命令配合`_update`完成修改操作,指定修改的内容放到`doc`中。
#注意是post请求,并且把修改的字段放在"doc"字典里
POST lqz/_update/1
{
"doc":{
"title":"足球小将"
}
}
#删除文档id为1的
DELETE lqz/_doc/1
#批量获取lqz索引_doc类型下id为2的数据和lqz2索引_doc类型下id为1的数据
GET _mget
{
"docs":[
{
"_index":"lqz",
"_type":"_doc",
"_id":2
},
{
"_index":"lqz2",
"_type":"_doc",
"_id":1
}
]
}
#批量获取lqz索引下id为1和2的数据
GET lqz/_mget
{
"docs":[
{
"_id":2
},
{
"_id":1
}
]
}
#同上
GET lqz/_mget
{
"ids":[1,2]
}
PUT test/_doc/2/_create
{
"field1" : "value22"
}
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
简单的没挑战,来点复杂的,elasticsearch提供两种查询方式:
-
查询字符串(query string),简单查询,就像是像传递URL参数一样去传递查询语句,被称为简单搜索或查询字符串(query string)搜索。
-
另外一种是通过DSL语句来进行查询,被称为DSL查询(Query DSL),DSL是Elasticsearch提供的一种丰富且灵活的查询语言,该语言以json请求体的形式出现,通过restful请求与Elasticsearch进行交互。
PUT zl/_doc/1
{
"name":"刘亦菲",
"age":30,
"from": "中国",
"desc": "性格直、娇美",
"tags": ["靓", "矮", "直"]
}
PUT zl/_doc/2
{
"name":"高圆圆",
"age":18,
"from":"中国",
"desc":"肤白貌美,娇憨可爱",
"tags":["白", "富","美"]
}
PUT zl/_doc/3
{
"name":"古力娜扎",
"age":22,
"from":"新疆",
"desc":"高鼻子,大眼睛,高大",
"tags":["高", "大", "上"]
}
PUT zl/_doc/4
{
"name":"仓老师",
"age":29,
"from":"日本",
"desc":"粗中有细,影后",
"tags":["肥", "大","猛"]
}
PUT zl/_doc/5
{
"name":"汤唯",
"age":25,
"from":"中国",
"desc":"仿佛兮若轻云之蔽月",
"tags":["闭月","羞花"]
}
GET zl/_doc/_search?q=from:中国 #查询form字段是"中国"的所有人
GET
命令,通过_serarch
查询,查询条件是什么呢?条件是from
属性是中国
的人都有哪些。最后,别忘了_search
和from
属性中间的英文分隔符
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.077993,
"hits" : [
{
"_index" : "zl",
"_type" : "doc",
"_id" : "1",
"_score" : 1.077993,
"_source" : {
"name" : "刘亦菲",
"age" : 30,
"from" : "中国",
"desc" : "性格直、娇美",
"tags" : [
"靓",
"矮",
"直"
]
}
},
{
"_index" : "zl",
"_type" : "doc",
"_id" : "2",
"_score" : 1.077993,
"_source" : {
"name" : "高圆圆",
"age" : 18,
"from" : "中国",
"desc" : "肤白貌美,娇憨可爱",
"tags" : [
"白",
"富",
"美"
]
}
},
{
"_index" : "zl",
"_type" : "doc",
"_id" : "5",
"_score" : 1.077993,
"_source" : {
"name" : "汤唯",
"age" : 25,
"from" : "中国",
"desc" : "仿佛兮若轻云之蔽月",
"tags" : [
"闭月",
"羞花"
]
}
}
]
}
}
GET zl/_doc/_search
{
"query": {
"match": {
"from": "中国"
}
}
}
match
中即可,而match
则是查询所有from
字段的值中含有中国
的结果就会返回。
GET zl/_doc/_search
{
"query": {
"match": {
"from": "中国"
}
}
}
GET zl/_doc/_search
{
"query": {
"match_all": {}
}
}
的值为空,表示没有查询条件,那就是查询全部。就像select * from table_name
zl
索引下_doc
PUT t1/_doc/1
{
"title": "中国是世界上人口最多的国家"
}
PUT t1/_doc/2
{
"title": "美国是世界上军事实力最强大的国家"
}
PUT t1/_doc/3
{
"title": "北京是中国的首都"
}
中国
作为搜索条件,我们希望只返回和中国
相关的文档。我们首先来使用match
GET t1/_doc/_search
{
"query": {
"match": {
"title": "中国"
}
}
}
中国
的文档。但是却把和美国
的文档也返回了,这并不是我们想要的。是怎么回事呢?因为这是elasticsearch在内部对文档做分词的时候,对于中文来说,就是一个字一个字分的,所以,我们搜中国
,中
和国
都符合条件,返回,而美国的国
也符合。 而我们认为中国
是个短语,是一个有具体含义的词。所以elasticsearch在处理中文分词方面比较弱势。后面会讲针对中文的插件。
GET t1/_doc/_search
{
"query": {
"match_phrase": {
"title": {
"query": "中国"
}
}
}
}
match_phrase
是在文档中搜索指定的词组,而中国
则正是一个词组,所以愉快的返回了。 那么,现在我们要想搜索中国
和世界
相关的文档,但又忘记其余部分了,怎么做呢?用match
也不行,那就继续用match_phrase
GET t1/_doc/_search
{
"query": {
"match_phrase": {
"title": "中国世界"
}
}
}
中国世界
这个短语。 我们搜索中国
和世界
这两个指定词组时,但又不清楚两个词组之间有多少别的词间隔。那么在搜的时候就要留有一些余地。这时就要用到了slop
了。相当于正则中的中国.*?世界
GET t1/_doc/_search
{
"query": {
"match_phrase": {
"title": {
"query": "中国世界",
"slop": 2
}
}
}
}
现在,两个词组之间有了2个词的间隔,这个时候,就可以查询到结果了。
slop
间隔你可以根据需要适当改动。上例表示 中国和世界这两个词之间最小的距离为2, 因为这两个词中间间隔1,只要大于等1的间隔距离都能匹配。
短语查询, 比如要查询:python系统 会把查询条件 “python和系统分词”,放到列表中,再去搜索的时候,必须满足python和系统同时存在的才能搜出来
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
和match_phrase
都不太合适,因为小黑输入的不是完整的词。那怎么办呢?我们用match_phrase_prefix
GET t3/_doc/_search
{
"query": {
"match_phrase_prefix": {
"desc": "bea"
}
}
}
结果搜出了t3索引下的两个_doc文档
you are bea
)。应用也非常的广泛,比如搜索框的提示信息,当使用这种行为进行搜索时,最好通过max_expansions
GET t3/_doc/_search
{
"query": {
"match_phrase_prefix": {
"desc": {
"query": "bea",
"max_expansions": 1
}
}
}
}
max_expansions
执行的是搜索的编辑(Levenshtein)距离。那什么是编辑距离呢?编辑距离是一种计算两个字符串间的差异程度的字符串度量(string metric)。我们可以认为编辑距离就是从一个字符串修改到另一个字符串时,其中编辑单个字符(比如修改、插入、删除)所需要的最少次数。俄罗斯科学家Vladimir Levenshtein于1965年提出了这一概念。
我们再引用elasticsearch官网的一段话:
该max_expansions设置定义了在停止搜索之前模糊查询将匹配的最大术语数,也可以对模糊查询的性能产生显着影响。但是,减少查询字词会产生负面影响,因为查询提前终止可能无法找到某些有效结果。重要的是要理解max_expansions查询限制在分片级别工作,这意味着即使设置为1,多个术语可能匹配,所有术语都来自不同的分片。此行为可能使其看起来好像max_expansions没有生效,因此请注意,计算返回的唯一术语不是确定是否有效的有效方法max_expansions。我想你也没看懂这句话是啥意思,但我们只需知道该参数工作于分片层,也就是Lucene部分,超出我们的研究范围了。
我们可以用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
的工作。
-
删除大多数的标点符号。
-
将文档分解为单个词条,我们称为token。
-
将token转为小写。
完事再保存到倒排索引上,当然,原文件还是要保存一分的,而倒排索引是用来查询的。 例如Beautiful girl!
,在经过分析后是这样的了
POST _analyze
{
"analyzer": "standard",
"text": "Beautiful girl!"
}
# 结果
["beautiful", "girl"]
而当在使用match查询时,elasticsearch同样会对查询关键字进行分析:
创建一个索引w10, 创建一个mapping,创建一个t1字段是text类型
PUT w10
{
"mappings": {
"properties":{
"t1":{
"type": "text"
}
}
}
}
# 给t1字段插入数据
PUT w10/_doc/1
{
"t1": "Beautiful girl!"
}
PUT w10/_doc/2
{
"t1": "sexy girl!"
}
# match查询, 会将Beautiful girl分词,分别去查,查出两篇文档
GET w10/_doc/_search
{
"query": {
"match": {
"t1": "Beautiful girl!"
}
}
}
也就是对查询关键字Beautiful girl!
进行分析,得到["beautiful", "girl"]
,然后分别将这两个单独的token去索引w10
中进行查询,结果就是将两篇文档都返回。
这在有些情况下是非常好用的,但是,如果我们想查询确切的词怎么办?也就是精确查询,将Beautiful girl!
当成一个token而不是分词后的两个token。
这就要用到了term查询了,term查询的是没有经过分析的查询关键字。
t1
类型是text
)是text
(因为elasticsearch会对文档进行分析,上面说过),那么你得到的可能是不尽如人意的结果或者压根没有结果:
GET w10/_doc/_search
{
"query": {
"term": {
"t1": "Beautiful girl!"
}
}
}
w10
中的两篇文档在经过elasticsearch分析后没有一个分词是Beautiful girl!
,那此次查询结果为空也就好理解了。
所以,我们这里得到一个论证结果:不要使用term对类型是text的字段进行查询,要查询text类型的字段,请改用match查询。因为elasticsearch会对text类型字段进行分词,用term精准查询无法返回结果。
学会了吗?那再来一个示例,你说一下结果是什么:
GET w10/_doc/_search
{
"query": {
"term": {
"t1": "Beautiful"
}
}
}
beautiful
,而我们查询的是大写的Beautiful
GET w10/doc/_search
{
"query": {
"term": {
"t1": "beautiful"
}
}
}
,你可以将term和keyword结合使用。最后,要想使用term查询多个精确的值怎么办?我只能说:亲,这里推荐卸载es呢!低调又不失尴尬的玩笑!
这里推荐使用terms
查询:
GET w10/_doc/_search
{
"query": {
"terms": {
"t1": ["beautiful", "sexy"]
}
}
}
查询结果如下:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "w10",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"t1" : "Beautiful girl!"
}
},
{
"_index" : "w10",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"t1" : "sexy girl!"
}
}
]
}
}
GET zl/_doc/_search
{
"query": {
"match": {
"from": "中国"
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
sort
来做排序,根据age
字段排序,是降序呢还是升序,由order
字段控制,desc
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "zl",
"_type" : "doc",
"_id" : "1",
"_score" : null,
"_source" : {
"name" : "刘亦菲",
"age" : 30,
"from" : "中国",
"desc" : "性格直、娇美",
"tags" : [
"靓",
"矮",
"直"
]
},
"sort" : [
30
]
},
{
"_index" : "zl",
"_type" : "doc",
"_id" : "5",
"_score" : null,
"_source" : {
"name" : "汤唯",
"age" : 25,
"from" : "中国",
"desc" : "仿佛兮若轻云之蔽月",
"tags" : [
"闭月",
"羞花"
]
},
"sort" : [
25
]
},
{
"_index" : "zl",
"_type" : "doc",
"_id" : "2",
"_score" : null,
"_source" : {
"name" : "高圆圆",
"age" : 18,
"from" : "中国",
"desc" : "肤白貌美,娇憨可爱",
"tags" : [
"白",
"富",
"美"
]
},
"sort" : [
18
]
}
]
}
}
GET zl/_doc/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "asc"
}
}
]
}
注意:在排序的过程中,只能使用可排序的属性进行排序。那么可以排序的属性有哪些呢?
- 数字
- 日期
其他的都不行!
GET zl/_doc/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 2,
"size": 1
}
上例,首先以age
降序排序,查询所有。并且在查询的时候,添加两个属性from
和size
来控制查询结果集的数据条数。
-
from:从哪开始查
-
size:返回几条结果
如上例的结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "zl",
"_type" : "doc",
"_id" : "5",
"_score" : null,
"_source" : {
"name" : "汤唯",
"age" : 25,
"from" : "中国",
"desc" : "仿佛兮若轻云之蔽月",
"tags" : [
"闭月",
"羞花"
]
},
"sort" : [
25
]
}
]
}
}
上例中,在返回的结果集中,从第2条开始,返回1条数据。那如果想要从第2条开始,返回2条结果怎么做呢?
GET zl/_doc/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 2,
"size": 2
}
from
为2,意为从第2条开始返回,返回多少呢?size
如果我们从第4条开始返回2条呢?
GET zl/_doc/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 4,
"size": 2
}
学到这里,我们也可以看到,我们的查询条件越来越多,开始仅是简单查询,慢慢增加条件查询,增加排序,对返回结果进行限制。所以,我们可以说:对于elasticsearch
来说,所有的条件都是可插拔的,彼此之间用,
分割。比如说,我们在查询中,仅对返回结果进行限制:
GET lqz/doc/_search
{
"query": {
"match_all": {}
},
"from": 4,
"size": 2
}
布尔查询是最常用的组合查询,根据子查询的规则,只有当文档满足所有子查询条件时,elasticsearch引擎才将结果返回。布尔查询支持的子查询条件共4种:
-
must(and)
-
should(or)
-
must_not(not)
-
filter
下面我们来看看每个子查询条件都是怎么玩的。
GET zl/_doc/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"from": "中国"
}
}
]
}
}
}
上例中,我们通过在bool
属性(字段)内使用must
来作为查询条件,那么条件是什么呢?条件同样被match
包围,就是from
为中国
的所有数据。 这里需要注意的是must
from
为中国
,并且age
为30
GET zl/_doc/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"from": "中国"
}
},
{
"match": {
"age": 30
}
}
]
}
}
}
must
列表中,在增加一个age
为30
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 2.077993,
"hits" : [
{
"_index" : "zl",
"_type" : "doc",
"_id" : "1",
"_score" : 2.077993,
"_source" : {
"name" : "刘亦菲",
"age" : 30,
"from" : "中国",
"desc" : "性格直、娇美",
"tags" : [
"靓",
"矮",
"直"
]
}
}
]
}
}
GET zl/_doc/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"from": "中国"
}
},
{
"match": {
"tags": "闭月"
}
}
]
}
}
}
must
的了,而是要用should
结果如下:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 3.5933313,
"hits" : [
{
"_index" : "zl",
"_type" : "doc",
"_id" : "5",
"_score" : 3.5933313,
"_source" : {
"name" : "汤唯",
"age" : 25,
"from" : "中国",
"desc" : "仿佛兮若轻云之蔽月",
"tags" : [
"闭月",
"羞花"
]
}
},
{
"_index" : "zl",
"_type" : "doc",
"_id" : "1",
"_score" : 1.077993,
"_source" : {
"name" : "刘亦菲",
"age" : 30,
"from" : "中国",
"desc" : "性格直、娇美",
"tags" : [
"靓",
"矮",
"直"
]
}
},
{
"_index" : "zl",
"_type" : "doc",
"_id" : "2",
"_score" : 1.077993,
"_source" : {
"name" : "高圆圆",
"age" : 18,
"from" : "中国",
"desc" : "肤白貌美,娇憨可爱",
"tags" : [
"白",
"富",
"美"
]
}
}
]
}
}
6.4 must_not
from
既不是中国
并且tags
也不是 高
,还有age
不是18
GET zl/_doc/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"from": "中国"
}
},
{
"match": {
"tags": "高"
}
},
{
"match": {
"age": 18
}
}
]
}
}
}
must
和should
都不能使用,而是使用must_not
,又在内增加了一个age
为18
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "zl",
"_type" : "doc",
"_id" : "4",
"_score" : 0.0,
"_source" : {
"name" : "仓老师",
"age" : 29,
"from" : "日本",
"desc" : "粗中有细,影后",
"tags" : [
"肥",
"大",
"猛"
]
}
}
]
}
}
上例中,只有仓老师这一条数据,因为只有苍老师既不是中国的人,标签没有“高”那一项,年龄也不等于18!
这里有点需要补充,条件中`age`对应的`18`你写成整形还是字符串都没啥……
GET zl/_doc/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"from": "中国"
}
}
],
"filter": {
"range": {
"age": {
"gt": 25
}
}
}
}
}
}
filter
条件过滤查询,过滤条件的范围用range
表示,gt
-
gt
:大于,相当于关系型数据库中的>
。 -
:大于等于,相当于关系型数据库中的
>=
。 -
lt
:小于,相当于关系型数据库中的<
。 -
lte
:小于等于,相当于关系型数据库中的<=
。
from
是中国
,age
在25~30
GET zl/_doc/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"from": "中国"
}
}
],
"filter": {
"range": {
"age": {
"gte": 25,
"lte": 30
}
}
}
}
}
}
注意事项:
filter
过滤,should
是或关系,如果在filter
过滤条件中使用should
的话,结果可能不会尽如人意!建议使用must
代替
-
must
:与关系,相当于关系型数据库中的and
。 -
should
:或关系,相当于关系型数据库中的or
。 -
must_not
:非关系,相当于关系型数据库中的not
。 -
filter
:过滤条件。 -
range
:条件筛选范围。 -
gt
:大于,相当于关系型数据库中的>
。 -
gte
:大于等于,相当于关系型数据库中的>=
。 -
lt
:小于,相当于关系型数据库中的<
。 -
lte
:小于等于,相当于关系型数据库中的<=
。
PUT lqz/_doc/1
{
"name":"顾老二",
"age":30,
"from": "gu",
"desc": "皮肤黑、武器长、性格直",
"tags": ["黑", "长", "直"]
}
GET lqz/_doc/_search
{
"query": {
"match": {
"name": "顾老二"
}
},
"_source": ["name", "age"]
}
_source
来控制仅返回name
和age
{
"took" : 8,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 0.8630463,
"hits" : [
{
"_index" : "lqz",
"_type" : "doc",
"_id" : "1",
"_score" : 0.8630463,
"_source" : {
"name" : "顾老二",
"age" : 30
}
}
]
}
}
在数据量很大的时候,我们需要什么字段,就返回什么字段就好了,用来提高查询效率。
PUT lqz/_doc/4
{
"name":"石头",
"age":29,
"from":"gu",
"desc":"粗中有细,狐假虎威",
"tags":["粗", "大","猛"]
}
GET lqz/_doc/_search
{
"query": {
"match": {
"name": "石头"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}
highlight
属性来实现结果高亮显示,需要的字段名称添加到fields
内即可,elasticsearch
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 1.5098256,
"hits" : [
{
"_index" : "lqz",
"_type" : "doc",
"_id" : "4",
"_score" : 1.5098256,
"_source" : {
"name" : "石头",
"age" : 29,
"from" : "gu",
"desc" : "粗中有细,狐假虎威",
"tags" : [
"粗",
"大",
"猛"
]
},
"highlight" : {
"name" : [
"<em>石</em><em>头</em>"
]
}
}
]
}
}
elasticsearch
GET lqz/_doc/_search
{
"query": {
"match": {
"name": "老二"
}
},
"highlight": {
"pre_tags": "<b class='key' style='color:red'>",
"post_tags": "</b>",
"fields": {
"name": {}
}
}
}
highlight
中,pre_tags
用来实现我们的自定义标签的前半部分,在这里,我们也可以为自定义的标签添加属性和样式。post_tags
实现标签的后半部分,组成一个完整的标签。至于标签中的内容,则还是交给fields
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 2.7725887,
"hits" : [
{
"_index" : "lqz",
"_type" : "_doc",
"_id" : "1",
"_score" : 2.7725887,
"_source" : {
"name" : "顾老二",
"age" : 30,
"from" : "gu",
"desc" : "皮肤很黄,武器很长,性格很直",
"tags" : [
"很黄",
"很长",
"很直"
]
},
"highlight" : {
"name" : [
"顾<b class='key' style='color:red'>老</b><b class='key' style='color:red'>二</b>"
]
}
}
]
}
}
自定义标签中属性或样式中的逗号一律用英文状态的单引号表示,应该与外部elasticsearch
语法的双引号区分开
聚合函数大家都不陌生,elasticsearch中也没玩出新花样,所以,这一章相对简单,只需要记得:
-
avg
-
max
-
min
-
sum
以及各自的用法即可。先来看求平均。
9.2 avg
from
是中国
# select avg(age) as my_avg
GET zl/_doc/_search
{
"query": {
"match": {
"from": "中国"
}
},
"aggs": {
"my_avg": {
"avg": {
"field": "age"
}
}
},
"_source": ["name", "age"]
}
from
是中国
的数据。在此基础上做查询平均值的操作,这里就用到了聚合函数,其语法被封装在aggs
中,而my_avg
则是为查询结果起个别名,封装了计算出的平均值。那么,要以什么属性作为条件呢?是age
年龄,查年龄的什么呢?是avg
,查平均年龄。
返回结果如下:
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.077993,
"hits" : [
{
"_index" : "zl",
"_type" : "doc",
"_id" : "1",
"_score" : 1.077993,
"_source" : {
"name" : "刘亦菲",
"age" : 30
}
},
{
"_index" : "zl",
"_type" : "doc",
"_id" : "2",
"_score" : 1.077993,
"_source" : {
"name" : "高圆圆",
"age" : 18
}
},
{
"_index" : "zl",
"_type" : "doc",
"_id" : "5",
"_score" : 1.077993,
"_source" : {
"name" : "汤唯",
"age" : 25
}
}
]
},
"aggregations" : {
"my_avg" : {
"value" : 24.333333333333332
}
}
}
上例中,在查询结果的最后是平均值信息,可以看到是24.3333岁。
虽然我们已经使用_source
对字段做了过滤,但是还不够。我不想看都有哪些数据,只想看平均值怎么办?别忘了size
!
GET zl/_doc/_search
{
"query": {
"match": {
"from": "中国"
}
},
"aggs": {
"my_avg": {
"avg": {
"field": "age"
}
}
},
"size": 0,
"_source": ["name", "age"]
}
size
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"my_avg" : {
"value" : 24.333333333333332
}
}
}
9.3
GET zl/_doc/_search
{
"query": {
"match": {
"from": "中国"
}
},
"aggs": {
"my_max": {
"max": {
"field": "age"
}
}
},
"size": 0
}
avg
替换成max
9.4
GET zl/_doc/_search
{
"query": {
"match": {
"from": "中国"
}
},
"aggs": {
"my_min": {
"min": {
"field": "age"
}
}
},
"size": 0
}
9.5
GET zl/_doc/_search
{
"query": {
"match": {
"from": "中国"
}
},
"aggs": {
"my_sum": {
"sum": {
"field": "age"
}
}
},
"size": 0
}
9.6
15~20,20~25,25~30
分组,并且算出每组的平均年龄。
分析需求,首先我们应该先把分组做出来。
GET zl/_doc/_search
{
"size": 0,
"query": {
"match_all": {}
},
"aggs": {
"age_group": {
"range": {
"field": "age",
"ranges": [
{
"from": 15,
"to": 20
},
{
"from": 20,
"to": 25
},
{
"from": 25,
"to": 30
}
]
}
}
}
}
aggs
的自定义别名age_group
中,使用range
来做分组,field
是以age
为分组,分组使用ranges
来做,from
和to
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"age_group" : {
"buckets" : [
{
"key" : "15.0-20.0",
"from" : 15.0,
"to" : 20.0,
"doc_count" : 1
},
{
"key" : "20.0-25.0",
"from" : 20.0,
"to" : 25.0,
"doc_count" : 1
},
{
"key" : "25.0-30.0",
"from" : 25.0,
"to" : 30.0,
"doc_count" : 2
}
]
}
}
}
返回的结果中可以看到,已经拿到了三个分组。doc_count
为该组内有几条数据,此次共分为三组,查询出4条内容。还有一条数据的age
属性值是30
,不在分组的范围内!那么接下来,我们就要对每个小组内的数据做平均年龄处理。
GET zl/_doc/_search
{
"size": 0,
"query": {
"match_all": {}
},
"aggs": {
"age_group": {
"range": {
"field": "age",
"ranges": [
{
"from": 15,
"to": 20
},
{
"from": 20,
"to": 25
},
{
"from": 25,
"to": 30
}
]
},
"aggs": {
"my_avg": {
"avg": {
"field": "age"
}
}
}
}
}
}
aggs
对age