Elasticsearch 聚合

Elasticsearch 聚合

聚合将数据汇总为指标、统计数据或其他分析。

Elasticsearch 将聚合分为三类:

类型 说明
Metric(指标聚合) 根据字段值进行统计计算
Bucket(桶聚合) 根据字段值、范围或其他条件进行分组
Pipeline(管道聚合) 根据其他聚合结果进行聚合

聚合的用法

所有的聚合,无论它们是什么类型,都遵从以下的规则。

  • 通过 JSON 来定义聚合计算,使用 aggregationsaggs 来标记聚合计算。需要给每个聚合起一个名字,指定它的类型以及和该类型相关的选项。
  • 它们运行在查询的结果之上。和查询不匹配的文档不会计算在内,除非你使用 global 聚集将不匹配的文档囊括其中。
  • 可以进一步过滤查询的结果,而不影响聚集。

以下是聚合的基本结构:

1
2
3
4
5
6
7
8
9
10
"aggregations" : { <!-- 最外层的聚合键,也可以缩写为 aggs -->
"<aggregation_name>" : { <!-- 聚合的自定义名字 -->
"<aggregation_type>" : { <!-- 聚合的类型,指标相关的,如 max、min、avg、sum,桶相关的 terms、filter 等 -->
<aggregation_body> <!-- 聚合体:对哪些字段进行聚合,可以取字段的值,也可以是脚本计算的结果 -->
}
[,"meta" : { [<meta_data_body>] } ]? <!-- 元 -->
[,"aggregations" : { [<sub_aggregation>]+ } ]? <!-- 在聚合里面在定义子聚合 -->
}
[,"<aggregation_name_2>" : { ... } ]* <!-- 聚合的自定义名字 2 -->
}
  • 在最上层有一个 aggregations 的键,可以缩写为 aggs
  • 在下面一层,需要为聚合指定一个名字。可以在请求的返回中看到这个名字。在同一个请求中使用多个聚合时,这一点非常有用,它让你可以很容易地理解每组结果的含义。
  • 最后,必须要指定聚合的类型。

关于聚合分析的值来源,可以取字段的值,也可以是脚本计算的结果

但是用脚本计算的结果时,需要注意脚本的性能和安全性;尽管多数聚集类型允许使用脚本,但是脚本使得聚集变得缓慢,因为脚本必须在每篇文档上运行。为了避免脚本的运行,可以在索引阶段进行计算。

此外,脚本也可以被人可能利用进行恶意代码攻击,尽量使用沙盒(sandbox)内的脚本语言。

::: details 【示例】根据 my-field 字段进行 terms 聚合计算

1
2
3
4
5
6
7
8
9
10
GET /my-index-000001/_search
{
"aggs": {
"my-agg-name": {
"terms": {
"field": "my-field"
}
}
}
}

响应结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"took": 78,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 5,
"relation": "eq"
},
"max_score": 1.0,
"hits": [...]
},
"aggregations": {
"my-agg-name": { // my-agg-name 聚合计算的结果
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": []
}
}
}

:::

::: details 用于测试的数据

/employees 索引添加测试数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
DELETE /employees
PUT /employees/
{
"mappings" : {
"properties" : {
"age" : {
"type" : "integer"
},
"gender" : {
"type" : "keyword"
},
"job" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 50
}
}
},
"name" : {
"type" : "keyword"
},
"salary" : {
"type" : "integer"
}
}
}
}

