0%

聚合操作

聚合操作aggregations

聚合操作是一种基于搜索的数据汇总,可以对文档中的数据进行统计汇总、分组等,可分为以下几类

  • Bucketing 分组聚合 每个分组都关联一个关键字和相关文档标准
  • Metric 度量聚合 在一组文档中对某一个数字类型字段进行计算
  • Matrix 矩阵聚合
  • Pipeline 管道聚合 该聚合的数据源是其他聚合的输出,然后进行相关指针的计算

聚合操作的语法如下

1
2
3
4
5
6
7
8
9
10
"aggregations" : {
"<aggregation_name>" : { // 定义的逻辑名
"<aggregation_type>" : { // 聚合类型
<aggregation_body>
}
[,"meta" : { [<meta_data_body>] } ]?
[,"aggregations" : { [<sub_aggregation>]+ } ]?
}
[,"<aggregation_name_2>" : { ... } ]*
}

度量聚合

单值度量聚合

平均值

可以计算平均数

1
2
3
4
5
{
"aggs" : {
"avg_grade" : { "avg" : { "field" : "grade" } }
}
}
基数

计算不同值的近似计数

1
2
3
4
5
6
7
8
9
{
"aggs" : {
"type_count" : {
"cardinality" : {
"field" : "type"
}
}
}
}

其还支持精确阈值precision_threshold选项

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"type_count" : {
"cardinality" : {
"field" : "_doc",
"precision_threshold": 100
}
}
}
}

低于阈值接近准确,高于阈值不准确,默认值3000,最大值40000

最大值
1
2
3
4
5
{
"aggs" : {
"max_price" : { "max" : { "field" : "price" } }
}
}
最小值
1
2
3
4
5
{
"aggs" : {
"min_price" : { "min" : { "field" : "price" } }
}
}
求和
1
2
3
4
5
{
"aggs" : {
"hat_prices" : { "sum" : { "field" : "price" } }
}
}
计数
1
2
3
4
5
{
"aggs" : {
"types_count" : { "value_count" : { "field" : "type" } }
}
}

多值聚合

统计

统计聚合中包含有最大值、最小值、和、计数、平均数

1
2
3
4
5
{
"aggs" : {
"grades_stats" : { "stats" : { "field" : "grade" } }
}
}

得到的结果为

1
2
3
4
5
6
7
8
9
10
11
{
"aggregations": {
"grades_stats": {
"count": 2,
"min": 50.0,
"max": 100.0,
"avg": 75.0,
"sum": 150.0
}
}
}
百分比

对聚合文档中提取的数字型值计算一个或多个百分比

1
2
3
4
5
6
7
8
9
{
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time"
}
}
}
}

结果可以展示出每个值对应的百分比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"aggregations": {
"load_time_outlier": {
"values" : {
"1.0": 5.0,
"5.0": 25.0,
"25.0": 165.0,
"50.0": 445.0,
"75.0": 725.0,
"95.0": 945.0,
"99.0": 985.0
}
}
}
}

当然如果只是关系某些百分比指标,可以使用percents来进行指定

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time",
"percents" : [95, 99, 99.9]
}
}
}
}
百分比分级

该百分比等级表示测试值低于某一特定值的百分比

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"load_time_ranks" : {
"percentile_ranks" : {
"field" : "load_time",
"values" : [500, 600]
}
}
}
}

其返回结果为

1
2
3
4
5
6
7
8
9
10
{
"aggregations": {
"load_time_ranks": {
"values" : {
"500.0": 90.01,
"600.0": 100.0
}
}
}
}
最高命中排行

最高命中排行聚合会在聚合文档中找到相关度最高的文档,可以使用的参数

  • from 第一个结果的偏移量
  • size 每个分组返回命中文档的最大数量,默认返回前三个
  • sort 指定最高命中文档应该排序,默认按照主查询分数排序
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
{
"aggs": {
"top_tags": {
"terms": {
"field": "type",
"size": 3
},
"aggs": {
"top_sales_hits": {
"top_hits": {
"sort": [
{
"date": {
"order": "desc"
}
}
],
"_source": {
"includes": [ "date", "price" ]
},
"size" : 1
}
}
}
}
}
}
脚本度量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"query" : {
"match_all" : {}
},
"aggs": {
"profit": {
"scripted_metric": {
"init_script" : "state.transactions = []",
"map_script" : "state.transactions.add(doc.type.value == 'sale' ? doc.amount.value : -1 * doc.amount.value)",
"combine_script" : "double profit = 0; for (t in state.transactions) { profit += t } return profit",
"reduce_script" : "double profit = 0; for (a in states) { profit += a } return profit"
}
}
}
}

