ElasticSearch全文搜索引擎之Aggregation聚合查询(结合Kibana)

一、简介
前面一篇文章已经详细介绍了查询相关的 API,但是当时并没有总结关于Aggregation 聚合查询这一方面的内容,本篇文章单独对聚合查询做一个总结。

聚合查询提供了功能可以分组并统计你的数据。理解聚合最简单的方式就是可以把它粗略的看做 SQL 的 GROUP BY 操作和 SQL 的聚合函数。

ElasticSearch 中常用的聚合有两种:

metric(度量)聚合:度量类型聚合主要针对的 number 类型的数据,需要 ES 做比较多的计算工作,类似于关系型数据库的组函数操作;
bucketing(桶)聚合:划分不同的“桶”,将数据分配到不同的“桶”里。非常类似 sql 中的 group 语句的含义,类似于关系型数据库的分组操作;
关于聚合查询的官方文档地址(es7.6 版本):https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations.html#search-aggregations

ES 中的聚合 API 如下:

"aggregations" : {  // 表示聚合操作,可以使用 aggs 替代
    "<aggregation_name>" : {  // 聚合名,可以是任意的字符串。用做响应的 key,便于快速取得正确的响应数据。
        "<aggregation_type>" : {   // 聚合类别,就是各种类型的聚合,如 min 等
            <aggregation_body>  // 聚合体,不同的聚合有不同的 body
        }
        [,"meta" : {  [<meta_data_body>] } ]?   
        [,"aggregations" : { [<sub_aggregation>]+ } ]?   // 嵌套的子聚合,可以有 0 或多个
    }
    [,"<aggregation_name_2>" : { ... } ]*  // 另外的聚合,可以有 0 或多个
}

二、数据准备

为了后面演示聚合查询,我们首先需要准备一些数据,批量插入测试数据:

# 批量插入测试数据
POST /user/info/_bulk
{"index":{"_id":1}}
{"name":"zs","realname":"张三","age":10,"birthday":"2018-12-27","salary":1000.0,"address":"广州市天河区科韵路 50 号"}
{"index":{"_id":2}}
{"name":"ls","realname":"李四","age":20,"birthday":"2017-10-20","salary":2000.0,"address":"广州市天河区珠江新城"}
{"index":{"_id":3}}
{"name":"ww","realname":"王五","age":30,"birthday":"2016-03-15","salary":3000.0,"address":"广州市天河区广州塔"}
{"index":{"_id":4}}
{"name":"zl","realname":"赵六","age":40,"birthday":"2003-04-19","salary":4000.0,"address":"广州市海珠区"}
{"index":{"_id":5}}
{"name":"tq","realname":"田七","age":50,"birthday":"2001-08-11","salary":5000.0,"address":"广州市天河区网易大厦"}

插入完成后,查看索引数据:

ElasticSearch 全文搜索引擎之 Aggregation 聚合查询(结合 Kibana)

可见,数据成功插入,下面我们分别对度量聚合、桶聚合进行详细的介绍。

三、度量(metric)聚合
Avg Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-metrics-avg-aggregation.html

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

假设数据由代表用户工资的文件组成,我们可以将他们的平均工资为:

POST /user/info/_search?size=0
{
  "aggs": {
    "avg_salary": {
      "avg": {
        "field": "salary"
      }
    }
  }
}

上述聚合计算所有文档的平均级别。聚合类型为 avg,字段设置定义计算平均值的文档的数字字段。以上将返回以下内容:

.............
  "aggregations" : {
    "avg_salary" : {
      "value" : 3000.0
    }
  }
 

可以看到,平均工资查询出来为 3000 元。

聚合的名称(上面的 avg_salary)还充当键,通过它可以从返回的响应中检索聚合结果。

Max Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-metrics-max-aggregation.html

最大值查询,跟踪并返回从聚合文档中提取的数值的最大值。这些值可以从文档中的特定数值字段中提取,也可以由提供的脚本生成。

如:查询员工的最高工资

