本文首发于公众号:Hunter后端

原文链接:es笔记七之聚合操作之桶聚合和矩阵聚合

桶(bucket)聚合并不像指标(metric)聚合一样在字段上计算,而是会创建数据的桶,我们可以理解为分组,根据某个字段进行分组,将符合条件的数据分到同一个组里。

桶聚合可以有子聚合,意思就是在分组之后,可以在每个组里再次进行聚合操作,聚合的数据就是每个组的数据。

以下是本篇笔记目录:

  1. 基本桶聚合操作
  2. 过滤聚合
  3. 多桶过滤聚合
  4. 全局聚合
  5. 直方图聚合
  6. 嵌套聚合
  7. 范围聚合
  8. 稀有词聚合
  9. 矩阵聚合

1、基本桶聚合操作

我们可以简单的先来进行一下桶聚合的操作,比如我们根据 age 字段对数据进行分组操作:

GET /bank/_search
{
"size": 0,
"aggs": {
"bucket_age": {
"terms": {
"field": "age",
"size": 20
}
}
}
}

返回的数据如下:

{
...
"aggregations" : {
"bucket_age" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 35,
"buckets" : [
{
"key" : 31,
"doc_count" : 61
},
{
"key" : 39,
"doc_count" : 60
},
{
"key" : 26,
"doc_count" : 59
},
...
]
}
}
}

所有的数据在 aggregations.bucket_age.buckets 下,这是一个数组,key 的内容为 age 的值,doc_count 为该 age 值的数据条数。

其中,bucket_age 为我们定义的桶聚合的名称。

接下来我们介绍桶聚合和指标聚合的其他操作。

2、过滤聚合

如果我们想针对某特定的数据进行聚合,那么就涉及数据的过滤,筛选出特定的数据进行聚合。

比如我们想筛选出 gender 的值为 "F" 的数据,然后对其进行取平均数的操作,我们可以使用 filter 来如下操作:

GET /bank/_search
{
"size": 0,
"aggs": {
"bucket_gender": {
"filter": {"term": {"gender.keyword": "F"}},
"aggs": {
"avg_balance": {"avg": {"field": "balance"}}
}
}
}
}

aggs.bucket_gender 我们使用 filter 对数据进行了一个过滤,筛选出 gender 的值为 "F" 的数据。

注意,在这里,因为我们写入数据前,没有预先定义字段的类型,所以 es 中将其自动转化成 text 属性的字段,所以在查询的时候用到的是 gender.keyword,意思是对 gender 字段的内容作为整体进行筛选。

如果本身是 keyword 属性,就不用加 .keyword 来操作。

与 filter 同级的 aggs,进行针对筛选出的数据进行聚合的操作,这里我们用到的是平均值。

返回的数据如下:

  ...
"aggregations" : {
"bucket_gender" : {
"doc_count" : 493,
"avg_balance" : {
"value" : 25623.34685598377
}
}
}
}

3、多桶过滤聚合

在上一点我们过滤的是单个条件,gender='F' 的情况,如果我们想要实现多个过滤来操作,可以使用 filters,使用方法也不一样。

比如我们想分别对 gender 的值为 F 和 M 的数据进行均值操作,我们可以一步步来操作,我们先来通过 filters 实现两个桶的聚合:

GET /bank/_search
{
"size": 0,
"aggs": {
"bucket_gender": {
"filters": {
"filters": {
"female": {"term": {"gender.keyword": "F"}},
"male": {"term": {"gender.keyword": "M"}}
}
}
}
}
}

返回的数据就是两个桶,包含了两类数据的总数:

  ...
"aggregations" : {
"bucket_gender" : {
"buckets" : {
"female" : {
"doc_count" : 493
},
"male" : {
"doc_count" : 507
}
}
}
}
}

如果想在此基础上接着对其进行均值计算,和前面的 filter 操作一样,在第一个 filters 同级的地方,加上我们的指标聚合操作:

GET /bank/_search
{
"size": 0,
"aggs": {
"bucket_gender": {
"filters": {
"filters": {
"female": {"term": {"gender.keyword": "F"}},
"male": {"term": {"gender.keyword": "M"}}
}
},
"aggs": {
"avg_balance": {"avg": {"field": "balance"}}
}
}
}
}

这样,在返回的桶的数据之内,还包含了一个均值的结果:

  ...
