Elasticsearch聚合学习之二:区间聚合
Posted 程序员欣宸
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Elasticsearch聚合学习之二:区间聚合相关的知识,希望对你有一定的参考价值。
欢迎访问我的GitHub
本篇概览
- 本文是《Elasticsearch聚合学习》系列的第二篇,上一篇是我们熟悉了聚合的基本操作,本篇的内容是按照区间聚合的实战操作;
环境信息
- 以下是本次实战的环境信息,请确保您的Elasticsearch可以正常运行:
- 操作系统:Ubuntu 18.04.2 LTS
- JDK:1.8.0_191
- Elasticsearch:6.7.1
- Kibana:6.7.1
- 实战用的数据依然是一些汽车销售的记录,在第一章有详细的导入步骤,请参考操作,导入后您的es中的数据如下图:
本章概要
- 本篇实战的聚合操作有以下内容:
- 指定字段的区间聚合;
- 时间字段的区间聚合;
- 扩展实战;
- 接下来开始实战吧。
条形图(histogram桶)
-
还记得terms桶么,用来将指定字段值相同的文档聚合在一个桶中,而histogram桶是将指定字段值在某个范围内的文档聚合在一个桶中,如下图所示,0-19999是一个桶,11000和15000在一个桶内,23000和31000在一个桶内,这就是histogram桶:
-
以汽车销售记录为例做一次聚合查询,为售价创建histogram桶,以20000作为间隔,每个桶负责的区间如上图所示,相关的销售记录就会被放入对应的桶中,请求参数和说明如下:
GET /cars/transactions/_search
"size":0, ---令返回值的hits对象为空
"aggs": ---聚合命令
"price": ---聚合字段名称
"histogram": ---桶类型
"field": "price", ---指定price字段的值作为判断条件
"interval": 20000 ---每个桶负责的区间大小为20000
- es返回的数据和说明如下:
"took" : 57,
"timed_out" : false,
"_shards" :
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
,
"hits" :
"total" : 8,
"max_score" : 0.0,
"hits" : [ ]
,
"aggregations" : ---聚合结果
"price" : ---请求参数中指定的名称
"buckets" : [ ---price桶的数据在此数组中
"key" : 0.0, ---第一个桶,区间[0-19999],0.0是起始值
"doc_count" : 3 ---这个区间有三个文档(price值分别是10000、12000、15000)
,
"key" : 20000.0, ---第二个桶,区间[20000-39999],20000.0是起始值
"doc_count" : 4 ---这个区间有四个文档
,
"key" : 40000.0, ---第三个桶,区间[40000-59999],40000.0是起始值
"doc_count" : 0 ---这个区间没有文档
,
......
控制空桶是否返回
- 在上面的返回值中,第三个桶中没有文档,在有的业务场景中,我们不需要没有数据的桶,此时可以用min_doc_count参数来控制,如果min_doc_count等于2,表示桶中最少有两条记录才会出现在返回内容中,如下所示,min_doc_count如果等于1,那么空桶就不会被es返回了:
GET /cars/transactions/_search
"size":0,
"aggs":
"price":
"histogram":
"field": "price",
"interval": 20000,
"min_doc_count": 1
- 返回值如下所示,没有文档的桶不再出现:
"took" : 16,
"timed_out" : false,
"_shards" :
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
,
"hits" :
"total" : 8,
"max_score" : 0.0,
"hits" : [ ]
,
"aggregations" :
"price" :
"buckets" : [
"key" : 0.0,
"doc_count" : 3
,
"key" : 20000.0,
"doc_count" : 4
,
"key" : 80000.0,
"doc_count" : 1
]
histogram桶加metrics
- 上面的例子返回结果只有每个桶内的文档数,也可以加入metrics对桶中的数据进行处理,例如计算每个区间内的最高价、最低价、平均售价,可以加入max、min、avg参数,如下:
GET /cars/transactions/_search
"size":0, ---令返回值的hits对象为空
"aggs": ---聚合命令
"price": ---聚合字段名称
"histogram": ---桶类型
"field": "price", ---指定price字段的值作为判断条件
"interval": 20000 ---每个桶负责的区间大小为20000
,
"aggs": ---表示对桶内数据做metrics
"max_price": ---指定metrics处理结果的字段名
"max": ---metrics类型为max
"field": "price" ---指定取price字段的值做最大值比较
,
"min_price": ---指定metrics处理结果的字段名
"min": ---metrics类型为min
"field": "price" ---指定取price字段的值做最小值比较
,
"avg_price": ---指定metrics处理结果的字段名
"avg": ---metrics类型为avg
"field": "price" ---指定取price字段的值计算平均值
- es返回数据和说明如下,可见每个桶中的文档都做了三种metrics处理:
"took" : 17,
"timed_out" : false,
"_shards" :
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
,
"hits" :
"total" : 8,
"max_score" : 0.0,
"hits" : [ ]
,
"aggregations" : ---聚合结果
"price" : ---请求参数中指定的名称
"buckets" : [ ---price桶的数据在此数组中
"key" : 0.0, ---第一个区间[0-19999],0.0是起始值
"doc_count" : 3, ---这个区间有三条记录(price值分别是10000、12000、15000)
"max_price" : ---指定的metrics结果名称
"value" : 15000.0 ---桶中有三个文档,price字段的最大值是15000
,
"min_price" :
"value" : 10000.0 ---桶中有三个文档,price字段的最小值是10000
,
"avg_price" :
"value" : 12333.333333333334 ---桶中有三个文档,price字段的平均值是12333.333333333334
,
......
时间区间的桶(date_histogram)
- 按照时间区间聚合也是常用的功能,例如在ELK上查询日志,通常都是按照时间来分段的,如下图:
-
histogram桶可以实现按照时间分段么?如果用毫秒数来处理,似乎是可以的,但是对年月日的处理就力不从心了,常见的时间区间处理,用date_histogram桶即可满足要求;
-
下面就是date_histogram桶的用法:每月销售多少台汽车:
GET /cars/transactions/_search
"size": 0, ---令返回值的hits对象为空
"aggs": ---聚合命令
"sales": ---聚合字段名称
"date_histogram": ---桶类型
"field": "sold", ---用sold字段的值作进行时间区间判断
"interval": "month", ---间隔单位是月
"format": "yyyy-MM-dd" ---返回的数据中,时间字段格式
,
"aggs": ---表示对桶内数据做metrics
"max_price": ---指定metrics处理结果的字段名
"max": ---metrics类型为max
"field": "price" ---指定取price字段的值做最大值比较
,
"min_price": ---指定metrics处理结果的字段名
"min": ---metrics类型为min
"field": "price" ---指定取price字段的值做最小值比较
- es返回数据如下,篇幅所限因此略去了头部和尾部的一些信息,只看关键的:
"aggregations" : ---聚合结果
"sales" : ---请求参数中指定的名称
"buckets" : [ ---sales桶的数据在此数组中
"key_as_string" : "2014-01-01", ---请求的format参数指定了key的格式
"key" : 1388534400000, ---真正的时间字段
"doc_count" : 1, ---2014年1月份的文档数量
"max_price" : ---2014年1月的文档做了metrics类型为max的处理后,结果在此
"value" : 80000.0 ---2014年1月的文档中,price字段的最大值
,
"min_price" : ---2014年1月的文档做了metrics类型为min的处理后,结果在此
"value" : 80000.0 ---2014年1月的文档中,price字段的最大值
,
"key_as_string" : "2014-02-01",
"key" : 1391212800000,
"doc_count" : 1,
"max_price" :
"value" : 25000.0
,
"min_price" :
"value" : 25000.0
,
......
- 上面的请求是以一个月作为区间的,如果想以其他时间单位作为区间又该怎么做呢?例如90天,把interval字段写成90d即可,其他粒度的时间间隔写法如下表:
表达式 | 含义 |
---|---|
1y | 一年(数量只能是1,例如2y不合法) |
1q | 一个季度(数量只能是1,例如2q不合法) |
1M | 一个月(数量只能是1,例如2M不合法,注意区分大写,M表示月,m表示分钟) |
1w | 一周(数量只能是1,例如2w不合法) |
2d | 两天(数量可以是整数类型) |
3h | 三个小时(数量可以是整数类型) |
4m | 四分钟(数量可以是整数类型,注意区分大写,M表示月,m表示分钟) |
5s | 五秒钟(数量可以是整数类型) |
- 注意:年、季度、月、周都的数量只能是1,其他粒度的数量可以是整数;
- 例如以90天作为区间来聚合,请求参数如下:
GET /cars/transactions/_search
"size": 0,
"aggs":
"sales":
"date_histogram":
"field": "sold",
"interval": "90d", -------表示以90天作为间隔
"format": "yyyy-MM-dd"
,
"aggs":
"max_price":
"max":
"field": "price"
,
"min_price":
"min":
"field": "price"
date_histogram的空桶处理
- date_histogram也支持min_doc_count参数,和histogram桶的用法一样,对于下面的请求,es的响应中不会有空桶:
GET /cars/transactions/_search
"size": 0,
"aggs":
"sales":
"date_histogram":
"field": "sold",
"interval": "1M",
"format": "yyyy-MM-dd",
"min_doc_count": 1
扩展实战
- 本篇的最后,来做一个略为复杂的聚合操作:按季度展示每个汽车品牌的销售总额;
- 显然,操作的第一步是按照时间区间做聚合,然后在每个桶中,将文档按照品牌做第二次聚合,第二次聚合的结果也可以理解为多个桶,每个桶中的文档,是某个平台在某个季度的销售总额;
- 请求如下:
GET /cars/transactions/_search
"size": 0, ---令返回值的hits对象为空
"aggs": ---聚合命令
"sales": ---聚合字段名称
"date_histogram": ---桶类型为时间区间
"field": "sold", ---指定sold字段的值作为判断条件
"interval": "1q", ---区间间隔为1季度
"format": "yyyy-MM-dd", ---返回的桶的key,被格式化时的格式
"min_doc_count": 1 ---空桶不返回
,
"aggs": ---第二层桶
"per_make_sum": ---聚合字段名称
"terms": ---桶类型为terms
"field": "make" ---按照make字段聚合
,
"aggs": ---第二层桶的metrics
"sum_price": ---聚合字段名称
"sum": ---metrics处理,累加
"field": "price" ---取price字段的值累加
- 收到响应如下:
"aggregations" :
"sales" :
"buckets" : [ ---聚合结果
"key_as_string" : "2014-01-01", ---当前桶的key的格式化后的值
"key" : 1388534400000, ---当前桶的key原值
"doc_count" : 2, ---当前桶中文档数
"per_make_sum" : ---第二层桶的名称
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [ ---第二层聚合结果
"key" : "bmw", ---聚合字段的值,这里是汽车品牌
"doc_count" : 1, ---桶内的文档数量
"sum_price" : ---metrics处理结果名称
"value" : 80000.0 ---metrics处理结果,这里是销售额累加值
,
"key" : "ford", ---聚合字段的值,这里是汽车品牌
"doc_count" : 1, ---桶内的文档数量
"sum_price" : ---metrics处理结果名称
"value" : 25000.0 ---metrics处理结果,这里是销售额累加值
]
,
"key_as_string" : "2014-04-01",
"key" : 1396310400000,
"doc_count" : 1,
"per_make_sum" :
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
"key" : "ford",
"doc_count" : 1,
"sum_price" :
"value" : 30000.0
]
,
- 至此,区间聚合的学习和实战就完成了,到目前为止,我们的操作用的都是索引中的全部数据,但是真是生产环境中,不会每次都用全部数据来做聚合,因此接下来的章节,会将聚合与查询、过滤等操作结合在一起实战;
欢迎关注51CTO博客:程序员欣宸
以上是关于Elasticsearch聚合学习之二:区间聚合的主要内容,如果未能解决你的问题,请参考以下文章
Elasticsearch学习之深入聚合分析二---案例实战
Elasticsearch学习之嵌套聚合,下钻分析,聚合分析
Elasticsearch学习之深入聚合分析三---案例实战