POST /user/info/_search?size=0
{
  "aggs": {
    "max_salary": {
      "max": {
        "field": "salary"
      }
    }
  }
}

以上将返回以下内容:

..........
  "aggregations" : {
    "max_salary" : {
      "value" : 5000.0
    }
  }

可以看到,成功查询出最高工资为 5000 元。

Min Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-metrics-min-aggregation.html

最小值查询,跟踪并返回从聚合文档中提取的数值中的最小值。

如:查询员工最低工资

POST /user/info/_search?size=0
{
  "aggs": {
    "min_salary": {
      "min": {
        "field": "salary"
      }
    }
  }
}
以上查询返回如下结果:
........
  "aggregations" : {
    "min_salary" : {
      "value" : 1000.0
    }
  }

可以看到,成功查询到最低工资 1000 元。

Sum Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-metrics-sum-aggregation.html

求和聚合查询,对从聚合的文档中提取的数值进行汇总。

如,统计所有员工的总工资

POST /user/info/_search?size=0
{
  "aggs": {
    "sum_salary": {
      "sum": {
        "field": "salary"
      }
    }
  }
}

以上查询返回如下结果:

......
  "aggregations" : {
    "sum_salary" : {
      "value" : 15000.0
    }
  }

可以看到,成功查询总工资为 15000 元。

Stats Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-metrics-stats-aggregation.html

统计查询,一次性统计出某个字段上的常用统计值。返回的统计信息包括:最小值,最大值,总和,计数和平均。

POST /user/info/_search?size=0
{
  "aggs": {
    "price_stats": {
      "stats": {
        "field": "salary"
      }
    }
  }
}

以上查询返回如下结果:

......
  "aggregations" : {
    "price_stats" : {
      "count" : 5,
      "min" : 1000.0,
      "max" : 5000.0,
      "avg" : 3000.0,
      "sum" : 15000.0
    }
  }

可以看到,stats 聚合查询同时返回了我们前面介绍了一些 max、min、sum、avg 等信息。

Value Count Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-metrics-valuecount-aggregation.html

统计某字段有值的文档数。

POST /user/info/_search?size=0
{
  "aggs": {
    "price_value_count": {
      "value_count": {
        "field": "salary"
      }
    }
  }
}

如上查询下面结果:

......
  "aggregations" : {
    "price_value_count" : {
      "value" : 5
    }
  }
 

还有一些其他的聚合查询,如:

Cardinality Aggregation :值去重计数

Extended Stats Aggregation:高级统计,比 stats 多 4 个统计结果: 平方和、方差、标准差、平均值加/减两个标准差的区间。

Geo Bounds Aggregation:求文档集中的地理位置坐标点的范围

Geo Centroid Aggregation:求地理位置中心点坐标值

Percentiles Aggregation:占比百分位对应的值统计

Percentile Ranks Aggregation:统计值小于等于指定值的文档占比

感兴趣的小伙伴可以跟着官网示例,自己手动去练练手,试一下。

三、bucketing(桶)聚合
Range Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-bucket-range-aggregation.html

基于多桶值源的聚合,使用户能够定义一组范围——每个范围表示一个桶。在聚合过程中,将根据每个 bucket 范围和相关/匹配文档的“bucket”检查从每个文档提取的值。

注意,此聚合包含每个范围的 from 值并排除 to 值。

自定义区间范围的聚合,我们可以自己手动地划分区间,ES 会根据划分出来的区间将数据分配不同的区间上去。

如: 统计 0-20 岁,20-40 岁,40~60 岁各个区间段的用户人数

POST /user/info/_search
{
  "aggs": {
    "age_ranges_count": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 0,
            "to": 20
          },
          {
            "from": 20,
            "to": 40
          },
          {
            "from": 40,
            "to": 60
          }
        ]
      }
    }
  }
}

以上查询返回下面结果:

...........
  "aggregations" : {
    "age_ranges_count" : {
      "buckets" : [
        {
          "key" : "0.0-20.0",
          "from" : 0.0,
          "to" : 20.0,
          "doc_count" : 1
        },
        {
          "key" : "20.0-40.0",
          "from" : 20.0,
          "to" : 40.0,
          "doc_count" : 2
        },
        {
          "key" : "40.0-60.0",
          "from" : 40.0,
          "to" : 60.0,
          "doc_count" : 2
        }
      ]
    }
  }
 

 

可以看到,成功统计出各个我们指定的区间段的用户人数。如果第一个区间开始值和最后一个区间结束值不想指定的话,可以不用写 from 和 to,如下:

GET /_search
{
    "aggs" : {
        "price_ranges" : {
            "range" : {
                "field" : "price",
                "ranges" : [
                    { "to" : 100.0 },
                    { "from" : 100.0, "to" : 200.0 },
                    { "from" : 200.0 }
                ]
            }
        }
    }
}

响应结果就类似下面:*-100.0、200.0-*,使用*代表某个区间值。

{
    ...
    "aggregations": {
        "price_ranges" : {
            "buckets": [
                {
                    "key": "*-100.0",
                    "to": 100.0,
                    "doc_count": 2
                },
                {
                    "key": "100.0-200.0",
                    "from": 100.0,
                    "to": 200.0,
                    "doc_count": 2
                },
                {
                    "key": "200.0-*",
                    "from": 200.0,
                    "doc_count": 3
                }
            ]
        }
    }
}

Terms Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-bucket-terms-aggregation.html

自定义分组依据 Term,对分组后的数据进行统计。

如:根据年龄分组,统计相同年龄的用户

POST /user/info/_search
{
  "aggs": {
    "age_count": {
      "terms": {
        "field": "age",
        "size": 3
      }
    }
  }
}

响应结果如下:

.....
"aggregations" : {
    "age_count" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 2,
      "buckets" : [
        {
          "key" : 10,
          "doc_count" : 1
        },
        {
          "key" : 20,
          "doc_count" : 1
        },
        {
          "key" : 30,
          "doc_count" : 1
        }
      ]
    }
  }

Date Range Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-bucket-daterange-aggregation.html

专用于日期值的范围聚合,专门针对 date 类型的字段。此聚合与 Range Aggregation 常规范围聚合的主要区别在于,可以用日期数学表达式表示 from 和 to 值,而且还可以指定返回 from 和 to 响应字段的日期格式。注意,此聚合包含每个范围的 from 值并排除 to 值。

now+10y:表示从现在开始的第 10 年。
now+10M:表示从现在开始的第 10 个月。
1990-01-10||+20y:表示从 1990-01-01 开始后的第 20 年,即 2010-01-01。
now/y:表示在年位上做舍入运算。
如: 统计生日在 2017 年、2018 年、2019 年的用户

POST /user/info/_search
{
  "aggs": {
    "birthday_count": {
      "date_range": {
        "field": "birthday",
        "format": "yyyy-MM-dd",
        "ranges": [
          {
            "from": "now/y-1y",
            "to": "now/y"
          },
          {
            "from": "now/y-2y",
            "to": "now/y-1y"
          },
          {
            "from": "now/y-3y",
            "to": "now/y-2y"
          }
        ]
      }
    }
  }
}

其中:

now/y:当前年的 1 月 1 日
now:当前时间
now/y-1y:当前年上一年的 1 月 1 日
响应结果:

......
"aggregations" : {
    "birthday_count" : {
      "buckets" : [
        {
          "key" : "2017-01-01-2018-01-01",
          "from" : 1.4832288E12,
          "from_as_string" : "2017-01-01",
          "to" : 1.5147648E12,
          "to_as_string" : "2018-01-01",
          "doc_count" : 1
        },
        {
          "key" : "2018-01-01-2019-01-01",
          "from" : 1.5147648E12,
          "from_as_string" : "2018-01-01",
          "to" : 1.5463008E12,
          "to_as_string" : "2019-01-01",
          "doc_count" : 1
        },
        {
          "key" : "2019-01-01-2020-01-01",
          "from" : 1.5463008E12,
          "from_as_string" : "2019-01-01",
          "to" : 1.5778368E12,
          "to_as_string" : "2020-01-01",
          "doc_count" : 0
        }
      ]
    }
  }

