Elasticsearch-桶聚合查询详解

前言

在之前我们详细面熟了es的查询用法,但是es还拥有强大的聚合查询功能,可以得到类似分组,直方图,折线图等数据组合。类似SQL的SUM、AVG、COUNT、GROUP BY
Elasticsearch-02-es的restapi使用

概念

1:ES聚合查询流程

ES聚合查询类似SQL的GROUP by,一般统计分析主要分为两个步骤:

  • 分组

对查询的数据首先进行一轮分组,可以设置分组条件,例如:新生入学,把所有的学生按专业分班,这个分班的过程就是对学生进行了分组。

  • 组内聚合

组内聚合,就是对组内的数据进行统计,例如:计算总数、求平均值等等,接上面的例子,学生都按专业分班了,那么就可以统计每个班的学生总数, 这个统计每个班学生总数的计算,就是组内聚合计算。

2:桶的概念

es中满足特定条件的文档的集合,叫做桶。
桶的就是一组数据的集合,对数据分组后,得到一组组的数据,就是一个个的桶。

提示:桶等同于组,分桶和分组是一个意思,ES使用桶代表一组相同特征的数据。

ES中桶聚合,指的就是先对数据进行分组,ES支持多种分组条件,例如:支持类似SQL的group by根据字段分组,当然ES比SQL更强大,支持更多的分组条件,以满足各种统计需求

3:指标

指标指的是对文档进行统计计算方式,又叫指标聚合。
桶内聚合,说的就是先对数据进行分组(分桶),然后对每一个桶内的数据进行指标聚合。

说白了就是,前面将数据经过一轮桶聚合,把数据分成一个个的桶之后,我们根据上面计算指标对桶内的数据进行统计。比如计算每个桶内,最大值,最小值,平均值等

常用的指标有:SUM、COUNT、MAX等统计函数。

ES分组聚合查询

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一组。

Terms聚合 - 类似SQL的group by,根据字段唯一值分组

terms聚合的作用跟SQL中group by作用一样,都是根据字段唯一值对数据进行分组(分桶),字段值相等的文档都分到同一个桶内。

GET person_info/_search
{
  "size": 0,   //这是为了不返回数据,只返回聚合结果
  "aggs": {
    "buket_name": {   // 聚合查询名字,随便取一个
      "terms": {		//聚合类型为: terms
        "field": "source.keyword" //根据source值来分组
      }
    }
  }
}

结果:

{
  "took" : 82,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 6854,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "buket_name" : {
      "doc_count_error_upper_bound" : 10,
      "sum_other_doc_count" : 1261,
      "buckets" : [
        {
          "key" : "填表",
          "doc_count" : 5340
        },
        {
          "key" : "普查",
          "doc_count" : 56
        },
        {
          "key" : "网上下载",
          "doc_count" : 39
        }
      ]
    }
  }
}

Histogram聚合 - 根据数值间隔分组,可做直方图

histogram(直方图)聚合,主要根据数值间隔分组,使用histogram聚合分桶统计结果,通常用在绘制条形图报表。

GET person_info/_search
{
  "size": 0, //这是为了不返回数据,只返回聚合结果
  "aggs": {
    "buket_name": {  // 聚合查询名字,随便取一个
      "histogram": { // 聚合类型为:histogram
        "field": "age",  //对age字段进行分类
        "interval": 5	//数字间隔为5
      }
    }
  }
}

结果:

{
  "took" : 4,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 6854,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "buket_name" : {
      "buckets" : [
        {
          "key" : 10,
          "doc_count" : 814
        },
        {
          "key" : 15.0,
          "doc_count" : 1612
        },
        {
          "key" : 20.0,
          "doc_count" : 1290
        },
        {
          "key" : 25.0,
          "doc_count" : 3138
        }
      ]
    }
  }
}

Date histogram聚合 - 根据时间间隔分组,可做时间折线图

类似histogram聚合,区别是Date histogram可以很好的处理时间类型字段,主要用于根据时间、日期分桶的场景。