PUT /employees/_bulk
{"index":{"_id":"1"}}
{"name":"Emma","age":32,"job":"Product Manager","gender":"female","salary":35000}
{"index":{"_id":"2"}}
{"name":"Underwood","age":41,"job":"Dev Manager","gender":"male","salary":50000}
{"index":{"_id":"3"}}
{"name":"Tran","age":25,"job":"Web Designer","gender":"male","salary":18000}
{"index":{"_id":"4"}}
{"name":"Rivera","age":26,"job":"Web Designer","gender":"female","salary":22000}
{"index":{"_id":"5"}}
{"name":"Rose","age":25,"job":"QA","gender":"female","salary":18000}
{"index":{"_id":"6"}}
{"name":"Lucy","age":31,"job":"QA","gender":"female","salary":25000}
{"index":{"_id":"7"}}
{"name":"Byrd","age":27,"job":"QA","gender":"male","salary":20000}
{"index":{"_id":"8"}}
{"name":"Foster","age":27,"job":"Java Programmer","gender":"male","salary":20000}
{"index":{"_id":"9"}}
{"name":"Gregory","age":32,"job":"Java Programmer","gender":"male","salary":22000}
{"index":{"_id":"10"}}
{"name":"Bryant","age":20,"job":"Java Programmer","gender":"male","salary":9000}
{"index":{"_id":"11"}}
{"name":"Jenny","age":36,"job":"Java Programmer","gender":"female","salary":38000}
{"index":{"_id":"12"}}
{"name":"Mcdonald","age":31,"job":"Java Programmer","gender":"male","salary":32000}
{"index":{"_id":"13"}}
{"name":"Jonthna","age":30,"job":"Java Programmer","gender":"female","salary":30000}
{"index":{"_id":"14"}}
{"name":"Marshall","age":32,"job":"Javascript Programmer","gender":"male","salary":25000}
{"index":{"_id":"15"}}
{"name":"King","age":33,"job":"Java Programmer","gender":"male","salary":28000}
{"index":{"_id":"16"}}
{"name":"Mccarthy","age":21,"job":"Javascript Programmer","gender":"male","salary":16000}
{"index":{"_id":"17"}}
{"name":"Goodwin","age":25,"job":"Javascript Programmer","gender":"male","salary":16000}
{"index":{"_id":"18"}}
{"name":"Catherine","age":29,"job":"Javascript Programmer","gender":"female","salary":20000}
{"index":{"_id":"19"}}
{"name":"Boone","age":30,"job":"DBA","gender":"male","salary":30000}
{"index":{"_id":"20"}}
{"name":"Kathy","age":29,"job":"DBA","gender":"female","salary":20000}

:::

限定数据范围

ES 聚合分析的默认作用范围是 query 的查询结果集。

同时 ES 还支持以下方式改变聚合的作用范围:

  • filter - 只对当前子聚合语句生效
  • post_filter - 对聚合分析后的文档进行再次过滤
  • global - 无视 query,对全部文档进行统计

::: details 【示例】使用 query 限定聚合数据的范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /employees/_search
{
"size": 0,
"query": {
"range": {
"age": {
"gte": 20
}
}
},
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword"

}
}
}
}

:::

::: details 【示例】使用 filter 限定聚合数据的范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
POST /employees/_search
{
"size": 0,
"aggs": {
"older_person": {
"filter":{
"range":{
"age":{
"from":35
}
}
},
"aggs":{
"jobs":{
"terms": {
"field":"job.keyword"
}
}},
"all_jobs": {
"terms": {
"field":"job.keyword"

}
}
}
}

:::

控制返回聚合结果

::: details 【示例】仅返回聚合结果

使用 field 限定聚合返回的展示字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
POST /employees/_search
{
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword"
}
}
}
}

// 找出所有的 job 类型,还能找到聚合后符合条件的结果
POST /employees/_search
{
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword"
}
}
},
"post_filter": {
"match": {
"job.keyword": "Dev Manager"
}
}
}

默认情况下,包含聚合的搜索会同时返回搜索命中和聚合结果。要仅返回聚合结果,请将 size 设置为 0

1
2
3
4
5
6
7
8
9
10
11
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword"
}
}
}
}

:::

聚合结果排序

::: details 【示例】聚合结果排序

指定 order,按照 _count_key 进行排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
POST /employees/_search
{
"size": 0,
"query": {
"range": {
"age": {
"gte": 20
}
}
},
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"order": [
{
"_count": "asc"
},
{
"_key": "desc"
}
]
}
}
}
}

POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"order": [
{
"avg_salary": "desc"
}
]
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
}
}
}

POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"order": [
{
"stats_salary.min": "desc"
}
]
},
"aggs": {
"stats_salary": {
"stats": {
"field": "salary"
}
}
}
}
}
}

:::

运行多个聚合

::: details 【示例】运行多个聚合

可以在同一请求中指定多个聚合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
POST /employees/_search
{
"size": 0,
"query": {
"range": {
"age": {
"gte": 40
}
}
},
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword"

}
},

"all":{
"global":{},
"aggs":{
"salary_avg":{
"avg":{
"field":"salary"
}
}
}
}
}
}

:::

运行子聚合

::: details 【示例】运行子聚合

Bucket 聚合支持 Bucket 或 Metric 子聚合。例如,具有 avg 子聚合的 terms 聚合会计算每个桶中文档的平均值。嵌套子聚合没有级别或深度限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
},
"min_salary_by_job":{
"min_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}

响应将子聚合结果嵌套在其父聚合下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
// ...
"aggregations": {
"jobs": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 6,
"buckets": [
{
"key": "Java Programmer",
"doc_count": 7,
"avg_salary": {
"value": 25571.428571428572
}
},
{
"key": "Javascript Programmer",
"doc_count": 4,
"avg_salary": {
"value": 19250.0
}
},
{
"key": "QA",
"doc_count": 3,
"avg_salary": {
"value": 21000.0
}
}
]
},
"min_salary_by_job": {
"value": 19250.0,
"keys": ["Javascript Programmer"]
}
}
}

:::

Metric(指标聚合)

指标聚合主要从不同文档的分组中提取统计数据,或者,从来自其他聚合的文档桶来提取统计数据。

这些统计数据通常来自数值型字段,如最小或者平均价格。用户可以单独获取每项统计数据,或者也可以使用 stats 聚合来同时获取它们。更高级的统计数据,如平方和或者是标准差,可以通过 extended stats 聚合来获取。

ES 支持的指标聚合类型:

类型 说明
avg 平均值聚合
boxplot 箱线图聚合
cardinality 近似计算非重复值
extended_stats 扩展统计聚合
geo_bounds 地理边界聚合
geo_centroid 根据* geo *字段的所有坐标值计算加权质心
geo_line_geo_line 根据地理数据生成可用于线性几何图形展示的数据
cartesian_bounds 笛卡尔积边界聚合
cartesian_centroid 计算所有坐标值加权质心
matrix_stats 矩阵统计聚合
max 最大值聚合
median_absolute_deviation 中位数绝对偏差聚合
min 最小值聚合
percentile_ranks 百分位排名聚合
percentiles 百分位聚合
rate 频率聚合
scripted_metric 脚本化指标聚合
stats 统计聚合
string_stats 字符串统计聚合
sum 求和聚合
t_test 校验聚合
top_hits 热门点击统计
top_metrics 热门指标聚合
value_count 值统计聚合
weighted_avg 加权平均值聚合

::: details 【示例】指标聚合示例

Metric 聚合测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Metric 聚合,找到最低的工资
POST /employees/_search
{
"size": 0,
"aggs": {
"min_salary": {
"min": {
"field":"salary"
}
}
}
}

// Metric 聚合,找到最高的工资
POST /employees/_search
{
"size": 0,
"aggs": {
"max_salary": {
"max": {
"field":"salary"
}
}
}
}

