抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

ElasticSearch 聚合查询

img

指标聚合

指标聚合比较简单,大多数都是对查询返回的数据进行简单的数值处理

Max

Max 聚合可以统计最大值,类似 MySQL 数据库中的 max 函数

基本查询

例如查询价格最高的书籍

1
2
3
4
5
6
7
8
9
10
GET books/_search
{
"aggs": {
"max_price": {
"max": {
"field": "price"
}
}
}
}

聚合结果在最下面,我们发现数据最大的值是269

image-20220810102629720

设置默认值

有些文档没有price字段,还可以使用 missing 来设置默认值

插入文档

插入的字段没有price

1
2
3
4
5
6
7
8
POST books/_doc
{
"name": "测试数据",
"publish" :"高等教育出版社",
"type" :"大学教材",
"author" :"大方哥",
"info": "高等教育出版社高等教育出版社高等教育出版社高等教育出版社高等教育出版社"
}
基本查询

我们下面就进行查询,使用 missing 参数,设置 price 空字段值为 1000

1
2
3
4
5
6
7
8
9
10
11
GET books/_search
{
"aggs": {
"max_price": {
"max": {
"field": "price",
"missing": 1000
}
}
}
}
忽略问题文档

有时候我们需要排除不存在price字段的数据,我们可以使用script进行处理,

基本查询

可以先通过 doc['price'].size()!=0 去判断文档是否有对应的属性

1
2
3
4
5
6
7
8
9
10
11
12
GET books/_search
{
"aggs": {
"max_price": {
"max": {
"script": {
"source": "if (doc['price'].size()!=0){doc.price.value}"
}
}
}
}
}

image-20220810103403652

Min

统计最小值,用法和 Max 基本一致:

基本查询
1
2
3
4
5
6
7
8
9
10
11
GET books/_search
{
"aggs": {
"min_price": {
"min": {
"field": "price",
"missing": 1000
}
}
}
}
忽略问题文档
1
2
3
4
5
6
7
8
9
10
11
12
GET books/_search
{
"aggs": {
"min_price": {
"min": {
"script": {
"source": "if (doc['price'].size()!=0){doc.price.value}"
}
}
}
}
}

Avg

统计平均值

基本查询

我们统计数据价格的平均值

1
2
3
4
5
6
7
8
9
10
GET books/_search
{
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}

书籍的平均价格是29.4元

image-20220810103712500

忽略问题文档
1
2
3
4
5
6
7
8
9
10
11
12
GET books/_search
{
"aggs": {
"avg_price": {
"avg": {
"script": {
"source": "if(doc['price'].size()!=0){doc.price.value}"
}
}
}
}
}

Sum

对字段求合

基本查询
1
2
3
4
5
6
7
8
9
10
GET books/_search
{
"aggs": {
"sum_price": {
"sum": {
"field": "price"
}
}
}
}
忽略问题文档
1
2
3
4
5
6
7
8
9
10
11
12
GET books/_search
{
"aggs": {
"sum_price": {
"sum": {
"script": {
"source": "if(doc['price'].size()!=0){doc.price.value}"
}
}
}
}
}

Cardinality

Cardinality 聚合用于基数统计,类似于 MySQL 中的 distinct count(0),去重后再计数。

注意事项

ElasticSearch 中 text 字段类型是分析型,默认不允许进行聚合操作,如果有聚合操作的需求,可以考虑以下两种方式:

  • 设置 text 字段类型的 fielddata 属性为 true。
  • 将字段类型或者字段的子域在设置成 keyword
设置fielddata

设置 text 字段类型的 fielddata 属性为 true

重置索引

因为 books 索引已经存在,我们要先删除,再新建索引设置 fielddata,导入数据后再进行 Cardinality 统计

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
DELETE books

PUT books
{
"mappings": {
"properties": {
"name":{
"type": "text",
"analyzer": "ik_max_word"
},
"publish":{
"type": "text",
"analyzer": "ik_max_word",
"fielddata": true
},
"type":{
"type": "text",
"analyzer": "ik_max_word"
},
"author":{
"type": "keyword"
},
"info":{
"type": "text",
"analyzer": "ik_max_word"
},
"price":{
"type": "double"
}
}
}
}
重新导入数据

