ES中的聚合查询,类似SQL的SUM/AVG/COUNT/GROUP BY分组查询,主要用于统计分析场景
1 概念
1> ES聚合查询流程
ES聚合查询类似SQL的GROUP by,一般统计分析主要分为两个步骤:
- 分组
- 组内聚合
对查询的数据首先进行一轮分组,可以设置分组条件,例如:新生入学,把所有的学生按专业分班,这个分班的过程就是对学生进行了分组。
组内聚合,就是对组内的数据进行统计,例如:计算总数、求平均值等等,接上面的例子,学生都按专业分班了,那么就可以统计每个班的学生总数, 这个统计每个班学生总数的计算,就是组内聚合计算。
2> 桶
满足特定条件的文档的集合,叫做桶。
桶的就是一组数据的集合,对数据分组后,得到一组组的数据,就是一个个的桶。
提示:桶等同于组,分桶和分组是一个意思,ES使用桶代表一组相同特征的数据。
ES中桶聚合,指的就是先对数据进行分组,ES支持多种分组条件,例如:支持类似SQL的group by根据字段分组,当然ES比SQL更强大,支持更多的分组条件,以满足各种统计需求
3> 指标
指标指的是对文档进行统计计算方式,又叫指标聚合。
桶内聚合,说的就是先对数据进行分组(分桶),然后对每一个桶内的数据进行指标聚合。
说白了就是,前面将数据经过一轮桶聚合,把数据分成一个个的桶之后,我们根据上面计算指标对桶内的数据进行统计。
常用的指标有:SUM、COUNT、MAX等统计函数。
2. Elasticsearch 分组聚合查询(bucket)
Elasticsearch桶聚合,目的就是数据分组,先将数据按指定的条件分成多个组,然后对每一个组进行统计。 组的概念跟桶是等同的,在ES中统一使用桶(bucket)这个术语。
ES桶聚合的作用跟SQL的group by的作用是一样的,区别是ES支持更加强大的数据分组能力,SQL只能根据字段的唯一值进行分组,分组的数量跟字段的唯一值的数量相等,例如: group by 店铺id, 去掉重复的店铺ID后,有多少个店铺就有多少个分组。
ES常用的桶聚合如下:
- Terms聚合 - 类似SQL的group by,根据字段唯一值分组
- Histogram聚合 - 根据数值间隔分组,例如: 价格按100间隔分组,0、100、200、300等等
- Date histogram聚合 - 根据时间间隔分组,例如:按月、按天、按小时分组
- Range聚合 - 按数值范围分组,例如: 0-150一组,150-200一组,200-500一组。
1> Terms聚合
terms聚合的作用跟SQL中group by作用一样,都是根据字段唯一值对数据进行分组(分桶),字段值相等的文档都分到同一个桶内。
GET /order/_search?size=0
{
"size":0,
"aggs": {
"shop_bulk": { // 聚合查询的名字,随便取个名字
"terms": { // 聚合类型为: terms
"field": "shop_id" // 根据shop_id字段值,分桶
}
}
}
}
等价SQL:
select shop_id, count(*) from order group by shop_id
2>Histogram聚合
histogram(直方图)聚合,主要根据数值间隔分组,使用histogram聚合分桶统计结果,通常用在绘制条形图报表。
POST /sales/_search
{
"size":0,
"aggs" : {
"prices_bulk" : { // 聚合查询名字,随便取一个
"histogram" : { // 聚合类型为:histogram
"field" : "price", // 根据price字段分桶
"interval" : 50 // 分桶的间隔为50,意思就是price字段值按50间隔分组
}
}
}
}
3> Date histogram聚合
类似histogram聚合,区别是Date histogram可以很好的处理时间类型字段,主要用于根据时间、日期分桶的场景。
POST /sales/_search?size=0
{
"aggs" : {
"sales_over_time" : { // 聚合查询名字,随便取一个
"date_histogram" : { // 聚合类型为: date_histogram
"field" : "date", // 根据date字段分组
"calendar_interval" : "month", // 分组间隔:month代表每月、支持minute(每分钟)、hour(每小时)、day(每天)、week(每周)、year(每年)
"format" : "yyyy-MM-dd" // 设置返回结果中桶key的时间格式
}
}
}
}
4> Range聚合
range聚合,按数值范围分桶。
GET /xxx/_search
{
"query": {
"range": {
"alarmNum": {
"gte": 20,
"lte": 50
}
}
},
"size": 0,
"aggs": {
"warncont": {
"range": {
"field": "alarmNum",
"ranges": [
{
"to": 24 #<
},
{
"from": 24, #>= and <
"to": 32
},
{
"from": 32 #>=
}
]
}
}
}
}
3.Elasticsearch 指标聚合(metrics)
ES指标聚合,就是类似SQL的统计函数,指标聚合可以单独使用,也可以跟桶聚合一起使用。
常用的统计函数如下:
- Value Count - 类似sql的count函数,统计总数
- Cardinality - 类似SQL的count(DISTINCT 字段), 统计不重复的数据总数
- Avg - 求平均值
- Sum - 求和
- Max - 求最大值
- Min - 求最小值
1> Value Count
GET /sales/_search?size=0
{
"aggs": {
"types_count": { // 聚合查询的名字,随便取个名字
"value_count": { // 聚合类型为:value_count
"field": "type" // 计算type这个字段值的总数
}
}
}
}
等价SQL:
select count(type) from sales
2> Cardinality
基数聚合,也是用于统计文档的总数,跟Value Count的区别是,基数聚合会去重,不会统计重复的值,类似SQL的count(DISTINCT 字段)用法。
POST /sales/_search?size=0
{
"aggs" : {
"type_count" : { // 聚合查询的名字,随便取一个
"cardinality" : { // 聚合查询类型为:cardinality
"field" : "type" // 根据type这个字段统计文档总数
}
}
}
}
等价SQL:
select count(DISTINCT type) from sales
3> Avg,Sum,Max,Min
求平均值
POST /exams/_search?size=0
{
"aggs": {
"avg_grade": { // 聚合查询名字,随便取一个名字
"avg": { // 聚合查询类型为: avg
"field": "grade" // 统计grade字段值的平均值
}
}
}
}
4. 综合例子(桶+指标)
1> 1个分组后,多个指标计算
GET /cars/_search
{
"size": 0, // size=0代表不需要返回query查询结果,仅仅返回aggs统计结果
"query" : { // 设置查询语句,先赛选文档
"match" : {
"make" : "ford"
}
},
"aggs" : { // 然后对query搜索的结果,进行统计
"colors" : { // 聚合查询名字
"terms" : { // 聚合类型为:terms 先分桶
"field" : "color"
},
"aggs": { // 通过嵌套聚合查询,设置桶内指标聚合条件
"avg_price": { // 聚合查询名字
"avg": { // 聚合类型为: avg指标聚合
"field": "price" // 根据price字段计算平均值
}
},
"sum_price": { // 聚合查询名字
"sum": { // 聚合类型为: sum指标聚合
"field": "price" // 根据price字段求和
}
}
}
}
}
}
2> 多个分组后,多个指标计算
GET /project_zcy/_search
{
"size": 0,
"query": {
"term": {
"project_status.keyword": {
"value": "执行中"
}
}
},
"aggs": {
"institue_bulk": {
"terms": {
"field": "institute_code.keyword", #分组code
"order": {
"_count": "asc"
}
},
"aggs": {
"depart_bulk": {
"terms": {
"field": "institute_name.keyword" #再次分组获取名称
},
"aggs": { #指标sum
"warm_sum": {
"sum": {
"field": "alarmNum"
}
},
"index_count": { #指标count
"value_count": {
"field": "institute_code.keyword"
}
}
}
}
}
}
}
}
5. Elasticsearch 聚合后排序
类似terms、histogram、date_histogram这类桶聚合都会动态生成多个桶,如果生成的桶特别多,我们如何确定这些桶的排序顺序,如何限制返回桶的数量。
1> 多桶排序
排序是对桶里面数据排序
默认情况,ES会根据doc_count文档总数,降序排序。
ES桶聚合支持两种方式排序:
- 内置排序
- 按度量指标排序
2> 内置排序
内置排序参数:
- _count - 按文档数排序。对 terms 、 histogram 、 date_histogram 有效
- _term - 按词项的字符串值的字母顺序排序。只在 terms 内使用
- _key - 按每个桶的键值数值排序, 仅对 histogram 和 date_histogram 有效
GET /cars/_search
{
"size" : 0,
"aggs" : {
"colors" : { // 聚合查询名字,随便取一个
"terms" : { // 聚合类型为: terms
"field" : "color",
"order": { // 设置排序参数
"_count" : "asc" // 根据_count排序,asc升序,desc降序
}
}
}
}
}
3> 按度量排序
通常情况下,我们根据桶聚合分桶后,都会对桶内进行多个维度的指标聚合,所以我们也可以根据桶内指标聚合的结果进行排序。
GET /cars/_search
{
"size" : 0,
"aggs" : {
"colors" : { // 聚合查询名字
"terms" : { // 聚合类型: terms,先分桶
"field" : "color", // 分桶字段为color
"order": { // 设置排序参数
"avg_price" : "asc" // 根据avg_price指标聚合结果,升序排序。
}
},
"aggs": { // 嵌套聚合查询,设置桶内聚合指标
"avg_price": { // 聚合查询名字,前面排序引用的就是这个名字
"avg": {"field": "price"} // 计算price字段平均值
}
}
}
}
}
6. 限制返回桶的数量
如果分桶的数量太多,可以通过给桶聚合增加一个size参数限制返回桶的数量。
GET /_search
{
"aggs" : {
"products" : { // 聚合查询名字
"terms" : { // 聚合类型为: terms
"field" : "product", // 根据product字段分桶
"size" : 5 // 限制最多返回5个桶
}
}
}
}