Histogram Aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-bucket-histogram-aggregation.html

直方图聚合,它将某个 number 类型字段等分成 n 份,统计落在每一个区间内的记录数。它与前面介绍的 Range 聚合非常像,只不过 Range 可以任意划分区间,而 Histogram 做等间距划分。既然是等间距划分,那么参数里面必然有距离参数,就是 interval 参数。

如:根据年龄间隔(20 岁)统计各个年龄段的员工总人数

POST /user/info/_search
{
  "aggs": {
    "age_histogram_count": {
      "histogram": {
        "field": "age",
        "interval": 20
      }
    }
  }
}

响应结果如下:

......
"aggregations" : {
    "age_histogram_count" : {
      "buckets" : [
        {
          "key" : 0.0,
          "doc_count" : 1
        },
        {
          "key" : 20.0,
          "doc_count" : 2
        },
        {
          "key" : 40.0,
          "doc_count" : 2
        }
      ]
    }
  }

Date histogram aggregation
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations-bucket-datehistogram-aggregation.html

日期直方图聚合,专门对时间类型的字段做直方图聚合。这种需求是比较常用见得的,我们在统计时,通常就会按照固定的时间断(1 个月或 1 年等)来做统计。

如:按年统计用户生日的总人数

POST /user/info/_search
{
  "aggs": {
    "birthday_data_histogram_count": {
      "date_histogram": {
        "field": "birthday",
        "interval": "year",
        "format": "yyyy-MM-dd"
      }
    }
  }
}

响应结果如下:

......
"aggregations" : {
    "birthday_data_histogram_count" : {
      "buckets" : [
        {
          "key_as_string" : "2001-01-01",
          "key" : 978307200000,
          "doc_count" : 1
        },
        {
          "key_as_string" : "2002-01-01",
          "key" : 1009843200000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2003-01-01",
          "key" : 1041379200000,
          "doc_count" : 1
        },
        {
          "key_as_string" : "2004-01-01",
          "key" : 1072915200000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2005-01-01",
          "key" : 1104537600000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2006-01-01",
          "key" : 1136073600000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2007-01-01",
          "key" : 1167609600000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2008-01-01",
          "key" : 1199145600000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2009-01-01",
          "key" : 1230768000000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2010-01-01",
          "key" : 1262304000000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2011-01-01",
          "key" : 1293840000000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2012-01-01",
          "key" : 1325376000000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2013-01-01",
          "key" : 1356998400000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2014-01-01",
          "key" : 1388534400000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2015-01-01",
          "key" : 1420070400000,
          "doc_count" : 0
        },
        {
          "key_as_string" : "2016-01-01",
          "key" : 1451606400000,
          "doc_count" : 1
        },
        {
          "key_as_string" : "2017-01-01",
          "key" : 1483228800000,
          "doc_count" : 1
        },
        {
          "key_as_string" : "2018-01-01",
          "key" : 1514764800000,
          "doc_count" : 1
        }
      ]
    }
  }

除了上述介绍的几种桶聚合查询,elasticsearch 官网还提供了很多其他各种各样的聚合查询,有兴趣的小伙伴们可以前往官网进行学习。

四、聚合查询嵌套使用
聚合操作是可以嵌套使用的。通过嵌套,可以使得 metric 类型的聚合操作作用在每一 bucket 上。我们可以使用 ES 的嵌套聚合操作来完成稍微复杂一点的统计功能。

如:统计每年中用户的最高工资

POST /user/info/_search
{
  "aggs": {
    "birthday_data_histogram_count": {
      "date_histogram": {
        "field": "birthday",
        "interval": "year",
        "format": "yyyy-MM-dd"
      },
      "aggs": {
        "max_salary": {
          "max": {
            "field": "salary"
          }
        }
      }
    }
  }
}