到 bookdata.json 文件目录下,用 cmd 命令行工具,重新导入数据:

1
curl -XPOST "http://192.168.245.151:9200/books/_bulk?pretty" -H "content-type:application/json" --data-binary @bookdata.json
基础查询

现在就可以使用 cardinality 查询出版社的总数量

1
2
3
4
5
6
7
8
9
10
GET books/_search
{
"aggs": {
"publish_count": {
"cardinality": {
"field": "publish"
}
}
}
}

查询出来的数据是43

image-20220810105619561

主要事项

这种方式虽然可以进行聚合操作,但是无法满足精准聚合,因为 text 类型会进行分词。

​ 而且 fielddata 是动态创建到内存中,如果文档很多时,可能有动态创建慢,占内存等问题,所以推荐使用下面第二种方式

设置keyword

将字段类型或者字段的子域设置成 keyword

重置索引

同样需要删除索引再新建,这次将 publish 字段子域设置成 keyword

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
DELETE books

PUT books
{
"mappings": {
"properties": {
"name":{
"type": "text",
"analyzer": "ik_max_word"
},
"publish":{
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"size": {
"type": "keyword"
}
}
},
"type":{
"type": "text",
"analyzer": "ik_max_word"
},
"author":{
"type": "keyword"
},
"info":{
"type": "text",
"analyzer": "ik_max_word"
},
"price":{
"type": "double"
}
}
}
}
重新导入数据

到 bookdata.json 文件目录下,用 cmd 命令行工具,重新导入数据:

1
curl -XPOST "http://192.168.245.151:9200/books/_bulk?pretty" -H "content-type:application/json" --data-binary @bookdata.json
基础查询

再次使用 cardinality 查询出版社的总数量

1
2
3
4
5
6
7
8
9
10
GET books/_search
{
"aggs": {
"publish_count": {
"cardinality": {
"field": "publish"
}
}
}
}

Stats

stats 表示基本统计聚合,可以同时返回 minmaxsumcountavg 结果

基本查询
1
2
3
4
5
6
7
8
9
10
11
GET books/_search
{
"aggs": {
"stats_agg": {
"stats": {
"field": "price"
}
}
}
}

image-20220810110521000

Extends Stats

Extends Stats 表示高级统计聚合,比 stats 聚合返回更多的内容

基本查询
1
2
3
4
5
6
7
8
9
10
GET books/_search
{
"aggs": {
"extended_stats_agg": {
"extended_stats": {
"field": "price"
}
}
}
}

image-20220810110621523

Percentiles

Percentiles 是百分位数值统计,运用于统计学中:将一组数据从小到大排序,并计算相应的累计百分位,则某一百分位所对应数据的值就称为这一百分位的百分位数。

基本查询

文字解释看起来比较难理解,还是拿书籍价格举例,分别看一下 25%、50%、75%、100% 这四个百分位上的书籍价格:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
GET books/_search
{
"aggs": {
"percentiles_agg": {
"percentiles": {
"field": "price",
"percents": [
1,
5,
10,
15,
25,
50,
75,
95,
100
]
}
}
}
}

可以看到,中位数 50% 的书籍价格是 28 元,也就是说有一半的书籍价格比 28 元低,另一半比 28 元高,对应的 25%、75%、100% 也是类似。

image-20220810110840655

Value count

Value count 可以按照字段统计文档数量,该字段值为空 null 的文档会被丢弃

基本查询
1
2
3
4
5
6
7
8
9
10
11
GET books/_search
{
"aggs": {
"count": {
"value_count": {
"field": "price"
}
}
}
}

image-20220810111009909

桶聚合

Terms

Terms 用于分组聚合,例如,统计各个出版社出版的图书总数量

基本查询

统计各出版社图书总数量,并列出前五个

1
2
3
4
5
6
7
8
9
10
11
GET books/_search
{
"aggs": {
"bucket_terms": {
"terms": {
"field": "publish.size",
"size": 5
}
}
}
}