脚本的作用域

  • init_script 初始化脚本,在任何文档收集之前执行
  • map_script 映射脚本,每个被采集的文档都会执行一次脚本
  • combine_script 联合脚本,每个分片会在文档采集结束的时候执行一次脚本
  • reduce_script 归纳脚本,在所有分片返回结果之后,请求节点执行一次脚本
地理边界

指定一个字段来获取边界,该字段类型需要是geo_point

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"query" : {
"match" : { "name" : "musée" }
},
"aggs" : {
"viewport" : {
"geo_bounds" : {
"field" : "location",
"wrap_longitude" : true
}
}
}
}
地理中心

从文档中的地理点数据类型字段获取的所有坐标值中计算出中心点

1
2
3
4
5
6
7
8
9
{
"aggs" : {
"centroid" : {
"geo_centroid" : {
"field" : "location"
}
}
}
}

分组聚合

分组聚合不是通过字段进行计算,而是根据文档创建分组,分组聚合下可以拥有子聚合,这些子聚合可以聚合由它们父聚合创建的分组

多分组聚合

子聚合

可以通过父类型文档的分组聚合产生子类型文档的分组,type表示父空间的分组应该被映射为哪一种字段

举例:

一个问题类型的文档

1
2
3
4
5
6
7
8
9
10
11
12
{
"join": {
"name": "question"
},
"body": "<p>I have Windows 2003 server and i bought a new Windows 2008 server...",
"title": "Whats the best way to file transfer my site from server to a newer one?",
"tags": [
"windows-server-2003",
"windows-server-2008",
"file-transfer"
]
}

一个答案类型的文档

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
{
"join": {
"name": "answer",
"parent": "1" // 父子关系是根据parent来进行绑定的
},
"owner": {
"location": "Norfolk, United Kingdom",
"display_name": "Sam",
"id": 48
},
"body": "<p>Unfortunately you're pretty much limited to FTP...",
"creation_date": "2009-05-04T13:45:37.030"
}

{
"join": {
"name": "answer",
"parent": "1"
},
"owner": {
"location": "Norfolk, United Kingdom",
"display_name": "Troll",
"id": 49
},
"body": "<p>Use Linux...",
"creation_date": "2009-05-05T13:45:37.030"
}

聚合

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
{
"aggs": {
"top-tags": { //父问题分组
"terms": {
"field": "tags.keyword",
"size": 10
},
"aggs": {
"to-answers": {
"children": { // 子答案分组
"type" : "answer"
},
"aggs": {
"top-names": {
"terms": {// 子答案的owner.display_name字段分组
"field": "owner.display_name.keyword",
"size": 10
}
}
}
}
}
}
}
}

得到的返回,会得到每个问题标签下的答案的所有者

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
{
"took": 25,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped" : 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 0.0,
"hits": []
},
"aggregations": {
"top-tags": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "file-transfer",
"doc_count": 1,
"to-answers": {
"doc_count": 2,
"top-names": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Sam",
"doc_count": 1
},
{
"key": "Troll",
"doc_count": 1
}
]
}
}
},
{
"key": "windows-server-2003",
"doc_count": 1,
"to-answers": {
"doc_count": 2,
"top-names": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Sam",
"doc_count": 1
},
{
"key": "Troll",
"doc_count": 1
}
]
}
}
},
{
"key": "windows-server-2008",
"doc_count": 1,
"to-answers": {
"doc_count": 2,
"top-names": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Sam",
"doc_count": 1
},
{
"key": "Troll",
"doc_count": 1
}
]
}
}
}
]
}
}
}
直方图聚合

直方图聚合是一个多分组聚合,可以应用于从文档中提取的数值,在数值上动态创建固定大小的分组

其使用的公式为bucket_key = Math.floor((value - offset) / interval) * interval + offset

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"prices" : {
"histogram" : { // 间隔为50的数据
"field" : "price",
"interval" : 50
}
}
}
}

得到的数据

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
{
...
"aggregations": {
"prices" : {
"buckets": [
{ // 0到50
"key": 0.0,
"doc_count": 1
},
{ // 50到100
"key": 50.0,
"doc_count": 1
},
{
"key": 100.0,
"doc_count": 0
},
{
"key": 150.0,
"doc_count": 2
},
{
"key": 200.0,
"doc_count": 3
}
]
}
}
}
日期直方图聚合

