聚合
类似于 DSL 查询表达式,聚合也有 可组合 的语法:独立单元的功能可以被混合起来提供你需要的自定义行为。这意味着只需要学习很少的基本概念,就可以得到几乎无尽的组合。
要掌握聚合,你只需要明白两个主要的概念:
桶(Buckets)
满足特定条件的文档的集合
指标(Metrics)
对桶内的文档进行统计计算
这就是全部了!每个聚合都是一个或者多个桶和零个或者多个指标的组合。翻译成粗略的SQL语句来解释吧:
SELECT COUNT(color)
FROM table
GROUP BY color
COUNT(color) 相当于指标。GROUP BY color 相当于桶。
桶在概念上类似于 SQL 的分组(GROUP BY),而指标则类似于 COUNT() 、 SUM() 、 MAX() 等统计方法。
聚合结构
"aggregations" : { "<aggregation_name>" : { "<aggregation_type>" : { <aggregation_body> } [,"meta" : { [<meta_data_body>] } ]? [,"aggregations" : { [<sub_aggregation>]+ } ]? } [,"<aggregation_name_2>" : { ... } ]* }
可以缩写
"aggs" : { "<聚合的名称>" : { "<aggregation_type>" : { <aggregation_body> } [,"meta" : { [<meta_data_body>] } ]? [,"aggregations" : { [<sub_aggregation>]+ } ]? } [,"<聚合的名称_2>" : { ... } ]* }
单值聚合
一个简单平均数的例子:
POST /exams/_bulk { "index": {}} { "grade" : 100, "name" : "张三" } { "index": {}} { "grade" : 50, "name" : "李四" } { "index": {}} { "grade" : 60, "name" : "王五" }
查询平均分:
这是单值聚合,并没有group by的作用:
POST /exams/_search?size=0 { "aggs" : { "avg_grade" : { "avg" : { "field" : "grade" } } } }
结果:
"hits" : { "total" : { "value" : 3, "relation" : "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "avg_grade" : { "value" : 70.0 } }
缺失补值
例:录入一些缺少了分数的记录
POST /exams/_bulk { "index": {}} { "name" : "刘七" } { "index": {}} { "name" : "赵八" }
missing语句的含义是缺值按指定值补上(即50分)
POST /exams/_search?size=0 { "aggs" : { "grade_avg" : { "avg" : { "field" : "grade", "missing": 50 } } } }
结果如下:
"hits" : { "total" : { "value" : 5, "relation" : "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "grade_avg" : { "value" : 62.0 } }
基数聚合 Cardinality Aggregationedit
用途在于快速计数的统计,有点像Redis里的基数计算,聚合查询存在误差,在5%范围之内。
POST /products/_bulk { "index": {}} { "type" : 1, "name" : "书1" } { "index": {}} { "type" : 1, "name" : "书2" } { "index": {}} { "type" : 1, "name" : "书3" } { "index": {}} { "type" : 2, "name" : "食物1" } { "index": {}} { "type" : 1, "name" : "书4" } { "index": {}} { "type" : 1, "name" : "食物2" }
聚合:
POST /products/_search?size=0 { "aggs" : { "type_count" : { "cardinality" : { "field" : "type" } } } }
结果:
"aggregations" : { "type_count" : { "value" : 2 } }
注意,类型是存在2种。所以查出有2种产品。
扩展状态统计Extended Stats Aggregationedit
扩展的统计数据聚合是统计数据聚合的扩展版本,其中添加了额外的度量,如平方和、方差、标准偏差和标准偏差界限。
{ "size": 0, "aggs" : { "grades_stats" : { "extended_stats" : { "field" : "grade", "missing": 50 } } } }
结果:
"hits" : { "total" : { "value" : 5, "relation" : "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "grades_stats" : { "count" : 5, "min" : 50.0, "max" : 100.0, "avg" : 62.0, "sum" : 310.0, "sum_of_squares" : 21100.0, "variance" : 376.0, "std_deviation" : 19.390719429665317, "std_deviation_bounds" : { "upper" : 100.78143885933063, "lower" : 23.218561140669365 } }
最大最小值
POST /sales/_search?size=0 { "aggs" : { "min_price" : { "min" : { "field" : "price" } } } }
百分位数聚合Percentiles Aggregation
这是一种多值度量聚合,计算从聚合文档中提取的数值上的一个或多个百分位数。这些值可以从文档中的特定数字字段中提取,也可以由提供的脚本生成。
百分位数表示观察值的某个百分比出现的点。例如,95%是大于观察值95%的值,即最好的5%的数据值。百分位数通常用于查找异常值。在正态分布中,0.13%和99.87%代表三个与平均值的标准差。任何超出三个标准差的数据通常被视为异常。
当一系列的百分位数被检索到时,它们可以用来估计数据分布,并确定数据是否是倾斜的、双峰的等。
增加一些数据:
POST /exams/_bulk { "index": {}} { "grade" : 88, "name" : "a" } { "index": {}} { "grade" : 97, "name" : "b" } { "index": {}} { "grade" : 82, "name" : "c" } { "index": {}} { "grade" : 79, "name" : "d" } { "index": {}} { "grade" : 66, "name" : "e" } { "index": {}} { "grade" : 85, "name" : "f" }
聚合:
GET exams/_search { "size": 0, "aggs" : { "load_time_outlier" : { "percentiles" : { "field" : "grade" , "missing": 75 } } } }
结果
"aggregations" : { "load_time_outlier" : { "values" : { "1.0" : 50.0, "5.0" : 50.5, "25.0" : 68.25, "50.0" : 79.0, "75.0" : 87.25, "95.0" : 99.85, "99.0" : 100.0 } }
结论:99.85分以上就是5%最好的分数,考了79分以上就能力压50%的同学了,68.25 - 87.25 是正态分布最多的分数。
百分位等级聚合Percentile Ranks Aggregation
一种多值度量聚合,它计算多个百分位数,高于从聚合文档中提取的数值。这些值可以从文档中的特定数字字段中提取,也可以由提供的脚本生成。
比如想知道95分到100分在百分位的位置,可以用以下语句:
GET exams/_search { "size": 0, "aggs" : { "load_time_ranks" : { "percentile_ranks" : { "field" : "grade", "values" : [95, 100], "missing": 75 } } } }
结果:
95分超越了85.6的同学,100分就是最高分。
"aggregations" : { "load_time_ranks" : { "values" : { "95.0" : 85.6060606060606, "100.0" : 100.0 } }
统计聚合 Stats Aggregation
包含:
min, max, sum, count and avg.
POST /exams/_search?size=0 { "aggs" : { "grades_stats" : { "stats" : { "field" : "grade" } } } }
结果
"hits" : { "total" : { "value" : 11, "relation" : "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "grades_stats" : { "count" : 9, "min" : 50.0, "max" : 100.0, "avg" : 78.55555555555556, "sum" : 707.0 } }
合计聚合Sum Aggregation
一个单值度量聚合,它汇总从聚合文档中提取的数值。这些值可以从文档中的特定数字字段中提取,也可以由提供的脚本生成。
假设数据由代表销售记录的文档组成,我们可以将所有帽子的销售价格加起来,得出合计。
如果写成SQL,就是 select sum(price) from sales where type = 'hat' group by type
数据:
POST /sales/_bulk { "index": {}} { "price" : 88, "name" : "a", "type" : "hat" } { "index": {}} { "price" : 97, "name" : "b", "type" : "hat" } { "index": {}} { "price" : 82, "name" : "c", "type" : "cloth" } { "index": {}} { "price" : 102, "name" : "c", "type" : "cloth" }
聚合:
POST /sales/_search?size=0 { "query" : { "constant_score" : { "filter" : { "match" : { "type" : "hat" } } } }, "aggs" : { "hat_prices" : { "sum" : { "field" : "price" } } } }
结果
"aggregations" : { "hat_prices" : { "value" : 185.0 } }
平均值+过滤
POST /sales/_search?size=0 { "aggs" : { "t_shirts" : { "filter" : { "term": { "type": "hat" } }, "aggs" : { "avg_price" : { "avg" : { "field" : "price" } } } } } }
top_hits聚合
top_hits聚合器可以有效地用于通过bucket聚合器按特定字段对结果集进行分组。一个或多个bucket聚合器确定结果集被切片到的属性。
有点类似SQL语言的top + order by
POST /product/_bulk { "index": {}} { "type" : 1, "id" : 1, "price" : 5 } { "index": {}} { "type" : 1, "id" : 2, "price" : 15 } { "index": {}} { "type" : 1, "id" : 3, "price" : 40 } { "index": {}} { "type" : 1, "id" : 4, "price" : 29 } { "index": {}} { "type" : 1, "id" : 5, "price" : 50 } { "index": {}} { "type" : 2, "id" : 6, "price" : 100 } { "index": {}} { "type" : 3, "id" : 7, "price" : 50 } { "index": {}} { "type" : 3, "id" : 8, "price" : 60 } { "index": {}} { "type" : 3, "id" : 9, "price" : 108 } { "index": {}} { "type" : 3, "id" : 10, "price" : 65 }
查询不同类型的最前的一条记录(通过id排序),顺序为asc,倒序为desc
POST /product/_search?size=0 { "aggs": { "top_tags": { "terms": { "field": "type", "size": 2 }, "aggs": { "top_sales_hits": { "top_hits": { "sort": [ { "id": { "order": "asc" } } ], "_source": { "includes": [ "id", "price" ] }, "size" : 1 } } } } } }
结果
"aggregations" : { "top_tags" : { "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 1, "buckets" : [ { "key" : 1, "doc_count" : 5, "top_sales_hits" : { "hits" : { "total" : { "value" : 5, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "product", "_type" : "_doc", "_id" : "uL-3nW8BdESjTDDp3t4v", "_score" : null, "_source" : { "price" : 5, "id" : 1 }, "sort" : [ 1 ] } ] } } }, { "key" : 3, "doc_count" : 4, "top_sales_hits" : { "hits" : { "total" : { "value" : 4, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "product", "_type" : "_doc", "_id" : "vr-3nW8BdESjTDDp3t4v", "_score" : null, "_source" : { "price" : 50, "id" : 7 }, "sort" : [ 7 ] } ] } } }
值计数聚合 Value Count Aggregation
通常,此聚合器将与其他单值聚合一起使用。例如,当计算平均值时,一个人可能对计算平均值所依据的值的数量感兴趣。
POST /product/_search?size=0 { "aggs" : { "types_count" : { "value_count" : { "field" : "type" } } } }
计算type不为空的总条数为多少,可以看到之前的例子就是10条记录
"hits" : { "total" : { "value" : 10, "relation" : "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "types_count" : { "value" : 10 } }
中位数绝对偏差汇总 Median Absolute Deviation Aggregation
该单值聚集接近其搜索结果的中值绝对偏差。
中位数绝对偏差是可变性的度量。它是一个稳健的统计,这意味着它对于描述可能有异常值或可能不是正态分布的数据是有用的。对于这些数据,它可以比标准差更具描述性。
它被计算为每个数据点偏离整个样本中值的中值。也就是说,对于随机变量X,
MAD=median(∣X i−median(X)∣)
考虑数据集(1, 1, 2, 2, 4, 6, 9),它的中位数为2。数据点到2的绝对偏差为(1, 1, 0, 0, 2, 4, 7),该偏差列表的中位数为1(因为排序后的绝对偏差为(0, 0, 1, 1, 2, 4, 7))。所以该数据的绝对中位差为1。绝对中位差是一种统计离差的测量。而且,MAD是一种鲁棒统计量,比标准差更能适应数据集中的异常值。对于标准差,使用的是数据到均值的距离平方,所以大的偏差权重更大,异常值对结果也会产生重要影响。对于MAD,少量的异常值不会影响最终的结果。由于MAD是一个比样本方差或者标准差更鲁棒的度量,它对于不存在均值或者方差的分布效果更好,比如柯西分布。
GET /product/_search { "size": 0, "aggs": { "price_average": { "avg": { "field": "price" } }, "review_variability": { "median_absolute_deviation": { "field": "price" } } } }
可以看到结果,平均数是52.2,中位数偏差18,偏差有点大。
"aggregations" : { "price_average" : { "value" : 52.2 }, "review_variability" : { "value" : 18.0 } }