es笔记七之聚合操作之桶聚合和矩阵聚合

打印 上一主题 下一主题

主题 918|帖子 918|积分 2754

本文首发于公众号:Hunter后端
原文链接:es笔记七之聚合操作之桶聚合和矩阵聚合
桶(bucket)聚合并不像指标(metric)聚合一样在字段上计算,而是会创建数据的桶,我们可以理解为分组,根据某个字段进行分组,将符合条件的数据分到同一个组里。
桶聚合可以有子聚合,意思就是在分组之后,可以在每个组里再次进行聚合操作,聚合的数据就是每个组的数据。
以下是本篇笔记目录:

  • 基本桶聚合操作
  • 过滤聚合
  • 多桶过滤聚合
  • 全局聚合
  • 直方图聚合
  • 嵌套聚合
  • 范围聚合
  • 稀有词聚合
  • 矩阵聚合
1、基本桶聚合操作

我们可以简单的先来进行一下桶聚合的操作,比如我们根据 age 字段对数据进行分组操作:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "bucket_age": {
  6.       "terms": {
  7.         "field": "age",
  8.         "size": 20
  9.       }
  10.     }
  11.   }
  12. }
复制代码
返回的数据如下:
  1. {
  2.   ...
  3.   "aggregations" : {
  4.     "bucket_age" : {
  5.       "doc_count_error_upper_bound" : 0,
  6.       "sum_other_doc_count" : 35,
  7.       "buckets" : [
  8.         {
  9.           "key" : 31,
  10.           "doc_count" : 61
  11.         },
  12.         {
  13.           "key" : 39,
  14.           "doc_count" : 60
  15.         },
  16.         {
  17.           "key" : 26,
  18.           "doc_count" : 59
  19.         },
  20.         ...
  21.       ]
  22.     }
  23.   }
  24. }     
复制代码
所有的数据在 aggregations.bucket_age.buckets 下,这是一个数组,key 的内容为 age 的值,doc_count 为该 age 值的数据条数。
其中,bucket_age 为我们定义的桶聚合的名称。
接下来我们介绍桶聚合和指标聚合的其他操作。
2、过滤聚合

如果我们想针对某特定的数据进行聚合,那么就涉及数据的过滤,筛选出特定的数据进行聚合。
比如我们想筛选出 gender 的值为 "F" 的数据,然后对其进行取平均数的操作,我们可以使用 filter 来如下操作:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "bucket_gender": {
  6.       "filter": {"term": {"gender.keyword": "F"}},
  7.       "aggs": {
  8.         "avg_balance": {"avg": {"field": "balance"}}
  9.       }
  10.     }
  11.   }
  12. }
复制代码
aggs.bucket_gender 我们使用 filter 对数据进行了一个过滤,筛选出 gender 的值为 "F" 的数据。
注意,在这里,因为我们写入数据前,没有预先定义字段的类型,所以 es 中将其自动转化成 text 属性的字段,所以在查询的时候用到的是 gender.keyword,意思是对 gender 字段的内容作为整体进行筛选。
如果本身是 keyword 属性,就不用加 .keyword 来操作。
与 filter 同级的 aggs,进行针对筛选出的数据进行聚合的操作,这里我们用到的是平均值。
返回的数据如下:
  1.   ...
  2.   "aggregations" : {
  3.     "bucket_gender" : {
  4.       "doc_count" : 493,
  5.       "avg_balance" : {
  6.         "value" : 25623.34685598377
  7.       }
  8.     }
  9.   }
  10. }
复制代码
3、多桶过滤聚合

在上一点我们过滤的是单个条件,gender='F' 的情况,如果我们想要实现多个过滤来操作,可以使用 filters,使用方法也不一样。
比如我们想分别对 gender 的值为 F 和 M 的数据进行均值操作,我们可以一步步来操作,我们先来通过 filters 实现两个桶的聚合:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "bucket_gender": {
  6.       "filters": {
  7.         "filters": {
  8.           "female": {"term": {"gender.keyword": "F"}},
  9.           "male": {"term": {"gender.keyword": "M"}}
  10.         }
  11.       }
  12.     }
  13.   }
  14. }