日期直方图聚合是一个多分组聚合,除了只能处理日期类型的值之外,和直方图聚合功能是一样的

1
2
3
4
5
6
7
8
9
10
11
{
"aggs" : {
"sales_over_time" : {
"date_histogram" : {
"field" : "date",
"interval" : "1d", // 间隔一天
"format" : "yyyy-MM-dd" // 如果不设置时间格式,会采用读取的第一个时间的时间格式
}
}
}
}

可以使用的时间间隔单位为

  • milliseconds (ms)
  • seconds (s)
  • minutes (m)
  • hours (h)
  • days (d)
  • weeks (w)
  • months (M)
  • quarters (q)
  • years (y)
时间范围聚合

时间范围聚合是一个专门用于处理时间数据的范围聚合,使用from,to来确定范围,包含from,排除to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"aggs": {
"range": {
"date_range": {
"field": "date",
"format": "MM-yyyy",
"ranges": [
{ "to": "now-10M/M" },
{ "from": "now-10M/M" }
]
}
}
}
}
范围聚合

范围聚合是一个基于多组值来源的聚合,使用from,to来确定范围,包含from,排除to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"aggs" : {
"price_ranges" : {
"range" : {
"field" : "price",
"ranges" : [
{ "to" : 100.0 },
{ "from" : 100.0, "to" : 200.0 },
{ "from" : 200.0 }
]
}
}
}
}
多重过滤聚合

可以定义多个分组,每个分组关联一个过滤条件

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"size": 0,
"aggs" : {
"messages" : {
"filters" : {
"filters" : {
"errors" : { "match" : { "body" : "error" }},
"warnings" : { "match" : { "body" : "warning" }}
}
}
}
}
}
地理点距离聚合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"aggs" : {
"rings_around_amsterdam" : {
"geo_distance" : {
"field" : "location",
"origin" : "52.3760, 4.894",
"ranges" : [
{ "to" : 100000 },
{ "from" : 100000, "to" : 300000 },
{ "from" : 300000 }
]
}
}
}
}
地理点散列网格聚合

将每个点分配到代表网格中各个单元格的分组里

低精度的

1
2
3
4
5
6
7
8
9
10
{
"aggregations" : {
"large-grid" : {
"geohash_grid" : {
"field" : "location",
"precision" : 3
}
}
}
}

高精度的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"aggregations" : {
"zoomed-in" : {
"filter" : {
"geo_bounding_box" : {
"location" : {
"top_left" : "52.4, 4.9",
"bottom_right" : "52.3, 5.0"
}
}
},
"aggregations":{
"zoom1":{
"geohash_grid" : {
"field": "location",
"precision": 8
}
}
}
}
}
}
IP范围聚合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"size": 10,
"aggs" : {
"ip_ranges" : {
"ip_range" : {
"field" : "ip",
"ranges" : [
{ "to" : "10.0.0.5" },
{ "from" : "10.0.0.5" }
]
}
}
}
}

单分组聚合

过滤聚合

过滤聚合包含当前文档集中所有匹配指定的过滤条件的文档

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"t_shirts" : {
"filter" : { "term": { "type": "t-shirt" } },
"aggs" : {
"avg_price" : { "avg" : { "field" : "price" } }
}
}
}
}
空值聚合

空值聚合可以在当前文档集中对所有缺失字段值的文档创建一个分组

1
2
3
4
5
6
7
{
"aggs" : {
"products_without_a_price" : {
"missing" : { "field" : "price" }
}
}
}
嵌套分组

嵌套分组可以用来聚合嵌套的文档

嵌套文档示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"mappings": {
"product" : {
"properties" : {
"resellers" : {
"type" : "nested",
"properties" : {
"reseller" : { "type" : "text" },
"price" : { "type" : "double" }
}
}
}
}
}
}

分组

1
2
3
4
5
6
7
8
9
10
11
12
{
"aggs" : {
"resellers" : {
"nested" : {
"path" : "resellers"
},
"aggs" : {
"min_price" : { "min" : { "field" : "resellers.price" } }
}
}
}
}
采样聚合

