(三)ElasticSearch 数据纵览——聚合

什么是聚合?

  通过 ES 聚合,我们将得到一个数据的概览。

  若我们需要的是纵览和分析全部的数据而不是寻找单个文档,比如说想要知道:

  • 世界上有多少手机厂商?
  • 每月新增的手机厂商有多少?
  • 按照手机厂商来划分,手机的平均定价是多少?

  通过聚合,可以搜寻到以上问题的答案,并且聚合也可以回答更加细微的问题,比如说:

  • 最受欢迎的手机厂商是哪家?
  • 目前性能最高的手机是哪家厂商的?是哪一款?

  聚合允许我们向数据提出一些复杂的问题。虽然功能完全不同于搜索,但它使用相同的数据结构。
  这意味着聚合的执行速度很快并且就像搜索一样几乎是实时的。

  对报告和仪表盘来说,聚合功能是非常强大的。
  比如说,实时显示的数据可以让你立即处理,而不是对数据进行汇总( 需要一周时间去运行 Hadoop 任务 ),报告还可以随着你的数据变化而变化,而不是预先计算的、过时的和不相关的。

  最后,聚合和搜索是同级别的。
  这意味着你可以在单个请求里同时对相同的数据进行搜索/过滤和分析,并且由于聚合是在用户搜索的上下文里计算的,不只是显示相关数据的数量,而是显示匹配查询条件的相关数据的数量。

基本概念

  类似于 DSL 查询,聚合也有可组合的语法,这意味着只需要学习很少的基本概念,就可以得到几乎无尽的组合。

  要掌握聚合,只需要明白 2 个主要的概念:

  • 桶(Buckets):满足特定条件的文档集合
  • 指标(Metrics):对桶内的文档进行统计计算

  这就是全部了!每个聚合都是一个或者多个桶零个或者多个指标的组合。
  下面将它们翻译成简单的 SQL 语句来解释吧:

1
2
3
SELECT COUNT(color) # COUNT(color)   相当于指标
FROM table
GROUP BY color # GROUP BY color 相当于分桶,将大桶以颜色划分为各种颜色桶

  如上,在概念上:

  • 划分桶类似于 SQL 的分组(GROUP BY)
  • 指标则类似于 COUNT() 、 SUM() 、 MAX() 等统计函数

  桶,简单来说就是满足特定条件的文档的集合:

  • 按性别划分,一个人属于男性桶或者女性桶
  • 按国家划分,世界奇迹之一长城属于中国桶
  • 按月份划分,2020-5-20属于 五月桶

  当聚合开始被执行,每个文档里面的值通过计算来决定符合哪个桶的条件。
  若匹配到,文档将放入相应的桶并接着进行聚合操作。

  桶也可以被嵌套在其他桶里面,提供层次化的或者有条件的划分方案。
  比如,诸葛亮会被放入蜀国这个桶,而整个蜀国桶会被放入三国这个桶。

  Elasticsearch 有很多种类型的桶,能让你通过很多种方式来划分文档(时间、最受欢迎的词、年龄区间、地理位置等等)。
  其实从根本上来讲,都是通过同样的原理进行操作:基于条件来划分文档。

指标

  桶能让我们划分文档到有意义的集合,但是最终我们需要的是对这些桶内的文档进行一些指标的计算。
  分桶是一种为了达到目的的手段:它提供了一种给文档分组的方法来让我们可以计算感兴趣的指标。

  大多数指标是简单的数学运算(例如最小值、平均值、最大值,还有汇总),这些将通过文档的值来计算。
  在实践中,指标能让你计算像平均薪资、最高出售价格、95% 的查询延迟、商品总数的此类数据。