复制代码
返回的数据就是两个桶,包含了两类数据的总数:
  1.   ...
  2.   "aggregations" : {
  3.     "bucket_gender" : {
  4.       "buckets" : {
  5.         "female" : {
  6.           "doc_count" : 493
  7.         },
  8.         "male" : {
  9.           "doc_count" : 507
  10.         }
  11.       }
  12.     }
  13.   }
  14. }
复制代码
如果想在此基础上接着对其进行均值计算,和前面的 filter 操作一样,在第一个 filters 同级的地方,加上我们的指标聚合操作:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "bucket_gender": {
  6.       "filters": {
  7.         "filters": {
  8.           "female": {"term": {"gender.keyword": "F"}},
  9.           "male": {"term": {"gender.keyword": "M"}}
  10.         }
  11.       },
  12.       "aggs": {
  13.         "avg_balance": {"avg": {"field": "balance"}}
  14.       }
  15.     }
  16.   }
  17. }
复制代码
这样,在返回的桶的数据之内,还包含了一个均值的结果:
  1.   ...
  2.   "aggregations" : {
  3.     "bucket_gender" : {
  4.       "buckets" : {
  5.         "female" : {
  6.           "doc_count" : 493,
  7.           "avg_balance" : {
  8.             "value" : 25623.34685598377
  9.           }
  10.         },
  11.         "male" : {
  12.           "doc_count" : 507,
  13.           "avg_balance" : {
  14.             "value" : 25803.800788954635
  15.           }
  16.         }
  17.       }
  18.     }
  19.   }
  20. }
复制代码
这里我们因为 gender 只有 F 和 M 两个值,所以没有第三类数据,对于其他数据,比如 age,有很多值,除了某几种特定的值外,我们还想获取剩下的值的信息,如何操作呢?
这里使用到 other_bucket_key 这个参数,比如我们除了定义的 female 和 male,我们还定义一个 non_gender 字段来统计非 M 和 F 的值,我们可以这样操作:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "bucket_gender": {
  6.       "filters": {
  7.         "other_bucket_key": "non_gender",
  8.         "filters": {
  9.           "female": {"term": {"gender.keyword": "F"}},
  10.           "male": {"term": {"gender.keyword": "M"}}
  11.         }
  12.       }
  13.     }
  14.   }
  15. }
复制代码
返回的值如下:
  1.   ...
  2.   "aggregations" : {
  3.     "bucket_gender" : {
  4.       "buckets" : {
  5.         "female" : {
  6.           "doc_count" : 493,
  7.           "avg_balance" : {
  8.             "value" : 25623.34685598377
  9.           }
  10.         },
  11.         "male" : {
  12.           "doc_count" : 507,
  13.           "avg_balance" : {
  14.             "value" : 25803.800788954635
  15.           }
  16.         },
  17.         "non_gender" : {
  18.           "doc_count" : 0,
  19.           "avg_balance" : {
  20.             "value" : null
  21.           }
  22.         }
  23.       }
  24.     }
  25.   }
  26. }
复制代码
4、全局聚合

如果我们要在限定的范围内进行聚合,但是又想在全局范围内获取聚合数据进行比对。
比如说,我们在 gender='F' 的范围进行聚合操作:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "query": {"match": {"gender.keyword": "F"}},
  5.   "aggs": {
  6.     "female_balance_avg": {
  7.       "avg": {
  8.         "field": "balance"
  9.       }
  10.     }
  11.   }
  12. }
复制代码
这里通过 query 操作筛选 gender='F' 的数据,然后对 balance 字段进行聚合,如果同时我们想要获取所有数据的 balance 的平均值,我们可以使用 global 来操作,如下:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "query": {"match": {"gender.keyword": "F"}},
  5.   "aggs": {
  6.     "total_balance_avg": {
  7.       "global": {},
  8.       "aggs": {
  9.         "avg_balance": {
  10.           "avg": {"field": "balance"}
  11.         }
  12.       }
  13.     },
  14.     "female_balance_avg": {
  15.       "avg": {
  16.         "field": "balance"
  17.       }
  18.     }
  19.   }
  20. }
复制代码
这样就有两个数据来比对,结果如下:
  1.   ...
  2.   "aggregations" : {
  3.     "female_balance_avg" : {
  4.       "value" : 25623.34685598377
  5.     },
  6.     "total_balance_avg" : {
  7.       "doc_count" : 1000,
  8.       "avg_balance" : {
  9.         "value" : 25714.837
  10.       }
  11.     }
  12.   }
  13. }