// 多个 Metric 聚合,找到最低最高和平均工资
POST /employees/_search
{
"size": 0,
"aggs": {
"max_salary": {
"max": {
"field": "salary"
}
},
"min_salary": {
"min": {
"field": "salary"
}
},
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
}

// 一个聚合,输出多值
POST /employees/_search
{
"size": 0,
"aggs": {
"stats_salary": {
"stats": {
"field": "salary"
}
}
}
}

:::

Bucket(桶聚合)

桶聚合不会像指标聚合那样计算字段的指标,而是创建文档桶。每个桶都与一个标准(取决于聚合类型)相关联,该标准确定当前上下文中的文档是否“落入”其中。换句话说,桶有效地定义了文档集。除了桶本身之外,聚合还计算并返回“落入”每个桶的文档数。

指标聚合相反,桶聚合可以保存子聚合。这些子聚合将针对其 “父” 桶聚合创建的桶进行聚合。

Elasticsearch 中支持的桶聚合类型:

类型 说明
adjacency_matrix 邻接矩阵聚合
auto_interval_date_histogram 自动间隔日期直方图聚合
categorize_text 对文本进行分类聚合
children 子文档聚合
composite 组合聚合
date_histogram 日期直方图聚合
date_range 日期范围聚合
diversified_sampler 多种采样器聚合
filter 过滤器聚合
filters 多过滤器聚合
geo_distance 地理距离聚合
geohash_grid geohash 网格
geohex_grid geohex 网格
geotile_grid geotile 网格
global 全局聚合
histogram 直方图聚合
ip_prefix IP 前缀聚合
ip_range IP 范围聚合
missing 空值聚合
multi_terms 多词项分组聚合
nested 嵌套聚合
parent 父文档聚合
random_sampler 随机采样器聚合
range 范围聚合
rare_terms 稀有多词项聚合
reverse_nested 反向嵌套聚合
sampler 采样器聚合
significant_terms 重要词项聚合
significant_text 重要文本聚合
terms 词项分组聚合
time_series 时间序列聚合
variable_width_histogram 可变宽度直方图聚合

::: details 【示例】terms 聚合查询

默认,ES 不允许对 Text 字段进行 terms 聚合查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 对 keword 进行聚合
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword"
}
}
}
}

// 对 Text 字段进行 terms 聚合查询,失败
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job"
}
}
}
}

// 对 Text 字段打开 fielddata,支持 terms aggregation
PUT employees/_mapping
{
"properties" : {
"job":{
"type": "text",
"fielddata": true
}
}
}

// 对 Text 字段进行 terms 分词。分词后的 terms
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job"
}
}
}
}

POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword"
}
}
}
}

:::

::: details 【示例】更多 Bucket 聚合示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// 对 job 进行近似去重统计
POST /employees/_search
{
"size": 0,
"aggs": {
"cardinate": {
"cardinality": {
"field": "job"
}
}
}
}

// 对 gender 进行聚合
POST /employees/_search
{
"size": 0,
"aggs": {
"gender": {
"terms": {
"field":"gender"
}
}
}
}

// 指定 bucket 的 size
POST /employees/_search
{
"size": 0,
"aggs": {
"ages_5": {
"terms": {
"field":"age",
"size":3
}
}
}
}

// 指定 size,不同工种中,年纪最大的 3 个员工的具体信息
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field":"job.keyword"
},
"aggs":{
"old_employee":{
"top_hits":{
"size":3,
"sort":[
{
"age":{
"order":"desc"
}
}
]
}
}
}
}
}
}

// Salary Ranges 分桶,可以自己定义 key
POST /employees/_search
{
"size": 0,
"aggs": {
"salary_range": {
"range": {
"field":"salary",
"ranges":[
{
"to":10000
},
{
"from":10000,
"to":20000
},
{
"key":">20000",
"from":20000
}
]
}
}
}
}

// Salary Histogram, 工资 0 到 10 万,以 5000 一个区间进行分桶
POST /employees/_search
{
"size": 0,
"aggs": {
"salary_histrogram": {
"histogram": {
"field":"salary",
"interval":5000,
"extended_bounds":{
"min":0,
"max":100000

}
}
}
}
}

// 嵌套聚合 1,按照工作类型分桶,并统计工资信息
POST /employees/_search
{
"size": 0,
"aggs": {
"Job_salary_stats": {
"terms": {
"field": "job.keyword"
},
"aggs": {
"salary": {
"stats": {
"field": "salary"
}
}
}
}
}
}