"aggregations" : {
"bucket_gender" : {
"buckets" : {
"female" : {
"doc_count" : 493,
"avg_balance" : {
"value" : 25623.34685598377
}
},
"male" : {
"doc_count" : 507,
"avg_balance" : {
"value" : 25803.800788954635
}
}
}
}
}
}

这里我们因为 gender 只有 F 和 M 两个值,所以没有第三类数据,对于其他数据,比如 age,有很多值,除了某几种特定的值外,我们还想获取剩下的值的信息,如何操作呢?

这里使用到 other_bucket_key 这个参数,比如我们除了定义的 female 和 male,我们还定义一个 non_gender 字段来统计非 M 和 F 的值,我们可以这样操作:

GET /bank/_search
{
"size": 0,
"aggs": {
"bucket_gender": {
"filters": {
"other_bucket_key": "non_gender",
"filters": {
"female": {"term": {"gender.keyword": "F"}},
"male": {"term": {"gender.keyword": "M"}}
}
}
}
}
}

返回的值如下:

  ...
"aggregations" : {
"bucket_gender" : {
"buckets" : {
"female" : {
"doc_count" : 493,
"avg_balance" : {
"value" : 25623.34685598377
}
},
"male" : {
"doc_count" : 507,
"avg_balance" : {
"value" : 25803.800788954635
}
},
"non_gender" : {
"doc_count" : 0,
"avg_balance" : {
"value" : null
}
}
}
}
}
}

4、全局聚合

如果我们要在限定的范围内进行聚合,但是又想在全局范围内获取聚合数据进行比对。

比如说,我们在 gender='F' 的范围进行聚合操作:

GET /bank/_search
{
"size": 0,
"query": {"match": {"gender.keyword": "F"}},
"aggs": {
"female_balance_avg": {
"avg": {
"field": "balance"
}
}
}
}

这里通过 query 操作筛选 gender='F' 的数据,然后对 balance 字段进行聚合,如果同时我们想要获取所有数据的 balance 的平均值,我们可以使用 global 来操作,如下:

GET /bank/_search
{
"size": 0,
"query": {"match": {"gender.keyword": "F"}},
"aggs": {
"total_balance_avg": {
"global": {},
"aggs": {
"avg_balance": {
"avg": {"field": "balance"}
}
}
},
"female_balance_avg": {
"avg": {
"field": "balance"
}
}
}
}

这样就有两个数据来比对,结果如下:

  ...
"aggregations" : {
"female_balance_avg" : {
"value" : 25623.34685598377
},
"total_balance_avg" : {
"doc_count" : 1000,
"avg_balance" : {
"value" : 25714.837
}
}
}
}

5、直方图聚合

这是个类似于直方图的区间桶的聚合操作。

比如对于 age 字段,我们想以 5 为步长进行聚合,如果 age 字段在 20-50 之间,那么返回的数据就会类似于 20-24,25-29,30-34... 以及落在这些区间的数据的数量。

而返回的每条数据并不会是一个区间,而是一个开始的数据,也就是说上面的例子会返回的 key 是 20,25,30 等。

比如我们想对 age 字段进行直方图聚合,步长为 5,用到的聚合的字段为 histogram,示例如下:

GET /bank/_search
{
"size": 0,
"aggs": {
"age_histogram": {
"histogram": {
"field": "age",
"interval": 5
}
}
}
}

在 histogram 聚合字段下,field 字段为我们要进行直方图聚合的字段,这里是 age 字段,interval 字段为进行划分的区间,我们定义为 5。

返回的数据如下:

  ...
"aggregations" : {
"age_histogram" : {
"buckets" : [
{
"key" : 20.0,
"doc_count" : 225
},
{
"key" : 25.0,
"doc_count" : 226
}
...
]
}
}

注意: 如果我们进行聚合的区间,比如说 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则不会被返回,使用示例如下:

GET /bank/_search
{
"size": 0,
"aggs": {
"age_histogram": {
"histogram": {
"field": "age",
"interval": 5,
"min_doc_count": 2
}
}
}
}

返回数据:

  ...
"aggregations" : {
"age_histogram" : {
"buckets" : [
{
"key" : 20.0,
"doc_count" : 5
},
{
"key" : 30.0,
"doc_count" : 2
},
...
]
}
}

指定返回区间

前面介绍的示例中,如果数据在 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 来限定返回数据的最大最小值,示例如下:

GET /bank/_search
{
"size": 0,
"aggs": {
"age_histogram": {
"histogram": {
"field": "age",
"interval": 5,
"extended_bounds": {
"min": 0,
"max": 90
}
}
}
}
}

这样返回的数据的区间就会在 0-90 之间,即便在全量数据的范围之外。

注意: 因为在数据区间之外的数据为 0,想要扩展的区间返回显示,记得要将最小返回计数值 min_doc_count 置为 0。

6、嵌套聚合

嵌套聚合,这里针对的是 es 中数据字段为数组,数组元素里又嵌套为对象的情况,官方文档举了个例子,新建一个 products 的 index,数据结构如下:

PUT /products
{
"mappings": {
"properties" : {
"resellers" : {
"type" : "nested",
"properties" : {
"reseller" : { "type" : "text" },
"price" : { "type" : "double" }
}
}
}
}
}

接下来我们往里添加两条条数据:

PUT /products/_doc/0
{
"name": "LED TV",
"resellers": [
{
"reseller": "companyA",
"price": 350
},
{
"reseller": "companyB",
"price": 500
}
]
} PUT /products/_doc/1
{
"name": "LED TV",
"resellers": [
{
"reseller": "companyA",
"price": 400
},
{
"reseller": "companyB",
"price": 250
}
]
}

然后我们想要在这两条数据里的 resellers 数组字段里的四个元素里获取 price 字段最小值,可以通过 nested.path 来指定 resellers 字段,然后进行聚合,使用示例如下:

GET /products/_search
{
"size": 0,
"query" : {
"match" : { "name" : "led tv" }
},
"aggs" : {
"resellers" : {
"nested" : {
"path" : "resellers"
},
"aggs" : {
"min_price" : { "min" : { "field" : "resellers.price" } }
}
}
}
}

7、范围聚合

范围聚合,即 range 聚合。我们可以通过指定范围来返回各个桶的数据,这个操作和直方图聚合是类似的,不过这个操作更灵活,聚合的范围不会写死。

如果是希望步长固定,我们可以使用直方图聚合,比如0-4,5-9 这种,如果我们直接想要自定义的 0-7,8-19 这种我们想要定义的可以使用范围聚合。

还是使用 age 字段来操作,比如我们想要获取 小于27,28-35,大于36 这个范围,我们可以如下操作:

GET /bank/_search
{
"size": 0,
"aggs": {
"age_range": {
"range": {
"field": "age",
"ranges": [
{"to": 27},
{"from": 27, "to": 35},
{"from": 35}
]
}
}
}
}

需要注意的是,from 的参数是开区间的,比如我们这里 from=27,那么逻辑就是 >27,如果区间两边没有限制,不填写相应的 from 和 to 参数即可,返回的 key 也会是 *-27 这种形式。

上面的命令返回的数据如下:r

  ...
"aggregations" : {
"age_range" : {
"buckets" : [
{
"key" : "*-27.0",
"to" : 27.0,
"doc_count" : 326
},
{
"key" : "27.0-35.0",
"from" : 27.0,
"to" : 35.0,
"doc_count" : 384
},
{
"key" : "35.0-*",
"from" : 35.0,
"doc_count" : 290
}
]
}
}
}

如果想要返回的数据以 key:{} 的形式返回,可以加上 keyed=true 参数:

GET /bank/_search
{
"size": 0,
"aggs": {
"age_range": {
"range": {
"field": "age",
"keyed": true,
"ranges": [
{"to": 27},
{"from": 27, "to": 35},
{"from": 35}
]
}
}
}
}

桶的子指标聚合

在上面的桶聚合操作之后,我们还可以对每个桶进行子指标聚合,比如说最大最小值,平均值,或者统计值等,以下是个操作示例:

GET /bank/_search
{
"size": 0,
"aggs": {
"age_range": {
"range": {
"field": "age",
"ranges": [
{"to": 27},
{"from": 27, "to": 35},
{"from": 35}
]
},
"aggs": {
"age_stats": {
"stats": {
"field": "age"
}
}
}
}
}
}

进行指标聚合的范围是分到每个桶的数据。

8、稀有词聚合

rare terms aggregation,这个的概念大概是这样的,比如我们根据 age 字段进行聚合,统计他们在文档中出现的次数,我们想要获取出现次数最少的几个,或者指定出现次数少于 50 的 age 值,就可以用到这个操作。

接下来我们对 age 字段进行这样的操作,只获取出现次数少于 50 的数据,示例如下:

GET /bank/_search
{
"size": 0,
"aggs": {
"rare_age": {
"rare_terms": {
"field": "age",
"max_doc_count": 50
}
}
}
}

这个的关键字是 rare_terms,rare_age 是我们指定的聚合名称,其下 field 是我们进行聚合字段,在这里是 age 字段,max_doc_count 则是我们指定的出现次数最大的值。

返回的数据会按照 doc_count 正序排列返回,大致如下:

  ...
"aggregations" : {
"rare_age" : {
"buckets" : [
{
"key" : 29,
"doc_count" : 35
},
{
"key" : 27,
"doc_count" : 39
},
{
"key" : 38,
"doc_count" : 39
},
...

范围过滤

我们还可以使用过滤的方式来指定或者排除某些值,这个操作是支持正则的,但经过测试,发现按照官方文档使用正则的 * 来筛选数据并不能真正起作用,所以这里我们介绍使用列表来实现过滤。

比如我们指定的 age 范围是 [29, 27, 24],使用 include:

GET /bank/_search
{
"size": 0,
"aggs": {
"rare_age": {
"rare_terms": {
"field": "age",
"max_doc_count": 51,
"include": [29, 27, 24]
}
}
}
}

如果我们要排除的 age 范围是 [29, 27, 24],使用 exclude:

GET /bank/_search
{
"size": 0,
"aggs": {
"rare_age": {
"rare_terms": {
"field": "age",
"max_doc_count": 51,
"exclude": [29, 27, 24]
}
}
}
}

9、矩阵聚合

矩阵聚合是很小的一部分,这里直接介绍一下。

前面在指标聚合的介绍中,有一个聚合统计汇总,其中介绍了一个参数是 stats,会返回对应字段的最大值、最小值、总数等数据,矩阵聚合 matrix 可以理解成是多个字段的 stats 的集合,会缺少一些统计值,但是返回的值更偏统计学方面的用途。

使用示例如下:

GET /bank/_search
{
"size": 0,
"aggs": {
"field_statis": {
"matrix_stats": {
"fields": ["age", "balance"]
}
}
}
}

返回的数据如下:

  ...
"aggregations" : {
"field_statis" : {
"doc_count" : 1000,
"fields" : [
{
"name" : "balance",
"count" : 1000,
"mean" : 25714.837000000014,
"variance" : 1.9757153733576667E8,
"skewness" : -0.009992486755643138,
"kurtosis" : 1.8088323899074914,
"covariance" : {
"balance" : 1.9757153733576667E8,
"age" : -2845.650777777781
},
"correlation" : {
"balance" : 1.0,
"age" : -0.033676422195874786
}
},
{
"name" : "age",
"count" : 1000,
...
}
...

其中,各参数的释义如下:

count: 总数

mean: 平均值

variance: 方差

skewness: 偏度

kurtosis: 峰度

covariance: 协方差

correlation: 与其他字段的相关性,比如 age 到 age 字段的相关性就是 1.0

如果想获取更多后端相关文章,可扫码关注阅读:

es笔记七之聚合操作之桶聚合和矩阵聚合的更多相关文章

  1. Java学习笔记七(目录操作)

    1.介绍 上一篇博客介绍的是java中经常使用的操作文件的方式,本篇博客着重解说一下,在Java中是怎样来操作目录的.主要是利用的是Java.IO包以下的File类,本篇博客着重解说一下该类的构造函数 ...

  2. Python学习笔记七:文件操作

    文件操作 对照一个word文件的操作方式,来体会文件操作的内容 打开文件:f=open(“file”),提示编码错误,windows默认是GBK f=open(“file”,encoding=”utf ...

  3. MongoDB 聚合操作

    在MongoDB中,有两种方式计算聚合:Pipeline 和 MapReduce.Pipeline查询速度快于MapReduce,但是MapReduce的强大之处在于能够在多台Server上并行执行复 ...

  4. MongoDB 聚合操作(转)

    在MongoDB中,有两种方式计算聚合:Pipeline 和 MapReduce.Pipeline查询速度快于MapReduce,但是MapReduce的强大之处在于能够在多台Server上并行执行复 ...

  5. Java自学-Lambda 聚合操作

    java 集合的聚合操作 步骤 1 : 传统方式与聚合操作方式遍历数据 遍历数据的传统方式就是使用for循环,然后条件判断,最后打印出满足条件的数据 for (Hero h : heros) { if ...

  6. ElasticSearch 学习记录之ES几种常见的聚合操作

    ES几种常见的聚合操作 普通聚合 POST /product/_search { "size": 0, "aggs": { "agg_city&quo ...

  7. elasticsearch聚合操作——本质就是针对搜索后的结果使用桶bucket(允许嵌套)进行group by,统计下分组结果,包括min/max/avg

    分析 Elasticsearch有一个功能叫做聚合(aggregations),它允许你在数据上生成复杂的分析统计.它很像SQL中的GROUP BY但是功能更强大. 举个例子,让我们找到所有职员中最大 ...

  8. Django笔记十六之aggregate聚合操作

    本文首发于微信公众号:Hunter后端 原文链接:Django笔记十六之aggregate聚合操作 这一篇笔记介绍一下关于聚合的操作,aggregate. 常用的聚合操作比如有平均数,总数,最大值,最 ...

  9. elasticsearch 权威指南聚合阅读笔记(七)

    count(1) select clssId,count(1) from student group by  classId { "size":0, "aggs" ...

  10. 白日梦的ES笔记三:万字长文 Elasticsearch基础概念统一扫盲

    目录 一.导读 二.彩蛋福利:账号借用 三.ES的Index.Shard及扩容机制 四.ES支持的核心数据类型 4.1.数字类型 4.2.日期类型 4.3.boolean类型 4.4.二进制类型 4. ...

随机推荐

  1. ISCTF 2022

    Re SigninReverse ida 64 位 打开程序,即可获得flag ISCTF{27413241-9eab-41e2-aca1-88fe8b525956} ezbase # coding= ...

  2. 初学J2V8

    V8和J2V8 V8 V8是Google开源的JavaScript和WebAssembly引擎,被用于Chrome浏览器和Node.js等.和其它JavaScript引擎把JavaScript转换成字 ...

  3. 【LeetCode2180】[Go/C++/C#/Ruby/Swift/Kotlin/Rust/PHP/TS/Racket/Dart/Java/Elixir/Scala/Erlang] 统计各位数字之和为偶数的整数个数

    目录 题解地址 代码 golang c++ C# ruby swift kotlin rust php typescript racket dart java elixir scala erlang ...

  4. find和filter有什么区别

    JavaScript 在 ES6 上有很多数组方法,每种方法都有独特的用途和好处. 在开发应用程序时,大多使用数组方法来获取特定的值列表并获取单个或多个匹配项. 在列出这两种方法的区别之前,我们先来一 ...

  5. IntelliJ IDEA 下载安装及配置使用教程(图文步骤详解)

    前言 壹哥在前面的文章中,带大家下载.安装.配置了Eclipse这个更好用的IDE开发工具,并教会了大家如何在Eclipse中进行项目的创建和代码编写.运行.但是实际上,在各种IDE开发工具中,Ecl ...

  6. JVM 频繁 FULL GC 快速排查整理

    在分享此案例前,先聊聊哪些场景会导致频繁Full GC: 内存泄漏(代码有问题,对象引用没及时释放,导致对象不能及时回收)死循环大对象程序执行了System.gc() 尤其是大对象,80%以上的情况就 ...

  7. 深入理解 Python 虚拟机:字节(bytes)的实现原理及源码剖析

    深入理解 Python 虚拟机:字节(bytes)的实现原理及源码剖析 在本篇文章当中主要给大家介绍在 cpython 内部,bytes 的实现原理.内存布局以及与 bytes 相关的一个比较重要的优 ...

  8. scp上传文件No such file or directory 问题

        问题描述 window使用scp复制文件到linux时报 No such file or directory 错误 解决办法 本地复制到Linux,win环境必须用绝对路径 上传成功

  9. Flask快速入门day02(1、CBV使用及源码分析,2、模板用法,3、请求与响应的基本用法,4、session的使用及源码分析,5、闪现,6、请求扩展)

    目录 Flask框架 一.CBV分析 1.CBV编写视图类方法 二.CBV源码分析 1.CBV源码问题 2.补充问题 3.总结 三.模板 1.py文件 2.html页面 四.请求与响应 1.reque ...

  10. Django笔记十五之in查询及date日期相关过滤操作

    这一篇介绍关于范围,日期的筛选 in range date year week weekday quarter hour 1.in in 对应于 MySQL 中的 in 操作,可以接受数组.元组等类型 ...