复制代码
5、直方图聚合

这是个类似于直方图的区间桶的聚合操作。
比如对于 age 字段,我们想以 5 为步长进行聚合,如果 age 字段在 20-50 之间,那么返回的数据就会类似于 20-24,25-29,30-34... 以及落在这些区间的数据的数量。
而返回的每条数据并不会是一个区间,而是一个开始的数据,也就是说上面的例子会返回的 key 是 20,25,30 等。
比如我们想对 age 字段进行直方图聚合,步长为 5,用到的聚合的字段为 histogram,示例如下:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "age_histogram": {
  6.       "histogram": {
  7.         "field": "age",
  8.         "interval": 5
  9.       }
  10.     }
  11.   }
  12. }
复制代码
在 histogram 聚合字段下,field 字段为我们要进行直方图聚合的字段,这里是 age 字段,interval 字段为进行划分的区间,我们定义为 5。
返回的数据如下:
  1.   ...
  2.   "aggregations" : {
  3.     "age_histogram" : {
  4.       "buckets" : [
  5.         {
  6.           "key" : 20.0,
  7.           "doc_count" : 225
  8.         },
  9.         {
  10.           "key" : 25.0,
  11.           "doc_count" : 226
  12.         }
  13.         ...
  14.     ]
  15.   }
  16. }   
复制代码
注意: 如果我们进行聚合的区间,比如说 25-29 之间聚合的数据是 0,那么 es 还是会返回这个区间,不过 doc_count 是 0,不会存在不返回这个区间 key 的情况。
最小 count 返回数据

前面我们说了就算区间 count 数是0,这个区间也会返回,但同时我们也可以规定 min_doc_count 这个参数来返回只有当区间 count 数大于等于这个值的时候才返回数据。
假设 age 的区间数据如下:
20-24:5
25-29:0
30-34:2
...
如果我们设置 min_doc_count=2,那么返回的区间 25-29则不会被返回,使用示例如下:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "age_histogram": {
  6.       "histogram": {
  7.         "field": "age",
  8.         "interval": 5,
  9.         "min_doc_count": 2
  10.       }
  11.     }
  12.   }
  13. }
复制代码
返回数据:
  1.   ...
  2.   "aggregations" : {
  3.     "age_histogram" : {
  4.       "buckets" : [
  5.         {
  6.           "key" : 20.0,
  7.           "doc_count" : 5
  8.         },
  9.         {
  10.           "key" : 30.0,
  11.           "doc_count" : 2
  12.         },
  13.         ...
  14.      ]
  15.    }
  16. }
复制代码
指定返回区间

前面介绍的示例中,如果数据在 20-50 之间,那么返回的区间数据就从 20 开始计数(具体的 key 会根据 interval 的设置不一样,比如设置 Interval=5,key 就会是 20, 25, 30...,如果是设置 Interval=3,那么 key 就会是 18, 21, 24...)。
如果我们想从 0 开始计数,即便是 0-20 之间的计数为 0,也想要返回20之前 0-4,5-9 的数,或者想要返回 50 之后的数据,包括 50-54,55-59 这种,我们可以使用extended_bounds.minextended_bounds.max 来限定返回数据的最大最小值,示例如下:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "age_histogram": {
  6.       "histogram": {
  7.         "field": "age",
  8.         "interval": 5,
  9.         "extended_bounds": {
  10.           "min": 0,
  11.           "max": 90
  12.         }
  13.       }
  14.     }
  15.   }
  16. }
复制代码
这样返回的数据的区间就会在 0-90 之间,即便在全量数据的范围之外。
注意: 因为在数据区间之外的数据为 0,想要扩展的区间返回显示,记得要将最小返回计数值 min_doc_count 置为 0。
6、嵌套聚合

嵌套聚合,这里针对的是 es 中数据字段为数组,数组元素里又嵌套为对象的情况,官方文档举了个例子,新建一个 products 的 index,数据结构如下:
  1. PUT /products
  2. {
  3.     "mappings": {
  4.         "properties" : {
  5.             "resellers" : {
  6.                 "type" : "nested",
  7.                 "properties" : {
  8.                     "reseller" : { "type" : "text" },
  9.                     "price" : { "type" : "double" }
  10.                 }
  11.             }
  12.         }
  13.     }
  14. }