桶和指标的组合

  聚合由桶和指标组成。
  聚合可能只有一个桶,可能只有一个指标,或者可能两个都有。当然,还可能有一些桶嵌套在其他桶里面。
  比如,我们可以通过所属国家来划分文档(桶),然后计算每个国家的平均 GDP(指标)。

  由于桶可以被嵌套,我们可以实现非常多并且非常复杂的聚合,比如:

  • ① 首先通过国家划分文档(桶)
  • ② 然后通过性别划分每个国家(桶)
  • ③ 在通过年龄区间划分每种性别(桶)
  • ④ 最后,为每个年龄区间计算平均薪资(指标)

  最后将告诉你每个 <国家, 性别, 年龄> 组合的人的平均薪酬,所有的这些都在一个请求内完成并且只遍历一次数据!

快速入门

数据准备

  下面先看一个例子。我们将会创建一些对汽车厂商有用的聚合,数据是关于汽车交易的信息:车型、制造商、售价、何时被出售等。

  首先我们批量索引一些数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PUT /cars
{
"mappings": {
"properties": {
"price": {
"type": "double"
},
"color": {
"type": "keyword"
},
"make":{
"type": "keyword"
},
"sold":{
"type": "date"
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /cars/_bulk
{ "index": {}}
{ "price" : 10000, "color" : "玫瑰红", "make" : "劳斯莱斯", "sold" : "2020-10-28" }
{ "index": {}}
{ "price" : 20000, "color" : "商务黑", "make" : "凯迪拉克", "sold" : "2020-11-05" }
{ "index": {}}
{ "price" : 30000, "color" : "土豪金", "make" : "福特", "sold" : "2020-05-18" }
{ "index": {}}
{ "price" : 15000, "color" : "宝石绿", "make" : "劳斯莱斯", "sold" : "2020-07-02" }
{ "index": {}}
{ "price" : 12000, "color" : "宝石绿", "make" : "奔驰", "sold" : "2020-08-19" }
{ "index": {}}
{ "price" : 20000, "color" : "玫瑰红", "make" : "宝马", "sold" : "2020-11-05" }
{ "index": {}}
{ "price" : 80000, "color" : "玫瑰红", "make" : "奔驰", "sold" : "2020-01-01" }
{ "index": {}}
{ "price" : 25000, "color" : "梦幻蓝", "make" : "凯迪拉克", "sold" : "2020-02-12" }

以桶分类

  有了数据,开始构建我们的第一个聚合。
  汽车厂商可能会想知道哪个颜色的汽车销量最好,用聚合可以轻易得到结果,用 terms桶操作:

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": { // ①
"popular_color": { // ②
"terms": { // ③
"field": "color"
}
}
}
}

  ① 聚合操作被置顶于顶层参数aggsaggregations缩写)下
  ② 然后,可以为聚合指定一个我们想要的名称,此处为popular_color
  ③ 最后,定义单个桶的类型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
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 8,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"popular_color" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "玫瑰红",
"doc_count" : 3
},
{
"key" : "宝石绿",
"doc_count" : 2
},
{
"key" : "商务黑",
"doc_count" : 1
},
{
"key" : "土豪金",
"doc_count" : 1
},
{
"key" : "梦幻蓝",
"doc_count" : 1
}
]
}
}
}

以度量(函数)按需计算

  前面的例子告诉我们每个桶里面的文档数量,这很有用。但通常,我们的应用需要提供更复杂的文档度量。
  例如,每种颜色汽车的平均价格是多少?

  为了获取更多信息,我们需要告诉 Elasticsearch 使用哪个字段,计算何种度量。
  这需要将度量嵌套在桶内, 度量会基于桶内的文档计算统计结果。

  让我们继续为汽车的例子加入average平均度量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /cars/_search
{
"size": 0,
"aggs": {
"colors": {
"terms": {
"field": "color"
},
"aggs": { // ①
"avg_price": { // ②
"avg": {
"field": "price" // ③
}
}
}
}
}
}

  ① 为度量新增aggs
  ② 为度量指定名字avg_price
  ③ 最后,为price定义avg度量(函数)

  正如所见,我们用前面的例子加入了新的aggs层。这个新的聚合层让我们可以将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
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
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 8,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"colors" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "玫瑰红",
"doc_count" : 3,
"avg_price" : {
"value" : 36666.666666666664
}
},
{
"key" : "宝石绿",
"doc_count" : 2,
"avg_price" : {
"value" : 13500.0
}
},
{
"key" : "商务黑",
"doc_count" : 1,
"avg_price" : {
"value" : 20000.0
}
},
{
"key" : "土豪金",
"doc_count" : 1,
"avg_price" : {
"value" : 30000.0
}
},
{
"key" : "梦幻蓝",
"doc_count" : 1,
"avg_price" : {
"value" : 25000.0
}
}
]
}
}
}

