ElasticSearch 提供的聚合分析功能有指标聚合(metrics aggregations)、桶聚合(bucket aggregations)、管道聚合(pipeline aggregations)和矩阵聚合(matrix aggregations)四大类。
聚合方式
聚合可以有多种用法。
直接聚合
直接聚合指的是聚合时的 DSL 没有 query 子句,是直接对索引内的所有文档进行聚合。
POST /hotel/_search
{
"size": 0,
"aggs": {
...
}
}
先查询后聚合
参加聚合的文档必须匹配 query 查询后进行聚合。
GET /hotel/_search
{
"size": 0,
"query": { //指定查询query逻辑
...
},
"aggs": {
...
}
}
前过滤器
有时需要对聚合条件进一步地过滤,但是又不能影响当前的查询条件。例如用户进行酒店搜索时的搜索条件是天津的酒店,但是聚合时需要将非满房的酒店平均价格进行聚合并展示给用户。此时不能变更用户的查询条件,需要在聚合子句中添加过滤条件。
GET /hotel/_search
{
"size": 0,
"query": { //指定查询的query逻辑
"term": {
"city": "天津"
}
},
"aggs": {
"my_agg": {
"filter": { //指定过滤器逻辑
"term": {
"full_room": false
}
},
"aggs": { //指定聚合逻辑
"my_avg": {
"avg": {
"field": "price"
}
}
}
}
}
}
后过滤器
在有些场景中,需要根据条件进行数据查询,但是聚合的结果集不受影响。例如在酒店搜索场景中,用户的查询词为“假日”,此时应该展现全国各地标题中带有“假日”的酒店。但是用户身在北京,只希望给用户展示北京的酒店的平均价格,这时可以使用 ES 提供的后过滤器功能。该过滤器是在查询和聚合之后进行过滤的,因此它的过滤条件对聚合没有影响。
GET /hotel/_search
{
"size": 0,
"query": { //指定查询的query逻辑
"match": {
"title": "假日"
}
},
"post_filter": { //指定后过滤器逻辑
"term": {
"city": "北京"
}
},
"aggs": { //指定聚合逻辑
"my_agg": {
"avg": {
"field": "price",
"missing":200
}
}
}
}
指标聚合
输出单值聚合
计算值 | 说明 | 注意 |
---|---|---|
avg | 计算平均值 | |
max | 计算最大值 | |
min | 计算最小值 | |
sum | 计算加和值 | |
value_count | 统计某字段有值的文档数 | 如果判断的字段是数组类型,则 value_count 统计的是符合条件的所有文档中该字段数组中非空元素个数的总和,而不是数组的个数总和。因此在遇到 price 数组中有两个非空值元素时,count 值加 2。 |
cardinality | 某字段值去重计数 |
以 avg 为例
GET /hotel/_search
{
"size": 0, //设置size的意图是不显示命中的文档
"aggs": {
"my_agg": { //聚合名称
"avg": {
"field": "price" //计算文档的平均价格
}
}
}
}
输出多值聚合
计算值 | 说明 |
---|---|
stats | 计算平均值、最小值、最大值 |
percentiles | 根据百分比位数查询对应的数据信息,默认情况下会按照 [ 1, 5, 25, 50, 75, 95, 99 ] 进行返回 |
percentile_ranks | 根据数据信息查询其对应的百分比位数 |
桶聚合
聚合方式
单维度桶聚合
最简单的桶聚合是单维度桶聚合,指的是按照一个维度对文档进行分组聚合。在桶聚合时,聚合的桶也需要匹配,匹配的方式有 terms、filter、ranges 等。
GET /hotel/_search
{
"size": 0,
"aggs": {
"my_agg": {
"terms": {
//按照城市进行聚合
"field": "city"
}
}
}
}
多维度桶嵌套聚合
在某些业务需求中,不仅需要一个维度的桶聚合,而且还可能有多维度桶嵌套聚合的需求。例如在搜索酒店时,可能需要统计各个城市的满房和非满房状态下的酒店平均价格。ES 支持嵌套桶聚合,进行嵌套时,可以使用 aggs 子句进行子桶的继续嵌套,指标放在最里面的子桶内。
GET /hotel/_search
{
"size": 0,
"aggs": {
"group_city": { //多维度桶名称
"terms": {
"field": "city"
},
"aggs": { //单维度桶
"group_full_room": {
"terms": {
"field": "full_room"
},
"aggs": { //聚合指标
"my_sum": {
"avg": {
"field": "price",
"missing": 200
}
}
}
}
}
}
}
}
聚合类型
terms 聚合
根据匹配条件分桶。
GET /hotel/_search
{
"size": 0,
"aggs": {
"my_agg": {
"terms": {
//按照城市进行聚合
"field": "city"
}
}
}
}
ranges 聚合
ranges 聚合匹配的是数值字段,表示按照数值范围进行分组。用户可以在 ranges 中添加分组,每个分组用 from 和 to 表示分组的起止数值。注意该分组包含起始数值,不包含终止数值。
GET /hotel/_search
{
"size": 0,
"aggs": {
"my_agg": {
"range": {
"field": "price",
//多个范围桶
"ranges": [
{
"to": 200 //不指定from,默认from为0
},
{
"from": 200,
"to": 500
},
{
"from": 500 //不指定to,默认to为该字段最大值
}
]
}
}
}
}
histogram 聚合
histogram 直方图聚合
根据指定的间隔构造存储桶。 属于每个间隔的值将形成一个间隔存储桶。
GET /hotel/_search
{
"size": 0,
"query": {
"range": {
"createTime": {
"gte": "2022-12-21 00:00:00",
"lte": "2022-12-21 12:00:00"
}
}
},
"aggs": {
"histogram_agg": {
"histogram": {
"field": "money",
"interval": 200
}
}
}
}
上述分区理论上应为 0-200、200-400 … 没有目标落在0-200、200-400 区间内。 因此,第一个存储区从 400-600 间隔开始。 因此,值最小的文档将确定最小存储桶(最小 key 的存储桶)。 相应地,具有最高值的文档将确定最大存储桶(具有最高 key 的存储桶)。
我们可以使用 extended_bounds
设置来强制直方图聚合,以根据特定的最小值开始构建其存储桶,并继续构建存储桶直至达到最大值(即使不再有文档)。 假设有如下情况:
- 数据存在于 200-600 之间,
extended_bounds
设置为 0-800,展示结果为 0-800 - 数据存在于 0-600 之间,
extended_bounds
设置为 0-200,展示结果为 0-600
即其区间取值为数据区间与extended_bounds
设置区间的交集,因此在实际使用中,可以将查询过滤范围和extended_bounds
区间保持一致。
min_doc_count
也会影响存储桶的区间。有如下几种情况:
- 当设置为大于 0 时,使用
extended_bounds
,仅会输出满足最小文档计数的桶 - 当设计为 0 时,返回所有桶
date_histogram 时间直方图聚合
这个聚合类似于正常的直方图,但只能与日期或日期范围值一起使用。 与直方图聚合聚合的主要区别在于,可以使用日期/时间表达式指定间隔。 基于时间的数据需要特殊的支持,因为基于时间的间隔并不总是固定的长度。
GET /hotel/_search
{
"size": 0,
"query": {
"range": {
"createTime": {
"gte": "2022-12-21 00:00:00",
"lte": "2022-12-21 12:00:00"
}
}
},
"aggs": {
"histogram_agg": {
"date_histogram": {
"field": "createTime",
"fixed_interval": "1h"
}
}
}
}
fixed_interval (时间间隔) 的可用表达式:
- year(1y)年
- quarter(1q)季度
- month(1M)月份
- week(1w)星期
- day(1d)天
- hour(1h)小时
- minute(1m)分钟
- second(1s)秒
地理距离聚合
使用地理距离聚合,您可以定义一个原点和到该点的一组距离范围。然后,聚合将评估每个 geo_point 值到原点的距离,并确定文档属于哪个范围。如果文档的 geo_point 值与原点之间的距离落入该存储桶的距离范围内,则该文档被视为属于该存储桶。
GET /hotel/_search
{
"size": 0,
"aggs": {
"my_agg": {
"geo_distance": {
"field": "location",
"origin": { //指定聚合的中心点经纬度
"lat": 39.915143,
"lon": 116.4039
},
"unit": "km", //指定聚合时的距离计量单位
"ranges": [ //指定每一个聚合桶的距离范围
{
"to": 3
},
{
"from": 3,
"to":10
},
{
"from": 10
}
]
}
}
}
}
IP 范围聚合
Elasticsearch 还具有对 IP 范围的内置支持。 IP聚合的工作方式与其他范围聚合类似。
让我们为 IP 地址创建索引映射,以说明此聚合的工作方式:
PUT /ips
{
"mappings": {
"properties": {
"ip_addr": {
"type": "ip"
}
}
}
}
当索引中包含一些数据时,让我们创建一个IP范围聚合:
GET ips/_search
{
"size": 0,
"aggs": {
"ip_ranges": {
"ip_range": {
"field": "ip_addr",
"ranges": [
{
"to": "172.16.0.4"
},
{
"from": "172.16.0.4"
}
]
}
}
}
}
聚合排序
ES 对于聚合结果的默认排序规则有时并非是我们期望的。可以使用 ES 提供的 sort 子句进行自定义排序,有多种排序方式供用户选择:可以按照聚合后的文档计数的大小进行排序;可以按照聚合后的某个指标进行排序;还可以按照每个组的名称进行排序。
按文档计数排序
GET /hotel/_search
{
"size": 0,
"aggs": {
"group_city": {
"terms": {
"field": "city",
"order": { //按照文档计数进行升序排列
"_count": "asc"
}
}
}
}
}
按聚合指标排序
GET /hotel/_search
{
"size": 0,
"aggs": {
"group_city": {
"terms": {
"field": "city",
"order": { //按照聚合指标进行升序排列
"my_avg": "asc"
}
},
"aggs": {
"my_avg": { //定义聚合指标
"avg": {
"field": "price",
"missing": 200
}
}
}
}
}
按分组Key排序
GET /hotel/_search
{
"size": 0,
"aggs": {
"group_city": {
"terms": {
"field": "city",
"order": { //按照分桶key的自然顺序升序排列
"_key": "asc"
}
},
"aggs": {
"my_avg": { //定义聚合指标
"avg": {
"field": "price",
"missing": 200
}
}
}
}
}
}
聚合分页
Top hits 聚合
Top hits 聚合指的是聚合时在每个分组内部按照某个规则选出前 N 个文档进行展示。
GET /hotel/_search
{
"size": 0,
"query": {
"match": {
"title": "金都"
}
},
"aggs": {
"group_city": { //按照城市进行桶聚合
"terms": {
"field": "city"
},
"aggs": {
"my_avg": {
"top_hits": { //指定返回每个桶的前3个文档
"size": 3
}
}
}
}
}
}
Collapse 聚合
当在索引中有大量数据命中时,Top hits 聚合存在效率问题,并且需要用户自行排序。针对上述问题,ES 推出了 Collapse 聚合,即用户可以在 collapse 子句中指定分组字段,匹配 query 的结果按照该字段进行分组,并在每个分组中按照得分高低展示组内的文档。当用户在 query 子句外指定 from 和 size 时,将作用在 Collapse 聚合之后,即此时的分页是作用在分组之后的。
GET /hotel/_search
{
"from": 0, //指定分页的起始位置
"size": 5, //指定每页返回的数量
"query": { //指定查询的query逻辑
"match": {
"title": "金都"
}
},
"collapse": { //指定按照城市进行Collapse聚合
"field": "city"
}
}