Elasticsearch-桶聚合查询详解

Elasticsearch-桶聚合查询详解在之前我们详细面熟了 es 的查询用法 但是 es 还拥有强大的聚合查询功能 可以得到类似分组 直方图 折线图等数据组合

大家好,欢迎来到IT知识分享网。

前言

概念

1:ES聚合查询流程

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

  • 分组

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

  • 组内聚合

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

2:桶的概念

提示:桶等同于组,分桶和分组是一个意思,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 } ] } } } 

java代码

TermsAggregationBuilder termAggBuilder = AggregationBuilders.terms("buket_name").field("analysis.sensitive_words").size(100).minDocCount(0); searchSourceBuilder.aggregation(termAggBuilder); ~~~~~~~~ try { 
    searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); } catch (IOException e) { 
    throw new RuntimeException(e); } Aggregations aggregations = searchResponse.getAggregations(); if(aggregations!= null){ 
    Terms terms = aggregations.get("buket_name"); List<? extends Terms.Bucket> buckets = terms.getBuckets(); JSONObject reslut = new JSONObject(true); for (Terms.Bucket bucket : buckets) { 
    //分组的key String key = bucket.getKeyAsString(); long docCount = bucket.getDocCount(); reslut.put(key,docCount); } return AjaxResult.success(reslut); }else { 
    return AjaxResult.error("查询无数据"); } 

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" : 00, "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这样的倍数,否则会引发异常。
  • 固定间隔是用参数 fixed_interval 配置的。

java代码

 DateHistogramAggregationBuilder dateHisAggBuilder = AggregationBuilders.dateHistogram("trendWeek") .field("data_time") .fixedInterval(DateHistogramInterval.DAY)//设置多久一个点 .minDocCount(0);//设置查询最小文档数为0,为了占位使用 searchSourceBuilder.aggregation(dateHisAggBuilder); ~~~~~ try { 
    searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); } catch (IOException e) { 
    throw new RuntimeException(e); } Aggregations aggregations = searchResponse.getAggregations(); if(aggregations!= null){ 
    Histogram histogram = aggregations.get("trendWeek"); List<? extends Histogram.Bucket> buckets = histogram.getBuckets(); JSONObject reslut = new JSONObject(true); for (Histogram.Bucket bucket : buckets) { 
    //分组的key String key = bucket.getKeyAsString(); long docCount = bucket.getDocCount(); reslut.put(key,docCount); } return AjaxResult.success(reslut); }else { 
    return AjaxResult.error("查询无数据"); } 

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

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/110035.html

(0)
上一篇 2026-02-03 09:27
下一篇 2026-02-03 09:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信