本文首发于公众号: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. 4种API性能恶化根因分析

    摘要:服务发生性能恶化时,需要投入大量人力分析性能异常根因,分析成本高,耗时长.我们提出了一种先在异常调用链内部分析候选根因,再在全局拓扑环境下对候选根因进行汇聚的二级分析方法,克服了调用链之间异常相 ...

  2. Linux配置为代理服务器,代理微信

    1.安装squid(全程使用root账号,如使用其他请注意权限问题) yum install squid 2.生成代理的账号密码 #如果没有htpasswd命令 先安装sudo yum install ...

  3. 关于Cookie要懂的知识

    ☞演示 什么是Cookie? http是一个无状态协议,它不对之前发生过的请求和响应的状态进行管理.这样就可能导致,我们登陆一个网站后,每次跳转新页面,之前的登陆状态都不能被记住,要重新登陆等问题. ...

  4. [Java]变量及其初始化 与 类对象的初始化

    1 变量 1.1 变量的[定义] 1.2 变量的[作用域] 1.3 变量的[初始值] 1.4 补充:缓存变量 1.5 变量的[分类]与[未初始化情况] 2 类对象 2.1 类对象的初始化/构造过程 1 ...

  5. 高可用(keepalived)部署方案

    前言:为了减少三维数据中心可视化管理系统的停工时间,保持其服务的高度可用性.同时部署多套同样的三维可视化系统,让三维数据中心可视化系统同时部署并运行到多个服务器上.同时提供一个虚拟IP,然后外面通过这 ...

  6. Windows下搭建java环境最新版本jdk运行jar文件

    1:安装JDK(Java Development Kit),链接https://www.oracle.com/java/technologies/downloads/#jdk18-windows下载最 ...

  7. 全网最详细中英文ChatGPT-GPT-4示例文档-人工智能助手从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/curl命令源代码,小白也能学)

    目录 Introduce 简介 setting 设置 Prompt 提示 Sample response 回复样本 API request 接口请求 python接口请求示例 node.js接口请求示 ...

  8. Mybatis 框架下 SQL 注入攻击的方式

    前言 SQL注入漏洞作为WEB安全的最常见的漏洞之一,在java中随着预编译与各种ORM框架的使用,注入问题也越来越少. 新手代码审计者往往对Java Web应用的多个框架组合而心生畏惧,不知如何下手 ...

  9. RDIFramework.NET WinForm版新增通知公告、系统新闻模块

    1.系统新闻功能描述 系统新闻模块,用户可以根据实际情况做相应应用,如用在内部业务系统的展示中或网站上新闻的展示.新闻可以分类进行管理,非常的实用.系统新闻管理主要分为添加.修改.删除与移动系统新闻. ...

  10. 实例讲解Playwright(一)

    实例讲解Playwright(一) 网址 说明 https://playwright.dev/ 官网首页 https://playwright.dev/python/docs/intro Python ...