嵌套桶

  在我们使用不同的嵌套方案时,聚合的力量才能真正得以显现。
  在前例中,我们已经看到如何将一个度量嵌入桶中,它的功能已经十分强大了。

  但真正令人激动的分析来自于将桶嵌套进另外一个桶所能得到的结果
  现在,我们想知道每个颜色的汽车制造商的分布:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /cars/_search
{
"size": 0,
"aggs": {
"colors": {
"terms": {
"field": "color"
},
"aggs": {
"avg_price": { // ①
"avg": {
"field": "price"
}
},
"make": { // ②
"terms": {
"field": "make" // ③
}
}
}
}
}
}

  ① 注意前例中的avg_price度量仍然保持原位
  ② 另一个聚合make被加入到了color颜色桶中。
  ③ 现在的聚合是terms桶,它会为每个汽车厂生成唯一的桶

  这里发生了一些有趣的事。 首先,我们可能会观察到之前例子中的 avg_price 度量完全没有变化,还在原来的位置。
  一个聚合的每个层级 都可以有多个度量或桶,avg_price度量告诉我们每种颜色汽车的平均价格。它与其他的桶和度量相互独立。

  这对我们的应用非常重要,因为这里面有很多相互关联,但又完全不同的度量需要收集。聚合使我们能够用一次数据请求获得所有的这些信息。

  另外一件值得注意的重要事情是我们新增的这个 make 聚合,它是一个 terms 桶(嵌套在 colors 、 terms 桶内)。这意味着它会为数据集中的每个唯一组合生成( color 、 make )元组。

  让我们看看返回的响应:

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
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 8,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"colors" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "玫瑰红",
"doc_count" : 3,
"avg_price" : {
"value" : 36666.666666666664
},
"make" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "劳斯莱斯",
"doc_count" : 1
},
{
"key" : "奔驰",
"doc_count" : 1
},
{
"key" : "宝马",
"doc_count" : 1
}
]
}
},
{
"key" : "宝石绿",
"doc_count" : 2,
"avg_price" : {
"value" : 13500.0
},
"make" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "劳斯莱斯",
"doc_count" : 1
},
{
"key" : "奔驰",
"doc_count" : 1
}
]
}
},
{
"key" : "商务黑",
"doc_count" : 1,
"avg_price" : {
"value" : 20000.0
},
"make" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "凯迪拉克",
"doc_count" : 1
}
]
}
},
{
"key" : "土豪金",
"doc_count" : 1,
"avg_price" : {
"value" : 30000.0
},
"make" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "福特",
"doc_count" : 1
}
]
}
},
{
"key" : "梦幻蓝",
"doc_count" : 1,
"avg_price" : {
"value" : 25000.0
},
"make" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "凯迪拉克",
"doc_count" : 1
}
]
}
}
]
}
}
}

  正如期望的那样,新的聚合嵌入在每个颜色桶中。
  现在我们观察到了对颜色分组后车辆的平均价格,以及按不同制造商分类的车辆信息
  其中,我们看到前例中的avg_price度量仍然维持不变。

  除此之外,响应结果还告诉我们以下几点:

  • 玫瑰红的车有三辆
  • 玫瑰红的车平均售价是 36666
  • 三辆车分别由是劳斯莱斯、奔驰、宝马厂商制造

聚合类型

  在快速入门中,我们看到聚合的仅仅只是冰山一角。
  其实,聚合共有以下 4 个分类:

  • Bucket Aggregations:桶聚合,以特定条件将总文档划分为不同的桶
  • Metric Aggregations:度量聚合,对桶内的文档进行函数计算(求和、平均等)
  • Matrix Aggregations:支持对多个字段的操作并提供一个结果矩阵,试验性版本
  • Pipeline Aggregations:管道聚合,可以对其他聚合结果进行二次聚合

  对快速入门中的聚合而言,其实仅仅可归属为Bucket Aggregations中的一种或Metric Aggregations的一种,下面将详细介绍。