复制代码
接下来我们往里添加两条条数据:
  1. PUT /products/_doc/0
  2. {
  3.   "name": "LED TV",
  4.   "resellers": [
  5.     {
  6.       "reseller": "companyA",
  7.       "price": 350
  8.     },
  9.     {
  10.       "reseller": "companyB",
  11.       "price": 500
  12.     }
  13.   ]
  14. }
  15. PUT /products/_doc/1
  16. {
  17.   "name": "LED TV",
  18.   "resellers": [
  19.     {
  20.       "reseller": "companyA",
  21.       "price": 400
  22.     },
  23.     {
  24.       "reseller": "companyB",
  25.       "price": 250
  26.     }
  27.   ]
  28. }
复制代码
然后我们想要在这两条数据里的 resellers 数组字段里的四个元素里获取 price 字段最小值,可以通过 nested.path 来指定 resellers 字段,然后进行聚合,使用示例如下:
  1. GET /products/_search
  2. {
  3.     "size": 0,
  4.     "query" : {
  5.         "match" : { "name" : "led tv" }
  6.     },
  7.     "aggs" : {
  8.         "resellers" : {
  9.             "nested" : {
  10.                 "path" : "resellers"
  11.             },
  12.             "aggs" : {
  13.                 "min_price" : { "min" : { "field" : "resellers.price" } }
  14.             }
  15.         }
  16.     }
  17. }
复制代码
7、范围聚合

范围聚合,即 range 聚合。我们可以通过指定范围来返回各个桶的数据,这个操作和直方图聚合是类似的,不过这个操作更灵活,聚合的范围不会写死。
如果是希望步长固定,我们可以使用直方图聚合,比如0-4,5-9 这种,如果我们直接想要自定义的 0-7,8-19 这种我们想要定义的可以使用范围聚合。
还是使用 age 字段来操作,比如我们想要获取 小于27,28-35,大于36 这个范围,我们可以如下操作:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "age_range": {
  6.       "range": {
  7.         "field": "age",
  8.         "ranges": [
  9.           {"to": 27},
  10.           {"from": 27, "to": 35},
  11.           {"from": 35}
  12.         ]
  13.       }
  14.     }
  15.   }
  16. }
复制代码
需要注意的是,from 的参数是开区间的,比如我们这里 from=27,那么逻辑就是 >27,如果区间两边没有限制,不填写相应的 from 和 to 参数即可,返回的 key 也会是 *-27 这种形式。
上面的命令返回的数据如下:r
  1.   ...
  2.   "aggregations" : {
  3.     "age_range" : {
  4.       "buckets" : [
  5.         {
  6.           "key" : "*-27.0",
  7.           "to" : 27.0,
  8.           "doc_count" : 326
  9.         },
  10.         {
  11.           "key" : "27.0-35.0",
  12.           "from" : 27.0,
  13.           "to" : 35.0,
  14.           "doc_count" : 384
  15.         },
  16.         {
  17.           "key" : "35.0-*",
  18.           "from" : 35.0,
  19.           "doc_count" : 290
  20.         }
  21.       ]
  22.     }
  23.   }
  24. }
复制代码
如果想要返回的数据以 key:{} 的形式返回,可以加上 keyed=true 参数:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "age_range": {
  6.       "range": {
  7.         "field": "age",
  8.         "keyed": true,
  9.         "ranges": [
  10.           {"to": 27},
  11.           {"from": 27, "to": 35},
  12.           {"from": 35}
  13.         ]
  14.       }
  15.     }
  16.   }
  17. }
复制代码
桶的子指标聚合

在上面的桶聚合操作之后,我们还可以对每个桶进行子指标聚合,比如说最大最小值,平均值,或者统计值等,以下是个操作示例:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "age_range": {
  6.       "range": {
  7.         "field": "age",
  8.         "ranges": [
  9.           {"to": 27},
  10.           {"from": 27, "to": 35},
  11.           {"from": 35}
  12.         ]
  13.       },
  14.       "aggs": {
  15.         "age_stats": {
  16.           "stats": {
  17.             "field": "age"
  18.           }
  19.         }
  20.       }
  21.     }
  22.   }
  23. }