GET person_info/_search
{
  "size": 0,
  "aggs": {
    "buket_name": { //这是为了不返回数据,只返回聚合结果
      "date_histogram": {  // 聚合类型为: date_histogram
        "field": "data",	// 根据date字段分组
        "calendar_interval": "month",		// 分组间隔,详解在下边
        "format" : "yyyy-MM-dd",	// 设置返回结果中桶key的时间格式
        "time_zone": "+08:00", 		//**设置时区,如果存入的时候没设置就不用填**
        "min_doc_count": 0,			// 没有数据的月份返回0
        "extended_bounds": {		//强制返回的日期区间,既需要填充0的范围
          "min": "2000-01-01",
          "max": "2003-01-01"
        }
      }
    }
  }
}

结果:

{
  "took" : 17,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 6854,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "buket_name" : {
      "buckets" : [
        {
          "key_as_string" : "2002-01-01",
          "key" : 1009843200000,
          "doc_count" : 5
        }
      ]
    }
  }
}

参考

参数说明1:“time_zone”: “+08:00”

  • 日期(date)类型的字段在 es中是以 long类型的值保存的。
  • es中默认 默认的时区是 0时区。
  • 如果我们有一个东八区的时间,那么在es中是如何存储的呢?
  • 如果我们此时存在 如下 东八区时间 2022-11-29 12:12:12,那么在 es 会存储为 2022-11-29 12:12:12 +0000 对应的时间戳,为什么会加上+0000,因为我们自己的时间字符串中没有时区,就会加上默认的0时区。
  • 也可用 时差(offset)来表示,使用 offset 参数按指定的正(+)或负(-)偏移量持续时间来更改每个桶的起始值,例如1h表示一个小时,1d表示一天。例如,当使用day作为时间间隔时,每个桶的时间区间从午夜到午夜。 将参数 offset 设置为 +6h 会将每个桶的时间区间更改为从早上6点到早上6点:

参数说明2:fixed_interval 和calendar_interval 区别

  • 原本的interval 字段在7.2中被弃用
  • 日历感知间隔是用参数 calendar_interval 配置的。 日历间隔只能以单位的“单数”数量指定(1d、1M等)。 不支持像 2d这样的倍数,否则会引发异常。

minute (1m)
hour(1h)
day(1d)
week(1w)
month(1m)
quarter( 季度1q)
year(1y)

  • 固定间隔是用参数 fixed_interval 配置的。

与日历感知间隔不同,固定间隔是固定数量的国际单位制(SI)单位,无论它们在日历上的位置如何,都不会偏离。 一秒钟总是由1000毫秒组成。 这允许以支持单位的任何倍数值指定固定间隔。
然而,这意味着固定的间隔不能表达其他单位,如“月(month)”,因为一个月的持续时间不是一个固定的量。 试图指定月或季度(quarter)等日历间隔将引发异常。

固定间隔支持的单位有:
毫秒 (ms)
秒 (s):定义为每个1000毫秒
分钟 (m):所有分钟都从00秒开始。 定义为每个60秒(60,000毫秒)
小时 (h):所有小时都从00分00秒开始。 定义为每60分钟(3,600,000毫秒)
天 (d):所有天都在尽可能早的时间开始,通常是00:00:00(午夜)。 定义为24小时(86,400,000毫秒)

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  #>=
          }
        ]
      }
    }
  }
}

Elasticsearch 指标聚合(metrics)-函数

ES指标聚合,就是类似SQL的统计函数,指标聚合可以单独使用,也可以跟桶聚合一起使用。

常用的统计函数如下:

  • Value Count - 类似sql的count函数,统计总数
  • Cardinality - 类似SQL的count(DISTINCT 字段), 统计不重复的数据总数
  • Avg - 求平均值
  • Sum - 求和
  • Max - 求最大值
  • Min - 求最小值

Value Count - 类似sql的count函数,统计总数