Bucket Aggregations

  Bucket 聚合可以根据特定条件来构造文档的集合

  在 ES 7.X 版本, Bucket 聚合已达 27 种之多。
  对于每一种聚合类型,若我们都去详细学习并掌握的话,是比较费时间的。
  因此,了解每种聚合类型的使用场景,简单而言,就是知道每种聚合是干嘛的,能对数据做怎样的分析,了解其注意事项和重要参数,之后即用即查就可以了。

Terms Aggregation

  术语聚合,可以按照某个字段的值,将文档聚合分类。
  术语聚合等价于 SQL 的GROUP BY

  示例如下(获取不同汽车厂商汽车的销售情况):

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": {
"car_make": {
"terms": {
"field": "make"
}
}
}
}

  测试结果:

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
{
...
"aggregations" : {
"car_make" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "凯迪拉克",
"doc_count" : 2
},
{
"key" : "劳斯莱斯",
"doc_count" : 2
},
{
"key" : "奔驰",
"doc_count" : 2
},
{
"key" : "宝马",
"doc_count" : 1
},
{
"key" : "福特",
"doc_count" : 1
}
]
}
}
}

:默认根据doc_count降序排序

Rare Terms Aggregation

  稀有术语聚合,与术语聚合类似,但聚合结果的排序是默认根据 doc_count 的值升序排序。

  示例如下(获取不同汽车厂商汽车的销售情况):