复制代码
进行指标聚合的范围是分到每个桶的数据。
8、稀有词聚合

rare terms aggregation,这个的概念大概是这样的,比如我们根据 age 字段进行聚合,统计他们在文档中出现的次数,我们想要获取出现次数最少的几个,或者指定出现次数少于 50 的 age 值,就可以用到这个操作。
接下来我们对 age 字段进行这样的操作,只获取出现次数少于 50 的数据,示例如下:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "rare_age": {
  6.       "rare_terms": {
  7.         "field": "age",
  8.         "max_doc_count": 50
  9.       }
  10.     }
  11.   }
  12. }
复制代码
这个的关键字是 rare_terms,rare_age 是我们指定的聚合名称,其下 field 是我们进行聚合字段,在这里是 age 字段,max_doc_count 则是我们指定的出现次数最大的值。
返回的数据会按照 doc_count 正序排列返回,大致如下:
  1.   ...
  2.   "aggregations" : {
  3.     "rare_age" : {
  4.       "buckets" : [
  5.         {
  6.           "key" : 29,
  7.           "doc_count" : 35
  8.         },
  9.         {
  10.           "key" : 27,
  11.           "doc_count" : 39
  12.         },
  13.         {
  14.           "key" : 38,
  15.           "doc_count" : 39
  16.         },
  17.         ...
复制代码
范围过滤

我们还可以使用过滤的方式来指定或者排除某些值,这个操作是支持正则的,但经过测试,发现按照官方文档使用正则的 * 来筛选数据并不能真正起作用,所以这里我们介绍使用列表来实现过滤。
比如我们指定的 age 范围是 [29, 27, 24],使用 include:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "rare_age": {
  6.       "rare_terms": {
  7.         "field": "age",
  8.         "max_doc_count": 51,
  9.         "include": [29, 27, 24]
  10.       }
  11.     }
  12.   }
  13. }
复制代码
如果我们要排除的 age 范围是 [29, 27, 24],使用 exclude:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "rare_age": {
  6.       "rare_terms": {
  7.         "field": "age",
  8.         "max_doc_count": 51,
  9.         "exclude": [29, 27, 24]
  10.       }
  11.     }
  12.   }
  13. }
复制代码
9、矩阵聚合

矩阵聚合是很小的一部分,这里直接介绍一下。
前面在指标聚合的介绍中,有一个聚合统计汇总,其中介绍了一个参数是 stats,会返回对应字段的最大值、最小值、总数等数据,矩阵聚合 matrix 可以理解成是多个字段的 stats 的集合,会缺少一些统计值,但是返回的值更偏统计学方面的用途。
使用示例如下:
  1. GET /bank/_search
  2. {
  3.   "size": 0,
  4.   "aggs": {
  5.     "field_statis": {
  6.       "matrix_stats": {
  7.         "fields": ["age", "balance"]
  8.       }
  9.     }
  10.   }
  11. }
复制代码
返回的数据如下:
  1.   ...
  2.   "aggregations" : {
  3.     "field_statis" : {
  4.       "doc_count" : 1000,
  5.       "fields" : [
  6.         {
  7.           "name" : "balance",
  8.           "count" : 1000,
  9.           "mean" : 25714.837000000014,
  10.           "variance" : 1.9757153733576667E8,
  11.           "skewness" : -0.009992486755643138,
  12.           "kurtosis" : 1.8088323899074914,
  13.           "covariance" : {
  14.             "balance" : 1.9757153733576667E8,
  15.             "age" : -2845.650777777781
  16.           },
  17.           "correlation" : {
  18.             "balance" : 1.0,
  19.             "age" : -0.033676422195874786
  20.           }
  21.         },
  22.         {
  23.           "name" : "age",
  24.           "count" : 1000,
  25.           ...
  26.         }
  27.     ...
复制代码
其中,各参数的释义如下:
count: 总数
mean: 平均值
variance: 方差
skewness: 偏度
kurtosis: 峰度
covariance: 协方差
correlation: 与其他字段的相关性,比如 age 到 age 字段的相关性就是 1.0
如果想获取更多后端相关文章,可扫码关注阅读:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

杀鸡焉用牛刀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表