GET /sales/_search?size=0
{
  "aggs": {
    "types_count": { // 聚合查询的名字,随便取个名字
      "value_count": { // 聚合类型为:value_count
        "field": "type" // 计算type这个字段值的总数
      }
    }
  }
}
 
等价SQL:
 
select count(type) from sales

Cardinality - 类似SQL的count(DISTINCT 字段), 统计不重复的数据总数

POST /sales/_search?size=0
{
    "aggs" : {
        "type_count" : { // 聚合查询的名字,随便取一个
            "cardinality" : { // 聚合查询类型为:cardinality
                "field" : "type" // 根据type这个字段统计文档总数
            }
        }
    }
}
 
等价SQL:
 
select count(DISTINCT type) from sales

Avg - 求平均值 Sum - 求和 Max - 求最大值 Min - 求最小值

POST /exams/_search?size=0
{
  "aggs": {
    "avg_grade": { // 聚合查询名字,随便取一个名字
      "avg": { // 聚合查询类型为: avg
        "field": "grade" // 统计grade字段值的平均值
      }
    }
  }
}

综合使用-多层嵌套

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"
              }
            }
          }
        }
      }
    }
  }
}

ES聚合排序

排序是对桶里面数据排序

默认情况,ES会根据doc_count文档总数,降序排序。

ES桶聚合支持两种方式排序:

  • 内置排序
  • 按度量指标排序

1:内置排序

内置排序参数:

  • _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降序
              }
            }
        }
    }
}

2:按度量排序

通常情况下,我们根据桶聚合分桶后,都会对桶内进行多个维度的指标聚合,所以我们也可以根据桶内指标聚合的结果进行排序。

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字段平均值
                }
            }
        }
    }
}

限制返回桶的数量 -size

GET /_search
{
    "aggs" : {
        "products" : { // 聚合查询名字
            "terms" : { // 聚合类型为: terms
                "field" : "product", // 根据product字段分桶
                "size" : 5 // 限制最多返回5个桶
            }
        }
    }
}
  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Elasticsearch 聚合查询(Aggregation)是一种用于对数据进行多维度分析的功能。聚合查询可以用于分析数据的分布情况、计算数据的统计信息、生成图表等。在 Elasticsearch 中,聚合查询是通过使用特定的聚合器(Aggregator)来完成的。 Java 中使用 Elasticsearch 聚合查询需要使用 Elasticsearch Java API。首先需要创建一个 SearchRequest 对象,并设置需要查询的索引和查询条件。然后创建一个 AggregationBuilder 对象,用于定义聚合查询的规则和参数。最后将 AggregationBuilder 对象添加到 SearchRequest 中,执行查询操作。 以下是一个简单的 Java 代码示例,用于查询某个索引下的数据,并按照某个字段进行分组聚合查询: ``` SearchRequest searchRequest = new SearchRequest("index_name"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("group_by_field").field("field_name"); searchSourceBuilder.aggregation(aggregationBuilder); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); Terms terms = searchResponse.getAggregations().get("group_by_field"); for (Terms.Bucket bucket : terms.getBuckets()) { String key = bucket.getKeyAsString(); long count = bucket.getDocCount(); System.out.println("key: " + key + ", count: " + count); } ``` 在上面的代码中,首先创建了一个 SearchRequest 对象,设置需要查询的索引和查询条件。然后创建了一个 TermsAggregationBuilder 对象,用于按照某个字段进行分组聚合查询。最后将 TermsAggregationBuilder 对象添加到 SearchRequest 中,执行查询操作。 查询结果会返回一个 Terms 对象,其中包含了分组聚合查询的结果。可以使用 Terms 对象的 getBuckets() 方法获取分组聚合查询的结果列表。每个分组聚合查询结果由一个 Terms.Bucket 对象表示,其中包含了分组聚合查询的键和文档数量等信息。 以上是简单的聚合查询示例,Elasticsearch 聚合查询功能非常强大,支持多种聚合器和聚合规则,可以根据具体需求进行调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苍煜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值