响应结果:

.....
"aggregations" : {
    "birthday_data_histogram_count" : {
      "buckets" : [
        {
          "key_as_string" : "2001-01-01",
          "key" : 978307200000,
          "doc_count" : 1,
          "max_salary" : {
            "value" : 5000.0
          }
        },
        {
          "key_as_string" : "2002-01-01",
          "key" : 1009843200000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2003-01-01",
          "key" : 1041379200000,
          "doc_count" : 1,
          "max_salary" : {
            "value" : 4000.0
          }
        },
        {
          "key_as_string" : "2004-01-01",
          "key" : 1072915200000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2005-01-01",
          "key" : 1104537600000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2006-01-01",
          "key" : 1136073600000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2007-01-01",
          "key" : 1167609600000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2008-01-01",
          "key" : 1199145600000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2009-01-01",
          "key" : 1230768000000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2010-01-01",
          "key" : 1262304000000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2011-01-01",
          "key" : 1293840000000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2012-01-01",
          "key" : 1325376000000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2013-01-01",
          "key" : 1356998400000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2014-01-01",
          "key" : 1388534400000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2015-01-01",
          "key" : 1420070400000,
          "doc_count" : 0,
          "max_salary" : {
            "value" : null
          }
        },
        {
          "key_as_string" : "2016-01-01",
          "key" : 1451606400000,
          "doc_count" : 1,
          "max_salary" : {
            "value" : 3000.0
          }
        },
        {
          "key_as_string" : "2017-01-01",
          "key" : 1483228800000,
          "doc_count" : 1,
          "max_salary" : {
            "value" : 2000.0
          }
        },
        {
          "key_as_string" : "2018-01-01",
          "key" : 1514764800000,
          "doc_count" : 1,
          "max_salary" : {
            "value" : 1000.0
          }
        }
      ]
    }
  }

可以看到,先通过 date_histogram 按照年分组,然后再通过嵌套 max 聚合查询统计出每年最高工资是多少。

如:求每个年龄区间段的工资总和

POST /user/info/_search
{
  "aggs": {
    "age_ranges_count": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 0,
            "to": 20
          },
          {
            "from": 20,
            "to": 40
          },
          {
            "from": 40,
            "to": 60
          }
        ]
      },
      "aggs": {
        "sum_salary": {
          "sum": {
            "field": "salary"
          }
        }
      }
    }
  }
}

响应结果:

......
"aggregations" : {
    "age_ranges_count" : {
      "buckets" : [
        {
          "key" : "0.0-20.0",
          "from" : 0.0,
          "to" : 20.0,
          "doc_count" : 1,
          "sum_salary" : {
            "value" : 1000.0
          }
        },
        {
          "key" : "20.0-40.0",
          "from" : 20.0,
          "to" : 40.0,
          "doc_count" : 2,
          "sum_salary" : {
            "value" : 5000.0
          }
        },
        {
          "key" : "40.0-60.0",
          "from" : 40.0,
          "to" : 60.0,
          "doc_count" : 2,
          "sum_salary" : {
            "value" : 9000.0
          }
        }
      ]
    }
  }

五、总结
本篇文章主要总结了 elasticsearch 提供的聚合查询,聚合查询类似于我们关系型数据库中的 group by 分组统计信息,这种需求很常见。文章通过详细的示例分别介绍了常见的几种度量聚合、桶聚合查询,在实际工作中,需要结合具体的业务查询需求灵活使用不同的聚合统计函数,更多详细使用方法可参见官网。希望对小伙伴们的学习有所帮助,由于笔者水平有限,文中可能存在有不对之处,还望指正,相互学习,一起进步!

参考文档:

https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-aggregations.html

https://www.cnblogs.com/wangzhuxing/p/9581947.html#_label1_2

 


来自:https://blog.csdn.net/Weixiaohuai/article/details/109003560

© 版权声明

☆ END ☆
喜欢就点个赞吧
点赞0 分享
图片正在生成中,请稍后...