terms 分组聚合不能作用于 text 类型,这里我们用的是 keyword 类型的子域 publish.size

image-20220810111650692

价格统计

terms 分组聚合的基础上,还可以对每个桶进行指标聚合,统计不同出版社所出版的图书平均价格:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GET books/_search
{
"aggs": {
"bucket_terms": {
"terms": {
"field": "publish.size",
"size": 5
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}

image-20220810112008989

Filter

Filter 是过滤器聚合,可以将符合过滤器中条件的文档分到一个桶中,然后可以求其平均值

基本查询

例如查询书名中包含 java 的图书的平均价格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GET books/_search
{
"aggs": {
"bucket_filter": {
"filter": {
"term": {
"name": "java"
}
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}

image-20220810112300923

Filters

多过滤器聚合,过滤条件可以有多个

基本查询

例如查询书名中包含 java 或者 office 的图书的平均价格:

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
GET books/_search
{
"aggs": {
"bucket_filter": {
"filters": {
"filters": [
{
"term": {
"name": "java"
}
},
{
"term": {
"name": "office"
}
}
]
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}

image-20220810112650273

Range

按照范围聚合,在某一个范围内的文档数统计

基本查询

例如统计图书价格在 0-50、50-100、100-150、150以上的图书数量:

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
GET books/_search
{
"aggs": {
"bucket_range": {
"range": {
"field": "price",
"ranges": [
{
"to": 50
},
{
"from": 50,
"to": 100
},
{
"from": 100,
"to": 150
},
{
"from": 150
}
]
}
}
}
}

image-20220810112947053

出版社统计

基于上面的架构聚集结果,在统计出来每一个分桶中出版社的数量

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
GET books/_search
{
"aggs": {
"bucket_range": {
"range": {
"field": "price",
"ranges": [
{
"to": 50
},
{
"from": 50,
"to": 100
},
{
"from": 100,
"to": 150
},
{
"from": 150
}
]
},
"aggs": {
"publish_size": {
"cardinality": {
"field": "publish.size"
}
}
}
}
}
}

image-20220810113304386

Date Range

ange 聚合和 Date Range 聚合都可以可以统计日期,后者的优势在于可以使用日期表达式

插入数据

首先造一些测试数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PUT blog/_doc/1
{
"title":"java",
"date":"2018-12-30"
}
PUT blog/_doc/2
{
"title":"java",
"date":"2020-12-30"
}
PUT blog/_doc/3
{
"title":"java",
"date":"2022-10-30"
}
基本查询

统计一年前到一年后的微博数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GET blog/_search
{
"aggs": {
"bucket_date_range": {
"date_range": {
"field": "date",
"ranges": [
{
"from": "now-10M/M",
"to": "now+1y/y"
}
]
}
}
}
}

image-20220810114008893

Date Histogram

时间直方图聚

基本查询

例如统计各个月份的博客数量

1
2
3
4
5
6
7
8
9
10
11
GET blog/_search
{
"aggs": {
"bucket_date_his": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
}
}
}
}

image-20220810114139539

Missing

空值聚合

基本查询

统计所有没有 price 字段的文档:

1
2
3
4
5
6
7
8
9
10
GET books/_search
{
"aggs": {
"bucket_missing": {
"missing": {
"field": "price"
}
}
}
}

image-20220810114247411

Children

可以根据父子文档关系进行分桶

基本查询

查询子类型为 student 的文档数量

1
2
3
4
5
6
7
8
9
10
11
GET stu_class/_search
{
"aggs": {
"bucket_children": {
"children": {
"type": "student"
}
}
}
}

Geo Distance

对地理位置数据做统计

基本查询

例如分别统计 (34.288991865037524,108.9404296875) 坐标方圆 600KM 和 超过 600KM 的城市数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
GET geo/_search
{
"aggs": {
"bucket_geo": {
"geo_distance": {
"field": "location",
"origin": {
"lat": 34.288991865037524,
"lon": 108.9404296875
},
"unit": "km",
"ranges": [
{
"to": 600
},{
"from": 600
}
]
}
}
}
}

image-20220810114436154

IP Range

IP 地址范围聚合

准备数据

之前的 blog 索引没有设置 ip 字段,删掉重新设置一下

重置索引
1
2
3
4
5
6
7
8
9
10
11
12
DELETE blog

PUT blog
{
"mappings": {
"properties": {
"ip": {
"type": "ip"
}
}
}
}
插入数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PUT blog/_doc/1
{
"title":"java",
"date":"2018-12-30",
"ip":"127.0.0.1"
}
PUT blog/_doc/2
{
"title":"java",
"date":"2020-12-30",
"ip":"127.0.0.5"
}
PUT blog/_doc/3
{
"title":"java",
"date":"2022-10-30",
"ip":"127.0.0.10"
}

基本查询

例如查询指定 IP 地址的博客数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET blog/_search
{
"aggs": {
"bucket_ip_range": {
"ip_range": {
"field": "ip",
"ranges": [
{
"from": "127.0.0.5",
"to": "127.0.0.11"
}
]
}
}
}
}

image-20220810114748374

管道聚合

管道聚合相当于在之前聚合的基础上,进行再次聚合

Avg Bucket

计算聚合平均值

基本查询

统计每个出版社所出版图书的平均值,然后再统计所有出版社的平均值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
GET books/_search
{
"aggs": {
"book_count": {
"terms": {
"field": "publish.size",
"size": 3
},
"aggs": {
"book_avg": {
"avg": {
"field": "price"
}
}
}
},
"avg_book":{
"avg_bucket": {
"buckets_path": "book_count>book_avg"
}
}
}
}

image-20220810115100513

Max 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
GET books/_search
{
"aggs": {
"book_count": {
"terms": {
"field": "publish.size",
"size": 3
},
"aggs": {
"book_avg": {
"avg": {
"field": "price"
}
}
}
},
"max_book":{
"max_bucket": {
"buckets_path": "book_count>book_avg"
}
}
}
}

image-20220810135344523

Min Bucket

计算聚合最小值

基本查询

统计每个出版社所出版图书的平均值,然后再统计平均值中的最小值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
GET books/_search
{
"aggs": {
"book_count": {
"terms": {
"field": "publish.size",
"size": 3
},
"aggs": {
"book_avg": {
"avg": {
"field": "price"
}
}
}
},
"min_book":{
"min_bucket": {
"buckets_path": "book_count>book_avg"
}
}
}
}

image-20220810135500544

Sum 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
GET books/_search
{
"aggs": {
"book_count": {
"terms": {
"field": "publish.size",
"size": 3
},
"aggs": {
"book_avg": {
"avg": {
"field": "price"
}
}
}
},
"sum_book":{
"sum_bucket": {
"buckets_path": "book_count>book_avg"
}
}
}
}

image-20220810135615811

Stats 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
GET books/_search
{
"aggs": {
"book_count": {
"terms": {
"field": "publish.size",
"size": 3
},
"aggs": {
"book_avg": {
"avg": {
"field": "price"
}
}
}
},
"stats_book":{
"stats_bucket": {
"buckets_path": "book_count>book_avg"
}
}
}
}

image-20220810135709961

Extended Stats Bucket

基本查询

统计每个出版社所出版图书的平均值,然后再统计平均值的各种数据,比 Stats 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
GET books/_search
{
"aggs": {
"book_count": {
"terms": {
"field": "publish.size",
"size": 3
},
"aggs": {
"book_avg": {
"avg": {
"field": "price"
}
}
}
},
"extended_book":{
"extended_stats_bucket": {
"buckets_path": "book_count>book_avg"
}
}
}
}

image-20220810135816944

Percentiles 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
GET books/_search
{
"aggs": {
"book_count": {
"terms": {
"field": "publish.size",
"size": 3
},
"aggs": {
"book_avg": {
"avg": {
"field": "price"
}
}
}
},
"percentiles_book":{
"percentiles_bucket": {
"buckets_path": "book_count>book_avg"
}
}
}
}

image-20220810135913523

评论