1
2
3
4
5
6
7
8
9
10
11
12
POST /cars/_search
{
"size": 0,
"aggs": {
"car_make": {
"rare_terms": {
"field": "make",
"max_doc_count": 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
{
...
"aggregations" : {
"car_make" : {
"buckets" : [
{
"key" : "宝马",
"doc_count" : 1
},
{
"key" : "福特",
"doc_count" : 1
},
{
"key" : "凯迪拉克",
"doc_count" : 2
},
{
"key" : "劳斯莱斯",
"doc_count" : 2
},
{
"key" : "奔驰",
"doc_count" : 2
}
]
}
}
}

参数:max_doc_count

  max_doc_count参数指定类术语出现的最大文档数(返回的bucket 的 doc_count <= 该值),
  max_doc_count参数的默认值为 1,最大值为 100。

Range Aggregation

  范围聚合可以根据自定义的范围进行聚合。
  示例如下(获取指定范围的汽车销售情况):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /cars/_search
{
"size": 0,
"aggs": {
"price_range": {
"range": {
"field": "price",
"ranges": [
{
"to": 30000
},
{
"from": 30000,
"to": 60000
},
{
"from": 60000
}
]
}
}
}
}

  测试结果:

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
{
...
"aggregations" : {
"price_range" : {
"buckets" : [
{
"key" : "*-30000.0",
"to" : 30000.0,
"doc_count" : 6
},
{
"key" : "30000.0-60000.0",
"from" : 30000.0,
"to" : 60000.0,
"doc_count" : 1
},
{
"key" : "60000.0-*",
"from" : 60000.0,
"doc_count" : 1
}
]
}
}
}

注意哦:该聚合包括from的值,但不包括to的值,即左闭右开。

Date Range Aggregation

  日期范围聚合,与Range Aggregation范围聚合类似,但针对的是date类型的字段。
  此聚合和范围聚合之间的主要区别为:fromto值可以在Date Math表达式中表示,并且还可以指定日期格式,通过该日期格式将返回fromto响应字段(注意此聚合包括from值,但不包括to每个范围的值)
  示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST /cars/_search
{
"size": 0,
"aggs": {
"price_date_range": {
"date_range": {
"field": "sold",
"format": "yyyy-MM-dd",
"ranges": [
{
"to": "now-10M/M"
},
{
"from": "now-10M/M"
}
]
}
}
}
}

  测试结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
...
"aggregations" : {
"price_date_range" : {
"buckets" : [
{
"key" : "*-2019-09-01",
"to" : 1.567296E12,
"to_as_string" : "2019-09-01",
"doc_count" : 0
},
{
"key" : "2019-09-01-*",
"from" : 1.567296E12,
"from_as_string" : "2019-09-01",
"doc_count" : 8
}
]
}
}
}

IP Range Aggregation IP

  IP 范围聚合,与Range Aggregation范围聚合类似,但针对的是 IP 类型的字段。

  示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /ip_addresses/_search
{
"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
11
12
13
14
15
16
17
18
19
{
...
"aggregations": {
"ip_ranges": {
"buckets": [
{
"key": "*-10.0.0.5",
"to": "10.0.0.5",
"doc_count": 10
},
{
"key": "10.0.0.5-*",
"from": "10.0.0.5",
"doc_count": 0
}
]
}
}
}

Histogram Aggregation

  直方图聚合可以根据数值或数值范围按固定间隔将文档分类:

  示例如下(获取不同价格区间汽车的销售情况):

1
2
3
4
5
6
7
8
9
10
11
12
POST /cars/_search
{
"size": 0,
"aggs": {
"price": {
"histogram": {
"field": "price",
"interval": 10000
}
}
}
}

  测试结果:

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
{
...
"aggregations" : {
"price" : {
"buckets" : [
{
"key" : 10000.0,
"doc_count" : 3
},
{
"key" : 20000.0,
"doc_count" : 3
},
{
"key" : 30000.0,
"doc_count" : 1
},
{
"key" : 40000.0,
"doc_count" : 0
},
{
"key" : 50000.0,
"doc_count" : 0
},
{
"key" : 60000.0,
"doc_count" : 0
},
{
"key" : 70000.0,
"doc_count" : 0
},
{
"key" : 80000.0,
"doc_count" : 1
}
]
}
}
}

Date Histogram Aggregation

  日期直方图聚合,根据 date 或 date range 类型的字段,按固定间隔将文档分类。

  示例如下(获取已销售汽车的每月销售情况):

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /cars/_search
{
"size": 0,
"aggs": {
"sold_date_info": {
"date_histogram": {
"field": "sold",
"calendar_interval": "month",
"format": "yyyy-MM-dd"
}
}
}
}

  返回结果:

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
{
...
"aggregations" : {
"sold_date_info" : {
"buckets" : [
{
"key_as_string" : "2020-01-01",
"key" : 1577836800000,
"doc_count" : 1
},
{
"key_as_string" : "2020-02-01",
"key" : 1580515200000,
"doc_count" : 1
},
{
"key_as_string" : "2020-03-01",
"key" : 1583020800000,
"doc_count" : 0
},
{
"key_as_string" : "2020-04-01",
"key" : 1585699200000,
"doc_count" : 0
},
{
"key_as_string" : "2020-05-01",
"key" : 1588291200000,
"doc_count" : 1
},
{
"key_as_string" : "2020-06-01",
"key" : 1590969600000,
"doc_count" : 0
},
{
"key_as_string" : "2020-07-01",
"key" : 1593561600000,
"doc_count" : 1
},
{
"key_as_string" : "2020-08-01",
"key" : 1596240000000,
"doc_count" : 1
},
{
"key_as_string" : "2020-09-01",
"key" : 1598918400000,
"doc_count" : 0
},
{
"key_as_string" : "2020-10-01",
"key" : 1601510400000,
"doc_count" : 1
},
{
"key_as_string" : "2020-11-01",
"key" : 1604188800000,
"doc_count" : 2
}
]
}
}
}

参数:calendar_interval

  calendar _ interval参数可以配置日历的间隔。
  calendar _ interval参数具体可以使用:

  • minute1m:每分钟聚合信息
  • hour1h:每小时聚合信息
  • day1d:每天聚合信息
  • week1w:每星期聚合信息
  • month1M:每月聚合信息
  • quarter1q:每季度聚合信息
  • year1y:每年聚合信息

注意哦calendar _ interval参数不支持多个日历单元(比如以 2 个月间隔划分)

参数:Fixed intervals

  Fixed intervals固定时间间隔是为了弥补calendar _ interval的不足而生。
  但是Fixed intervals意味着固定间隔不能表示其他单位,如月,因为一个月的持续时间不是一个固定的数量。试图指定诸如月份或季度之类的日历间隔将引发异常。

  因此,Fixed intervals可接受的单位为:

  • ms毫秒
  • s
  • m
  • h
  • d:天

参数:format

  format参数可以支持指定表达式的日期格式。

Missing Aggregation

  缺失聚合获取指定Field无值(缺失字段或为 NULL 值)的文档集合。
  缺失聚合等价于 SQL:

1
2
3
SELECT COUNT
FROM product
WHERE prod_price IS NULL

  比如获取没有标价的商品的总数:

1
2
3
4
5
6
7
8
9
10
11

POST /sales/_search?size=0
{
"aggs": {
"products_without_a_price": {
"missing": {
"field": "price"
}
}
}
}

Filter Aggregation 过滤器聚合

  暂未使用,后续研究。

Filters Aggregation 过滤器集合聚合

  暂未使用,后续研究。

Children Aggregation 子聚合

  暂未使用,后续研究。

Geo Distance Aggregation 地理距离聚合

  暂未使用,后续研究。

GeoHash grid Aggregation GeoHash 网格聚合

  暂未使用,后续研究。

Global Aggregation 全局聚合

  暂未使用,后续研究。

Adjacency Matrix Aggregation 邻接矩阵聚合

  暂未使用,后续研究。

Nested Aggregation 嵌套聚合

  暂未使用,后续研究。

Reverse nested Aggregation 反向嵌套聚合

  暂未使用,后续研究。

Sampler Aggregation 取样器聚合

  暂未使用,后续研究。

Diversified Sampler Aggregation 多元化取样器聚合

  暂未使用,后续研究。

Significant Terms Aggregation 重要术语聚合

  暂未使用,后续研究。

Significant Text Aggregation 重要文本聚合

  暂未使用,后续研究。

Metric Aggregations

  Metric Aggregations,即度量(指标)聚合。
  可以对桶内的文档进行相关函数计算,比如计算平均值,总数,最大值,最小值等等。

Avg Aggregation

  平均聚合:计算从聚合文档指定字段中提取数值的平均值。
  这些值可以从文档中的特定数字字段中提取,也可以由提供的脚本生成。

  来看一个例子(获取已销售汽车售价的平均值):

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": {
"price_avg": {
"avg": {
"field": "price"
}
}
}
}

  返回结果如下:

1
2
3
4
5
6
7
8
{
...
"aggregations" : {
"price_avg" : {
"value" : 26500.0
}
}
}

missing 参数

  missing参数定义应如何处理缺少值的文档。默认情况下,这些文档将被忽略,但也可以将它们视为具有值。

1
2
3
4
5
6
7
8
9
10
11
12
POST /cars/_search
{
"size": 0,
"aggs": {
"price_avg": {
"avg": {
"field": "price",
"missing": 1000.0
}
}
}
}

Max Aggregation

  最大聚合:计算从聚合文档指定字段中提取数值中的最大值。
  这些值可以从文档中的特定数字字段中提取,也可以由提供的脚本生成。

  来看一个例子(获取已销售汽车中价格最高的信息):

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": {
"price_max": {
"max": {
"field": "price"
}
}
}
}

  返回结果如下:

1
2
3
4
5
6
7
8
{
...
"aggregations" : {
"price_max" : {
"value" : 80000.0
}
}
}

missing 参数

  missing参数定义应如何处理缺少值的文档。默认情况下,这些文档将被忽略,但也可以将它们视为具有值。

Min Aggregation

  最小聚合:计算从聚合文档指定字段中提取数值中的最小值。
  这些值可以从文档中的特定数字字段中提取,也可以由提供的脚本生成。

  来看一个例子(获取已销售汽车中价格最低的信息):

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": {
"price_min": {
"min": {
"field": "price"
}
}
}
}

  返回结果如下:

1
2
3
4
5
6
7
8
{
...
"aggregations" : {
"price_min" : {
"value" : 10000.0
}
}
}

missing 参数

  missing参数定义应如何处理缺少值的文档。默认情况下,这些文档将被忽略,但也可以将它们视为具有值。

1
2
3
4
5
6
7
8
9
10
11
12
POST /cars/_search
{
"size": 0,
"aggs": {
"price_avg": {
"avg": {
"field": "price",
"missing": 1000.0
}
}
}
}

Sum Aggregation

  总和聚合:计算从聚合文档指定字段中提取数值的总和值。
  这些值可以从文档中的特定数字字段中提取,也可以由提供的脚本生成。

  来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": {
"price_sum": {
"sum": {
"field": "price"
}
}
}
}

  返回结果如下:

1
2
3
4
5
6
7
8
{
...
"aggregations" : {
"price_sum" : {
"value" : 212000.0
}
}
}

missing 参数

  missing参数定义应如何处理缺少值的文档。默认情况下,这些文档将被忽略,但也可以将它们视为具有值。

Value Count Aggregation

  计数聚合:统计 Field 不为 null 的文档数。
  其类似于如下 SQL:

1
2
3
SELECT COUNT(*)
FROM t
WHERE field IS NOT NULL

  来看一个例子(获取已销售汽车的制造商数量):

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": {
"car_make": {
"value_count": {
"field": "make"
}
}
}
}

  返回结果如下:

1
2
3
4
5
6
7
8
{
...
"aggregations" : {
"car_sum" : {
"value" : 8
}
}
}

missing 参数

  missing参数定义应如何处理缺少值的文档。默认情况下,这些文档将被忽略,但也可以将它们视为具有值。

Stats Aggregation

  统计聚合:汇总 Avg、Max、Min、Sum、Value 5 种聚合的结果。

  示例如下:

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": {
"price_stat": {
"stats": {
"field": "price"
}
}
}
}

  返回结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
...
"aggregations" : {
"price_stat" : {
"count" : 8,
"min" : 10000.0,
"max" : 80000.0,
"avg" : 26500.0,
"sum" : 212000.0
}
}
}

missing 参数

  missing参数定义应如何处理缺少值的文档。默认情况下,这些文档将被忽略,但也可以将它们视为具有值。

Extended Stats Aggregation

  扩展统计聚合,在Stats Aggregation基础上再加以下 4 个计算指标:

  • sum_of_squares
  • variance
  • std_deviation
  • std_deviation_bounds

  示例如下:

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": {
"price_stat": {
"extended_stats": {
"field": "price"
}
}
}
}

  返回结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
...
"aggregations" : {
"price_stat" : {
"count" : 8,
"min" : 10000.0,
"max" : 80000.0,
"avg" : 26500.0,
"sum" : 212000.0,
"sum_of_squares" : 9.194E9,
"variance" : 4.47E8,
"std_deviation" : 21142.374511865975,
"std_deviation_bounds" : {
"upper" : 68784.74902373194,
"lower" : -15784.74902373195
}
}
}
}

missing 参数

  missing参数定义应如何处理缺少值的文档。默认情况下,这些文档将被忽略,但也可以将它们视为具有值。

String Stats Aggregation

  字符串统计聚合,用于计算keyword类型字段的以下指标:

  • count:非空字段数
  • min_length:最短term的长度
  • max_length:最长term长度
  • avg_length:所有terms的平均长度
  • entropy:通过聚合收集的所有术语计算出的值。香农熵量化了该领域所包含的信息量。它是一个非常有用的度量一个数据集的广泛属性,如多样性,相似性,随机性等

  示例如下:

1
2
3
4
5
6
7
8
9
10
11
POST /cars/_search
{
"size": 0,
"aggs": {
"make_string_stats": {
"string_stats": {
"field": "make"
}
}
}
}

  返回结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
...
"aggregations" : {
"make_string_stats" : {
"count" : 8,
"min_length" : 2,
"max_length" : 4,
"avg_length" : 3.0,
"entropy" : 3.584962500721156
}
}
}

Cardinality Aggregation

  基数聚合,计算Field Value有多少种不同的值。
  基数聚合类似于 SQL:

1
2
SELECT COUNT(DISTINCT name) AS name
FROM *

  假设您正在索引商店销售额,并希望计算匹配查询的销售产品的唯一数量:

1
2
3
4
5
6
7
8
9
10
POST /sales/_search?size=0
{
"aggs" : {
"type_count" : {
"cardinality" : {
"field" : "type"
}
}
}
}

  返回结果如下:

1
2
3
4
5
6
7
8
{
...
"aggregations" : {
"type_count" : {
"value" : 3
}
}
}

Geo Bounds Aggregation

  暂未使用,后续研究。

Geo Centroid Aggregation

  暂未使用,后续研究。

Percentiles Aggregation

  暂未使用,后续研究。

Percentile Ranks Aggregation

  暂未使用,后续研究。

Scripted Metric Aggregation

  暂未使用,后续研究。

Top Hits Aggregation

  暂未使用,后续研究。

Pipeline Aggregations

  Pipeline Aggregations,即管道聚合。
  管道聚合可以二次聚合其他聚合,比如将桶和度量进行二次聚合。

聚合原理—— Doc Values

  聚合使用一个叫doc values的数据结构(在 Doc Values 介绍 里简单介绍)。 Doc values 可以使聚合更快、更高效并且内存友好,所以理解它的工作方式对我们帮助会很大。
  为什么产生并使用 Doc values 呢?
  Doc values 的存在是因为倒排索引只对某些操作是高效的
  倒排索引的优势在于查找包含某个项的文档,而对于从另外一个方向的相反操作并不高效,比如确定哪些项是否存在单个文档里,但是聚合却刚好需要这种次级的访问模式。

  对于以下倒排索引:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Term      Doc_1   Doc_2   Doc_3
------------------------------------
brown | X | X |
dog | X | | X
dogs | | X | X
fox | X | | X
foxes | | X |
in | | X |
jumped | X | | X
lazy | X | X |
leap | | X |
over | X | X | X
quick | X | X | X
summer | | X |
the | X | | X
------------------------------------

  如果我们想要获得所有包含 brown 的文档的词的完整列表,我们会创建如下查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET /my_index/_search
{
"query" : {
"match" : {
"body" : "brown"
}
},
"aggs" : {
"popular_terms": {
"terms" : {
"field" : "body"
}
}
}
}

  查询部分简单又高效:倒排索引是根据项来排序的,所以我们首先在词项列表中找到 brown ,然后扫描所有列,找到包含 brown 的文档,因此我们可以快速看到 Doc_1Doc_2 包含 brown 这个 token。

  但是对于聚合部分,由于我们需要找到 Doc_1Doc_2 里所有唯一的词项,用倒排索引做这件事情代价很高——我们会迭代索引里的每个词项并收集 Doc_1Doc_2 列里面 token。这很慢而且难以扩展,且随着词项和文档的数量增加,执行时间也会增加。

  Doc values 通过转置两者间的关系来解决这个问题
  倒排索引将词项映射到包含它们的文档,而 Doc Values 将文档映射到它们包含的词项:

1
2
3
4
5
6
Doc      Terms
-----------------------------------------------------------------
Doc_1 | brown, dog, fox, jumped, lazy, over, quick, the
Doc_2 | brown, dogs, foxes, in, lazy, leap, over, quick, summer
Doc_3 | dog, dogs, fox, jumped, over, quick, the
-----------------------------------------------------------------

  当数据被转置之后,想要收集到 Doc_1Doc_2 的唯一 token ,便会变得非常容易:

  • 首先获得每个文档行
  • 其次获取所有的词项
  • 最后求两个集合的并集

  因此,搜索和聚合是相互紧密缠绕的:

  • 搜索操作使用倒排索引查找文档
  • 聚合操作收集和聚合 doc values 里的数据

参考

0%