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个桶
            }
        }
    }
}

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