文章目录
搜索
即从一个索引下按照特定的字段或关键词搜索出符合用户预期的一个或者一堆cocument,然后根据文档的相关度得分,在返回的结果集里并根据得分对这些文档进行一定的排序。
聚合
根据业务需求,对文档中的某个或某几个字段进行数据的分组并做一些指标数据的统计分析,比如要计算一批文档中某个业务字段的总数,平均数,最大最小值等,都属于聚合的范畴。
以上两个概念后是理解下面实验的基础,如果是传统关系数据库mysql、oracle等存储的数据,也可以搜索和聚合,但是在数据聚合分析一块,毕竟不是它们的强项,而且需要在程序中做大量的处理,耗时费力,尤其是大数据量的情况下就有些力不从心了。
但在es中,由于内置了聚合统计的相关功能,只需要使用好它的语法即可达到几近实时的聚合统计,和搜索花费时间基本上没有太大差别,因此使用es很适合在数据量大的业务场景下做聚合统计与分析。
高阶概念
- Buckets(桶/集合):满足特定条件的文档的集合
- Metrics(指标):对桶内的文档进行统计计算(例如最小值,求和,最大值等)
在聚合统计分析中,使用很频繁的一个名词叫 aggs,它是聚合的关键词之一,下面就用实验来演示一下使用aggs进行数据聚合的多种场景。
1、实验准备数据,首先往es整合批量插入一些实验数据,这里我们以一个家电卖场的电视为背景进行模拟
设定文档中field的相关分词属性。
c
PUT http://192.168.56.235:9201/demo2
{
"setting":{
"index":{
"number_of_shards":5,
"number_of_replicats":1
}
},
"mappings":{
"sales":{
"properties":{
"price":{
"type":"long"
},
"color":{
"type":"keyword"
},
"brand":{
"type":"keyword"
},
"sold_date":{
"type":"date"
}
}
}
}
}
2、批量插入数据
c
POST http://192.168.56.235:9201/demo2/sales
{ "price" : 1000, "color" : "红色", "brand" : "长虹", "sold_date" : "2016-10-28" }
{ "price" : 2000, "color" : "红色", "brand" : "长虹", "sold_date" : "2016-11-05" }
{ "price" : 3000, "color" : "绿色", "brand" : "小米", "sold_date" : "2017-05-18" }
{ "price" : 1500, "color" : "蓝色", "brand" : "TCL", "sold_date" : "2017-07-02" }
{ "price" : 1200, "color" : "绿色", "brand" : "TCL", "sold_date" : "2018-08-19" }
{ "price" : 2000, "color" : "红色", "brand" : "长虹", "sold_date" : "2017-11-05" }
{ "price" : 8000, "color" : "红色", "brand" : "三星", "sold_date" : "2017-01-01" }
{ "price" : 2500, "color" : "蓝色", "brand" : "小米", "sold_date" : "2018-02-12" }
数据准备完毕
2、按照颜色分组统计各种颜色电视的数量
查询语法如下:
c
GET http://192.168.56.235:9201/demo2/sales/_search
{
"size":0,
"aggs":{
"group_color":{
"terms":{
"field":"color"
}
}
}
}
查询结果如下,这里简单对其中的几个参数和结果名称做一下说明。
在查询语句中:
size:0
表示聚合查询的结果不需要返回中间的文档内容,group_color
我们自定义的分组名字,最好是见名知意的
在返回结果中:
hits:{}
,这部分存放的是返回结果的基本统计结果,如果上面的size
制指定了不为0
,文档内容则会放在这个里面buckets:
存放聚合后的统计结果详细信息,以key-value
的形式展现
3、按照颜色分组统计各种颜色电视的数量,并在此基础上,统计出各种颜色电视的平均价格
分析:
按照color去分bucket,可以拿到每个color bucket中的数量,这个仅仅只是一个bucket操作, doc_count其实只是es的bucket操作默认执行的一个内置metric。
在一个aggs执行的bucket操作(terms),平级的json结构下,再加一个aggs,这个第二个aggs内部,同样取个名字,执行一个metric操作,avg,对之前的每个bucket中的数据的指定的field、pricefield,求一个平均值
就是一个metric,就是一个对一个bucket分组操作之后,对每个bucket都要执行的一个metric,也可以理解成功嵌套聚合,在es中获取到某个指标的数据后,继续对这个指标的数据进行其他聚合分析也被叫做下钻
该需求查询语句如下:
c
{
"size":0,
"aggs":{
"group_color":{
"terms":{
"field":"color"
},
"aggs":{
"avg_color_price":{
"avg":{
"field":"price"
}
}
}
}
}
}
返回结果如下,通过结果可以很清晰的看出来,在颜色统计分析的基础上,每一个{}里面还增加了一个指标,即自定义的计算平均值的avg_color_price
,这个查询几乎是毫秒级的,基本没有延迟,如果转化为sql查询应该是这样的:
c
select avg(price) from tvs.sales group by color
4、根据颜色分组,求出每种颜色的电视价格的最大值,最小值,平均值
c
{
"size":0,
"aggs":{
"group_by_color":{
"terms":{
"field":"color"
},
"aggs":{
"max_price":{
"max":{
"field":"price"
}
},
"min_price":{
"min":{
"field":"price"
}
},
"avg_price":{
"avg":{
"field":"price"
}
}
}
}
}
}
所得结果如下:
c
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 7,
"max_score": 0,
"hits": []
},
"aggregations": {
"group_by_color": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "红色",
"doc_count": 4,
"max_price": {
"value": 8000
},
"min_price": {
"value": 1000
},
"avg_price": {
"value": 3250
}
},
{
"key": "蓝色",
"doc_count": 2,
"max_price": {
"value": 2500
},
"min_price": {
"value": 1500
},
"avg_price": {
"value": 2000
}
},
{
"key": "绿色",
"doc_count": 1,
"max_price": {
"value": 1200
},
"min_price": {
"value": 1200
},
"avg_price": {
"value": 1200
}
}
]
}
}
}
5、按照不同的价格区间对电视进行划分,并求出每个价格区间的电视的平均价格
在es中根据区间间隔划分,有一个叫做 histogram的语法可以帮助我们执行,类似于terms,也是进行bucket分组操作,接收一个field,按照这个field的值的各个范围区间,进行bucket分组操作。
c
"histogram":{
"field": "price",
"interval": 2000
},
- interval:2000,划分范围,0 ~ 2000,2000 ~ 4000,4000 ~ 6000,6000 ~ 8000,8000 ~ 10000,buckets
- 根据price的值,比如2500,看落在哪个区间内,比如2000 ~ 4000,此时就会将这条数据放入2000 ~ 4000对应的那个bucket中
- bucket划分的方法
terms
,将field值相同的数据划分到一个bucket中 - bucket有了之后,一样的,去对每个bucket执行
avg,count,sum,max,min,
等各种metric操作,聚合分析
c
{
"size":0,
"aggs":{
"interval_price":{
"histogram":{
"field":"price",
"interval":2000
},
"aggs":{
"revenue":{
"avg":{
"field":"price"
}
}
}
}
}
}
查询的结果如下:可以看到,按照2000一个等级将所有电视的价格划分在不同的区间了,并将每个区间的价格平均值统计了出来
c
{
"took": 7,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 7,
"max_score": 0,
"hits": []
},
"aggregations": {
"interval_price": {
"buckets": [
{
"key": 0,
"doc_count": 3,
"revenue": {
"value": 1233.3333333333333
}
},
{
"key": 2000,
"doc_count": 3,
"revenue": {
"value": 2166.6666666666665
}
},
{
"key": 4000,
"doc_count": 0,
"revenue": {
"value": null
}
},
{
"key": 6000,
"doc_count": 0,
"revenue": {
"value": null
}
},
{
"key": 8000,
"doc_count": 1,
"revenue": {
"value": 8000
}
}
]
}
}
}
6、按照不同的时间区间对电视进行划分,并求出每个价格区间的电视的平均价格
date histogram,按照我们指定的某个date类型的日期field,以及日期interval,按照一定的日期间隔,去划分bucket,这个概念的理解和上一个有点类似,可以对照理解。
date interval = 1 month
2017-01-01~2017-01-31,就是一个bucket
2017-02-01~2017-02-28,就是一个bucket
然后会去扫描每个数据的date field,判断date落在哪个bucket中,就将其放入那个bucket
2017-01-05,就将其放入2017-01-01~2017-01-31,就是一个bucket
min_doc_count :即使某个日期interval,2017-01-01~2017-01-31中,一条数据都没有,那么这个区间也是要返回的,不然默认是会过滤掉这个区间的
extended_bounds,min,max:划分bucket的时候,会限定在这个起始日期,和截止日期内
根据上述分析我们构建查询语句
c
{
"size":0,
"aggs":{
"sales":{
"date_histogram":{
"field":"sold_date",
"interval":"month",
"format":"yyyy-MM-dd",
"min_doc_count":0,
"extended_bounds":{
"min":"2017-01-01",
"max":"2018-12-31"
}
}
}
}
}
返回结果如下,按照月份,将指定区间内各个月份 的数量做了统计
当然,如果我们觉得按照月份统计粒度太细,也可以根据季度 对数据进行统计,只需要将month 换成quarter即可,查询语法如下:
c
{
"size":0,
"aggs":{
"sales":{
"date_histogram":{
"field":"sold_date",
"interval":"quarter",
"format":"yyyy-MM-dd",
"min_doc_count":0,
"extended_bounds":{
"min":"2017-01-01",
"max":"2018-12-31"
}
}
}
}
}
查询结果如下: