跳至主要內容

Elasticsearch 聚合

钝悟...大约 11 分钟数据库搜索引擎数据库Elasticsearch数据库搜索引擎数据库Elasticsearch聚合

Elasticsearch 聚合

Elasticsearch 是一个分布式的全文搜索引擎,索引和搜索是 Elasticsearch 的基本功能。事实上,Elasticsearch 的聚合(Aggregations)功能也十分强大,允许在数据上做复杂的分析统计。Elasticsearch 提供的聚合分析功能主要有指标聚合(metrics aggregations)桶聚合(bucket aggregations)管道聚合(pipeline aggregations)矩阵聚合(matrix aggregations) 四大类,管道聚合和矩阵聚合官方说明是在试验阶段,后期会完全更改或者移除,这里不再对管道聚合和矩阵聚合进行讲解。

聚合的具体结构

所有的聚合,无论它们是什么类型,都遵从以下的规则。

  • 使用查询中同样的 JSON 请求来定义它们,而且你是使用键 aggregations 或者是 aggs 来进行标记。需要给每个聚合起一个名字,指定它的类型以及和该类型相关的选项。
  • 它们运行在查询的结果之上。和查询不匹配的文档不会计算在内,除非你使用 global 聚集将不匹配的文档囊括其中。
  • 可以进一步过滤查询的结果,而不影响聚集。

以下是聚合的基本结构:

"aggregations" : { <!-- 最外层的聚合键,也可以缩写为 aggs -->
    "<aggregation_name>" : { <!-- 聚合的自定义名字 -->
        "<aggregation_type>" : { <!-- 聚合的类型,指标相关的,如 max、min、avg、sum,桶相关的 terms、filter 等 -->
            <aggregation_body> <!-- 聚合体:对哪些字段进行聚合,可以取字段的值,也可以是脚本计算的结果 -->
        }
        [,"meta" : {  [<meta_data_body>] } ]? <!-- 元 -->
        [,"aggregations" : { [<sub_aggregation>]+ } ]? <!-- 在聚合里面在定义子聚合 -->
    }
    [,"<aggregation_name_2>" : { ... } ]* <!-- 聚合的自定义名字 2 -->
}
  • 在最上层有一个 aggregations 的键,可以缩写为 aggs
  • 在下面一层,需要为聚合指定一个名字。可以在请求的返回中看到这个名字。在同一个请求中使用多个聚合时,这一点非常有用,它让你可以很容易地理解每组结果的含义。
  • 最后,必须要指定聚合的类型。

关于聚合分析的值来源,可以取字段的值,也可以是脚本计算的结果

但是用脚本计算的结果时,需要注意脚本的性能和安全性;尽管多数聚集类型允许使用脚本,但是脚本使得聚集变得缓慢,因为脚本必须在每篇文档上运行。为了避免脚本的运行,可以在索引阶段进行计算。

此外,脚本也可以被人可能利用进行恶意代码攻击,尽量使用沙盒(sandbox)内的脚本语言。

示例:查询所有球员的平均年龄是多少,并对球员的平均薪水加 188(也可以理解为每名球员加 188 后的平均薪水)。

POST /player/_search?size=0
{
  "aggs": {
    "avg_age": {
      "avg": {
        "field": "age"
      }
    },
    "avg_salary_188": {
      "avg": {
        "script": {
          "source": "doc.salary.value + 188"
        }
      }
    }
  }
}

指标聚合

指标聚合(又称度量聚合)主要从不同文档的分组中提取统计数据,或者,从来自其他聚合的文档桶来提取统计数据。

这些统计数据通常来自数值型字段,如最小或者平均价格。用户可以单独获取每项统计数据,或者也可以使用 stats 聚合来同时获取它们。更高级的统计数据,如平方和或者是标准差,可以通过 extended stats 聚合来获取。

Max Aggregation

Max Aggregation 用于最大值统计。例如,统计 sales 索引中价格最高的是哪本书,并且计算出对应的价格的 2 倍值,查询语句如下:

GET /sales/_search?size=0
{
  "aggs" : {
    "max_price" : {
      "max" : {
        "field" : "price"
      }
    },
    "max_price_2" : {
      "max" : {
        "field" : "price",
        "script": {
          "source": "_value * 2.0"
        }
      }
    }
  }
}

指定的 field,在脚本中可以用 _value 取字段的值

聚合结果如下:

{
  ...
  "aggregations": {
    "max_price": {
      "value": 188.0
    },
    "max_price_2": {
      "value": 376.0
    }
  }
}

Min Aggregation

Min Aggregation 用于最小值统计。例如,统计 sales 索引中价格最低的是哪本书,查询语句如下:

GET /sales/_search?size=0
{
  "aggs" : {
    "min_price" : {
      "min" : {
        "field" : "price"
      }
    }
  }
}

聚合结果如下:

{
  ...
  "aggregations": {
    "min_price": {
      "value": 18.0
    }
  }
}

Avg Aggregation

Avg Aggregation 用于计算平均值。例如,统计 exams 索引中考试的平均分数,如未存在分数,默认为 60 分,查询语句如下:

GET /exams/_search?size=0
{
  "aggs" : {
    "avg_grade" : {
      "avg" : {
        "field" : "grade",
        "missing": 60
      }
    }
  }
}

如果指定字段没有值,可以通过 missing 指定默认值;若未指定默认值,缺失该字段值的文档将被忽略(计算)

聚合结果如下:

{
  ...
  "aggregations": {
    "avg_grade": {
      "value": 78.0
    }
  }
}

除了常规的平均值聚合计算外,elasticsearch 还提供了加权平均值的聚合计算,详情参见 Elasticsearch 指标聚合之 Weighted Avg Aggregationopen in new window

Sum Aggregation

Sum Aggregation 用于计算总和。例如,统计 sales 索引中 type 字段中匹配 hat 的价格总和,查询语句如下:

GET /exams/_search?size=0
{
  "query" : {
    "constant_score" : {
      "filter" : {
        "match" : { "type" : "hat" }
      }
    }
  },
  "aggs" : {
    "hat_prices" : {
      "sum" : { "field" : "price" }
    }
  }
}

聚合结果如下:

{
  ...
  "aggregations": {
    "hat_prices": {
      "value": 567.0
    }
  }
}

Value Count Aggregation

Value Count Aggregation 可按字段统计文档数量。例如,统计 books 索引中包含 author 字段的文档数量,查询语句如下:

GET /books/_search?size=0
{
  "aggs" : {
    "doc_count" : {
      "value_count" : { "field" : "author" }
    }
  }
}

聚合结果如下:

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

Cardinality Aggregation

Cardinality Aggregation 用于基数统计,其作用是先执行类似 SQL 中的 distinct 操作,去掉集合中的重复项,然后统计去重后的集合长度。例如,在 books 索引中对 language 字段进行 cardinality 操作可以统计出编程语言的种类数,查询语句如下:

GET /books/_search?size=0
{
  "aggs" : {
    "all_lan" : {
      "cardinality" : { "field" : "language" }
    },
    "title_cnt" : {
      "cardinality" : { "field" : "title.keyword" }
    }
  }
}

假设 title 字段为文本类型(text),去重时需要指定 keyword,表示把 title 作为整体去重,即不分词统计

聚合结果如下:

{
  ...
  "aggregations": {
    "all_lan": {
      "value": 8
    },
    "title_cnt": {
      "value": 18
    }
  }
}

Stats Aggregation

Stats Aggregation 用于基本统计,会一次返回 count、max、min、avg 和 sum 这 5 个指标。例如,在 exams 索引中对 grade 字段进行分数相关的基本统计,查询语句如下:

GET /exams/_search?size=0
{
  "aggs" : {
    "grades_stats" : {
      "stats" : { "field" : "grade" }
    }
  }
}

聚合结果如下:

{
  ...
  "aggregations": {
    "grades_stats": {
      "count": 2,
      "min": 50.0,
      "max": 100.0,
      "avg": 75.0,
      "sum": 150.0
    }
  }
}

Extended Stats Aggregation

Extended Stats Aggregation 用于高级统计,和基本统计功能类似,但是会比基本统计多出以下几个统计结果,sum_of_squares(平方和)、variance(方差)、std_deviation(标准差)、std_deviation_bounds(平均值加/减两个标准差的区间)。在 exams 索引中对 grade 字段进行分数相关的高级统计,查询语句如下:

GET /exams/_search?size=0
{
  "aggs" : {
    "grades_stats" : {
      "extended_stats" : { "field" : "grade" }
    }
  }
}

聚合结果如下:

{
  ...
  "aggregations": {
    "grades_stats": {
      "count": 2,
      "min": 50.0,
      "max": 100.0,
      "avg": 75.0,
      "sum": 150.0,
      "sum_of_squares": 12500.0,
      "variance": 625.0,
      "std_deviation": 25.0,
      "std_deviation_bounds": {
        "upper": 125.0,
        "lower": 25.0
      }
    }
  }
}

Percentiles Aggregation

Percentiles Aggregation 用于百分位统计。百分位数是一个统计学术语,如果将一组数据从大到小排序,并计算相应的累计百分位,某一百分位所对应数据的值就称为这一百分位的百分位数。默认情况下,累计百分位为 [ 1, 5, 25, 50, 75, 95, 99 ]。以下例子给出了在 latency 索引中对 load_time 字段进行加载时间的百分位统计,查询语句如下:

GET latency/_search
{
  "size": 0,
  "aggs" : {
    "load_time_outlier" : {
      "percentiles" : {
        "field" : "load_time"
      }
    }
  }
}

需要注意的是,如上的 load_time 字段必须是数字类型

聚合结果如下:

{
  ...
  "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 参数指定百分位,如下:

GET latency/_search
{
  "size": 0,
  "aggs" : {
    "load_time_outlier" : {
      "percentiles" : {
        "field" : "load_time",
        "percents": [60, 80, 95]
      }
    }
  }
}

Percentiles Ranks Aggregation

Percentiles Ranks Aggregation 与 Percentiles Aggregation 统计恰恰相反,就是想看当前数值处在什么范围内(百分位), 假如你查一下当前值 500 和 600 所处的百分位,发现是 90.01 和 100,那么说明有 90.01 % 的数值都在 500 以内,100 % 的数值在 600 以内。

GET latency/_search
{
  "size": 0,
    "aggs" : {
      "load_time_ranks" : {
        "percentile_ranks" : {
          "field" : "load_time",
          "values" : [500, 600]
        }
      }
  }
}

同样 load_time 字段必须是数字类型

返回结果大概类似如下:

{
  ...
  "aggregations": {
    "load_time_ranks": {
      "values" : {
        "500.0": 90.01,
        "600.0": 100.0
      }
    }
  }
}

可以设置 keyed 参数为 true,将对应的 values 作为桶 key 一起返回,默认是 false

GET latency/_search
{
  "size": 0,
  "aggs": {
    "load_time_ranks": {
      "percentile_ranks": {
        "field": "load_time",
        "values": [500, 600],
        "keyed": true
      }
    }
  }
}

返回结果如下:

{
  ...
  "aggregations": {
    "load_time_ranks": {
      "values": [
        {
          "key": 500.0,
          "value": 90.01
        },
        {
          "key": 600.0,
          "value": 100.0
        }
      ]
    }
  }
}

桶聚合

bucket 可以理解为一个桶,它会遍历文档中的内容,凡是符合某一要求的就放入一个桶中,分桶相当于 SQL 中的 group by。从另外一个角度,可以将指标聚合看成单桶聚合,即把所有文档放到一个桶中,而桶聚合是多桶型聚合,它根据相应的条件进行分组。

种类描述/场景
词项聚合(Terms Aggregation)用于分组聚合,让用户得知文档中每个词项的频率,它返回每个词项出现的次数。
差异词项聚合(Significant Terms Aggregation)它会返回某个词项在整个索引中和在查询结果中的词频差异,这有助于我们发现搜索场景中有意义的词。
过滤器聚合(Filter Aggregation)指定过滤器匹配的所有文档到单个桶(bucket),通常这将用于将当前聚合上下文缩小到一组特定的文档。
多过滤器聚合(Filters Aggregation)指定多个过滤器匹配所有文档到多个桶(bucket)。
范围聚合(Range Aggregation)范围聚合,用于反映数据的分布情况。
日期范围聚合(Date Range Aggregation)专门用于日期类型的范围聚合。
IP 范围聚合(IP Range Aggregation)用于对 IP 类型数据范围聚合。
直方图聚合(Histogram Aggregation)可能是数值,或者日期型,和范围聚集类似。
时间直方图聚合(Date Histogram Aggregation)时间直方图聚合,常用于按照日期对文档进行统计并绘制条形图。
空值聚合(Missing Aggregation)空值聚合,可以把文档集中所有缺失字段的文档分到一个桶中。
地理点范围聚合(Geo Distance Aggregation)用于对地理点(geo point)做范围统计。

Terms Aggregation

Terms Aggregation 用于词项的分组聚合。最为经典的用例是获取 X 中最频繁(top frequent)的项目,其中 X 是文档中的某个字段,如用户的名称、标签或分类。由于 terms 聚集统计的是每个词条,而不是整个字段值,因此通常需要在一个非分析型的字段上运行这种聚集。原因是, 你期望“big data”作为词组统计,而不是“big”单独统计一次,“data”再单独统计一次。

用户可以使用 terms 聚集,从分析型字段(如内容)中抽取最为频繁的词条。还可以使用这种信息来生成一个单词云。

{
  "aggs": {
    "profit_terms": {
      "terms": { // terms 聚合 关键字
        "field": "profit",
        ......
      }
    }
  }
}

在 terms 分桶的基础上,还可以对每个桶进行指标统计,也可以基于一些指标或字段值进行排序。示例如下:

{
  "aggs": {
    "item_terms": {
      "terms": {
        "field": "item_id",
        "size": 1000,
        "order":[{
          "gmv_stat": "desc"
        },{
          "gmv_180d": "desc"
        }]
      },
      "aggs": {
        "gmv_stat": {
          "sum": {
            "field": "gmv"
          }
        },
        "gmv_180d": {
          "sum": {
            "script": "doc['gmv_90d'].value*2"
          }
        }
      }
    }
  }
}

返回的结果如下:

{
  ...
  "aggregations": {
    "hospital_id_agg": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 260,
      "buckets": [
        {
          "key": 23388,
          "doc_count": 18,
          "gmv_stat": {
            "value": 176220
          },
          "gmv_180d": {
            "value": 89732
          }
        },
        {
          "key": 96117,
          "doc_count": 16,
          "gmv_stat": {
            "value": 129306
          },
          "gmv_180d": {
            "value": 56988
          }
        },
        ...
      ]
    }
  }
}

默认情况下返回按文档计数从高到低的前 10 个分组,可以通过 size 参数指定返回的分组数。

Filter Aggregation

Filter Aggregation 是过滤器聚合,可以把符合过滤器中的条件的文档分到一个桶中,即是单分组聚合。

{
  "aggs": {
    "age_terms": {
      "filter": {"match":{"gender":"F"}},
      "aggs": {
        "avg_age": {
          "avg": {
            "field": "age"
          }
        }
      }
    }
  }
}

Filters Aggregation

Filters Aggregation 是多过滤器聚合,可以把符合多个过滤条件的文档分到不同的桶中,即每个分组关联一个过滤条件,并收集所有满足自身过滤条件的文档。

{
  "size": 0,
  "aggs": {
    "messages": {
      "filters": {
        "filters": {
          "errors": { "match": { "body": "error" } },
          "warnings": { "match": { "body": "warning" } }
        }
      }
    }
  }
}

在这个例子里,我们分析日志信息。聚合会创建两个关于日志数据的分组,一个收集包含错误信息的文档,另一个收集包含告警信息的文档。而且每个分组会按月份划分。

{
  ...
  "aggregations": {
    "messages": {
      "buckets": {
        "errors": {
          "doc_count": 1
        },
        "warnings": {
          "doc_count": 2
        }
      }
    }
  }
}

Range Aggregation

Range Aggregation 范围聚合是一个基于多组值来源的聚合,可以让用户定义一系列范围,每个范围代表一个分组。在聚合执行的过程中,从每个文档提取出来的值都会检查每个分组的范围,并且使相关的文档落入分组中。注意,范围聚合的每个范围内包含 from 值但是排除 to 值。

{
  "aggs": {
    "age_range": {
      "range": {
        "field": "age",
          "ranges": [{
            "to": 25
          },
          {
            "from": 25,
            "to": 35
          },
          {
            "from": 35
          }]
        },
        "aggs": {
          "bmax": {
            "max": {
              "field": "balance"
            }
          }
        }
      }
    }
  }
}

返回结果如下:

{
  ...
  "aggregations": {
    "age_range": {
      "buckets": [{
        "key": "*-25.0",
        "to": 25,
        "doc_count": 225,
        "bmax": {
          "value": 49587
        }
      },
      {
        "key": "25.0-35.0",
        "from": 25,
        "to": 35,
        "doc_count": 485,
        "bmax": {
          "value": 49795
        }
      },
      {
        "key": "35.0-*",
        "from": 35,
        "doc_count": 290,
        "bmax": {
          "value": 49989
        }
      }]
    }
  }
}

参考资料

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.7