采样聚合用在子聚合来限制得分最高的文档的样本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"query": {
"query_string": {
"query": "tags:kibana OR tags:javascript"
}
},
"aggs": {
"sample": {
"sampler": {
"shard_size": 200 // 限制每个分片执行采样的过程中多少高得分文档会被采集
},
"aggs": {
"keywords": {
"significant_terms": {
"field": "tags",
"exclude": ["kibana", "javascript"]
}
}
}
}
}
}
重要索引词聚合

根据提供的索引词来进行聚合

1
2
3
4
5
6
7
8
9
10
{
"query" : {
"terms" : {"force" : [ "British Transport Police" ]}
},
"aggregations" : {
"significant_crime_types" : {
"significant_terms" : { "field" : "crime_type" }
}
}
}
索引词聚合
1
2
3
4
5
6
7
{
"aggs" : {
"genres" : {
"terms" : { "field" : "genre" }
}
}
}

管道聚合

管道聚合工作于其他聚合产生的输出结果而不是文档集,有两大类,父类聚合和兄弟聚合

  • 父类聚合 在父聚合的基础上进行管道聚合,可以在现有分组的基础上计算新的分组或者聚合
  • 兄弟聚合 在兄弟聚合输出结果的基础上进行管道聚合,可以计算与兄弟聚合相同等级的新聚合

通过bucket_path参数指定请求指标的路径,管道聚合可以引用需要的聚合来执行计算,可以在buckets_path参数中引入另一个管道聚合,使管道聚合链接起来

buckets_path语法

  • 聚合分隔符为 >
  • 指标分隔符为 .
  • 聚合名为 <聚合名称>
  • 指标为 <指标名称>
  • 路径为 <聚合名>[ <聚合分隔符>, <聚合名> ]* [ <指标分隔符>, <指标> ]

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"aggs": {
"my_date_histo":{
"date_histogram":{
"field":"timestamp",
"interval":"day"
},
"aggs":{
"the_sum":{
"sum":{ "field": "lemmings" }
},
"the_movavg":{
"moving_avg":{ "buckets_path": "the_sum" }
}
}
}
}
}
平均分租

平均分租聚合会计算在一组聚合中指定指标的平均值,指定的指标必须是数字型而且这个组聚合必须是多组聚合

1
2
3
4
5
{
"avg_bucket": {
"buckets_path": "the_sum"
}
}

可用参数有

  • buckets_path 分组路径,必填
  • gap_policy 当数据缺口出现时应用的策略,默认skip
  • format 输出值格式,默认null
移动平均聚合

移动平均聚合会在数据上滑动一个固定大小的窗口并且给出窗口的平均值

1
2
3
4
5
6
7
8
9
10
11
{
"moving_avg": {
"buckets_path": "the_sum",
"model": "holt",
"window": 5,
"gap_policy": "insert_zeros",
"settings": {
"alpha": 0.8
}
}
}

可用参数有

  • buckets_path 分组路径,必填
  • model 移动平均加权的模型,默认simple
  • gap_policy 当数据缺口出现时应用的策略,默认insert_zeros
  • window 滑动窗口,默认5
  • settings 模型的具体设置,根据指定的模型有不同的内容
总和分组聚合

用于计算一组聚合创建的所有分组中指定指标的和

1
2
3
4
5
{
"sum_bucket": {
"buckets_path": "the_sum"
}
}
总和累计聚合

计算父直方图聚合中指定的指标的累计值

1
2
3
4
5
{
"cumulative_sum": {
"buckets_path": "the_sum"
}
}
最大分组聚合

一组指定指标的最大值

1
2
3
4
5
{
"max_bucket": {
"buckets_path": "the_sum"
}
}
最小分组聚合

一组指定指标的最小值

1
2
3
4
5
{
"min_bucket": {
"buckets_path": "the_sum"
}
}
统计分组聚合

可进行计算count、min、max、avg、sum

1
2
3
4
5
{
"stats_bucket": {
"buckets_path": "the_sum"
}
}
百分位分组聚合

在一组聚合的所有分组中对一个指标的指标计算百分比

1
2
3
4
5
{
"percentiles_bucket": {
"buckets_path": "the_sum"
}
}
差值聚合

计算一个指定指标两个分组之间的差值

1
2
3
4
{
"derivative": {
"buckets_path": "sales"
}
分组选择器聚合

分组选择器聚合是指在聚合的结果执行一个脚本来决定当前分组是否应该保留

1
2
3
4
5
6
7
8
9
{
"bucket_selector": {
"buckets_path": {
"my_var1": "the_sum",
"my_var2": "the_value_count"
},
"script": "params.my_var1 > params.my_var2"
}
}