// 多次嵌套。根据工作类型分桶,然后按照性别分桶,计算工资的统计信息
POST /employees/_search
{
"size": 0,
"aggs": {
"Job_gender_stats": {
"terms": {
"field": "job.keyword"
},
"aggs": {
"gender_stats": {
"terms": {
"field": "gender"
},
"aggs": {
"salary_stats": {
"stats": {
"field": "salary"
}
}
}
}
}
}
}
}

:::

Pipeline(管道聚合)

管道聚合处理从其他聚合而不是文档集生成的输出,从而将信息添加到输出树中。

Pipeline 聚合的分析结果会输出到原结果中,根据位置的不同,分为两类:

管道聚合可以通过使用 buckets_path 参数来指示所需指标的路径,从而引用执行计算所需的聚合。管道聚合不能具有子聚合,但根据类型,它可以引用buckets_path中的另一个管道,从而允许链接管道聚合。

以下为 Elasticsearch 支持的管道聚合类型:

类型 说明
avg_bucket 平均桶
bucket_script 桶脚本
bucket_count_ks_test 桶数 k-s 测试
bucket_correlation 桶关联
bucket_selector 桶选择器
bucket_sort 桶排序
change_point 更改点
cumulative_cardinality 累积基数
cumulative_sum 累计总和
derivative 导数
extended_stats_bucket 扩展的统计信息桶
inference_bucket 推理桶
max_bucket 最大桶
min_bucket 最小桶
moving_function 移动功能
moving_percentiles 移动百分位数
normalize 正常化
percentiles_bucket 百分位数桶
serial_differencing 序列差分
stats_bucket 统计桶
sum_bucket 总和桶

::: details 【示例】Pipeline 聚合示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// 平均工资最低的工作类型
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
},
"min_salary_by_job":{
"min_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}

// 平均工资最高的工作类型
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
},
"max_salary_by_job":{
"max_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}

// 平均工资的平均工资
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
},
"avg_salary_by_job":{
"avg_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}

// 平均工资的统计分析
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
},
"stats_salary_by_job":{
"stats_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}

// 平均工资的百分位数
POST /employees/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
},
"percentiles_salary_by_job":{
"percentiles_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}

// 按照年龄对平均工资求导
POST /employees/_search
{
"size": 0,
"aggs": {
"age": {
"histogram": {
"field": "age",
"min_doc_count": 1,
"interval": 1
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
},
"derivative_avg_salary":{
"derivative": {
"buckets_path": "avg_salary"
}
}
}
}
}
}

// Cumulative_sum
POST /employees/_search
{
"size": 0,
"aggs": {
"age": {
"histogram": {
"field": "age",
"min_doc_count": 1,
"interval": 1
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
},
"cumulative_salary":{
"cumulative_sum": {
"buckets_path": "avg_salary"
}
}
}
}
}
}

// Moving Function
POST /employees/_search
{
"size": 0,
"aggs": {
"age": {
"histogram": {
"field": "age",
"min_doc_count": 1,
"interval": 1
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
},
"moving_avg_salary":{
"moving_fn": {
"buckets_path": "avg_salary",
"window":10,
"script": "MovingFunctions.min(values)"
}
}
}
}
}
}

:::

聚合的执行流程

ES 在进行聚合分析时,协调节点会在每个分片的主分片、副分片中选一个,然后在不同分片上分别进行聚合计算,然后将每个分片的聚合结果进行汇总,返回最终结果。

由于,并非基于全量数据进行计算,所以聚合结果并非完全准确。

要解决聚合准确性问题,有两个解决方案:

  • 解决方案 1:当数据量不大时,设置 Primary Shard 为 1,这意味着在数据全集上进行聚合。
  • 解决方案 2:设置 shard_size 参数,将计算数据范围变大,进而使得 ES 的整体性能变低,精准度变高。shard_size 值的默认值是 size * 1.5 + 10

参考资料