聚合查询
聚合查询实际上是一种统计和计算,按照官方文档的解释共有四类
- Metric(指标): 指标分析类型,如计算最大值、最小值、平均值等等 (对桶内的文档进行聚合分析的操作)
- Bucket(桶): 分桶类型,类似SQL中的GROUP BY语法 (满足特定条件的文档的集合)
- Pipeline(管道): 管道分析类型,基于上一级的聚合分析结果进行在分析
- Matrix(矩阵): 矩阵分析类型(聚合是一种面向数值型的聚合,用于计算一组文档字段中的统计信息)
一般聚合的写法如下:
"aggregations" : { "<aggregation_name>" : { <!--聚合的名字 --> "<aggregation_type>" : { <!--聚合的类型 --> <aggregation_body> <!--聚合体:对哪些字段进行聚合 --> } [,"meta" : { [<meta_data_body>] } ]? <!--元 --> [,"aggregations" : { [<sub_aggregation>]+ } ]? <!--在聚合里面在定义子聚合 --> } [,"<aggregation_name_2>" : { ... } ]* <!--聚合的名字 --> }
其中aggregations 也可简写为 aggs,虽然Elasticsearch有四种聚合方式,但在一般实际开发中,用到的比较多的就是Metric和Bucket。其实从功能上只有前两类:一类是进行数学计算聚合使用的【max、min、avg、sum、stats 】,类似sql里的函数,也就是ES里的统计聚合,另一类则是进行分组聚合使用的,类似sql里的group,也就是ES里的桶聚合。剩余的两类就是程序上的辅助聚合方式,一种是管道二次处理,一种似是分桶同时处理统计计算。
桶(bucket)
- 简单来说桶就是满足特定条件的文档的集合。*
- 当聚合开始被执行,每个文档里面的值通过计算来决定符合哪个桶的条件,如果匹配到,文档将放入相应的桶并接着开始聚合操作。
- 桶也可以被嵌套在其他桶里面。
指标(metric)
- 桶能让我们划分文档到有意义的集合,但是最终我们需要的是对这些桶内的文档进行一些指标的计算。分桶是一种达到目的地的手段:它提供了一种给文档分组的方法来让我们可以计算感兴趣的指标。
- 大多数指标是简单的数学运算(如:最小值、平均值、最大值、汇总),这些是通过文档的值来计算的。
接下来我们分别实践一下。
桶聚合
我们先来看一个桶聚合,例如我们想要进行如下聚合【按照年龄聚合,0-10,10-20,20-30】,查询语句如下:
{ "size": 0, "aggs": { "user_age_info": { "range": { "field": "age", "ranges": [ { "from": 0, "to": 10 }, { "from": 11, "to": 20 }, { "from": 21, "to": 30 } ] } } } }
返回结果为:
{ "took": 2, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "user_age_info": { "buckets": [ { "key": "0.0-10.0", "from": 0.0, "to": 10.0, "doc_count": 4 }, { "key": "11.0-20.0", "from": 11.0, "to": 20.0, "doc_count": 7 }, { "key": "21.0-30.0", "from": 21.0, "to": 30.0, "doc_count": 4 } ] } } }
指标聚合
指标聚合分为两种,分别是单值分析和多值分析,各自有几个查询关键字:
- 单值分析只输出一个分析结果:min,max,avg,sum,cardinality
- 多值分析输出多个分析结果:stats, extended_stats, percentile, percentile_rank, top hits
统计计算这里我们分别使用一下以上几种方式进行分析,最后再结合分组进行一些复合的统计计算查询。
单值分析
我们分别带入场景来进行单值指标的聚合分析。
max
这里我们想查询,所有员工信息中,【年龄最大的员工】,则查询参数为:
{ "size":0, "aggs" : { "max_age" : { "max" : { "field" : "age" } } } }
返回为:
{ "took": 15, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "max_age": { "value": 28.0 } } }
min
这里我们想查询,所有员工信息中,【年龄最小的员工】,则查询参数为:
{ "size":0, "aggs" : { "min_age" : { "min" : { "field" : "age" } } } }
返回结果为:
{ "took": 6, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "min_age": { "value": 8.0 } } }
avg
这里我们想查询,所有员工信息中,【平均年龄是多少】,则查询参数为:
{ "size":0, "aggs" : { "avg_age" : { "avg" : { "field" : "age" } } } }
返回结果为:
{ "took": 19, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "avg_age": { "value": 18.0 } } }
sum
这里我们想查询,所有员工信息中,【年龄总和为多大】,则查询参数为:
{ "size":0, "aggs" : { "sun_age" : { "sum" : { "field" : "age" } } } }
返回结果为:
{ "took": 9, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "sun_age": { "value": 270.0 } } }
cardinality
cardinality 求唯一值,即不重复的字段有多少(相当于mysql中的distinct),我们现在查询所有员工信息共有多少种年龄,请求参数为:
{ "size":0, "aggs" : { "age_count" : { "cardinality" : { "field" : "age" } } } }
返回值为:
{ "took": 22, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "sex_count": { "value": 3 } } }
共有8、18、28三种年龄
多值分析
多值分析包括以下关键字stats,extended_stats,percentile,percentile_rank,top hits,我们一个一个讨论下
stats
我想统计所有员工年龄的各种单值分析状态,可以使用stats,请求参数如下:
{ "size":0, "aggs" : { "age_stats" : { "stats" : { "field" : "age" } } } }
返回值为:
{ "took": 3, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "age_stats": { "count": 15, "min": 8.0, "max": 28.0, "avg": 18.0, "sum": 270.0 } } }
Percentiles
对指定字段的值按从小到大累计每个值对应的文档数的占比,返回指定占比比例对应的值。默认按照[ 1, 5, 25, 50, 75, 95, 99 ]来统计,我们想统计各个年龄百分区间内员工的数量:
{ "size":0, "aggs" : { "age_percentiles" : { "percentiles" : { "field" : "age" } } } }
返回结果为:
{ "took": 15, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "age_percentiles": { "values": { "1.0": 8.0, "5.0": 8.0, "25.0": 10.5, "50.0": 18.0, "75.0": 25.5, "95.0": 28.0, "99.0": 28.0 } } } }
可以理解为99%的员工都小于等于28岁。这个按百分值进行强制分布
Percentile Ranks
上面是通过百分比求文档值,这里通过文档值求百分比,请求参数为:
{ "size":0, "aggs" : { "age_percentile_ranks" : { "percentile_ranks" : { "field" : "age","values" : [8, 18,28] } } } }
返回值为:
{ "took": 11, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "age_percentile_ranks": { "values": { "8.0": 20.0, "18.0": 66.66666666666666, "28.0": 100.0 } } } }
age<=8岁的占比20%,感觉两种百分比的形式还是不是非常准确的,关于查询的准确性我们之后再另行讨论。
分桶指标复合分析
这里我们来依据年龄段进行,分析,想要进行如下聚合【按照年龄聚合,0-20,20-40】,并且对每个年龄段内的年龄进行平均值计算。
{ "size": 0, "aggs": { "user_age_info": { "range": { "field": "age", "ranges": [ { "from": 0, "to": 20 }, { "from": 21, "to": 40 } ] }, "aggs": { "age_avg": { "avg": { "field": "age" } } } } } }
返回值为:
{ "took": 7, "timed_out": false, "_shards": { "total": 3, "successful": 3, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 15, "relation": "eq" }, "max_score": null, "hits": [] }, "aggregations": { "user_age_info": { "buckets": [ { "key": "0.0-20.0", "from": 0.0, "to": 20.0, "doc_count": 11, "age_avg": { "value": 14.363636363636363 } }, { "key": "21.0-40.0", "from": 21.0, "to": 40.0, "doc_count": 4, "age_avg": { "value": 28.0 } } ] } } }
0到20岁之间共4个8岁,7个11岁,平均值为14.36岁。总而言之,桶聚合一般关键字为keyword,用来分类。
总结
以上就是周一到周四,四天的早上起大早的成果,将近3万字的成果,分别为普通模式、复合模式以及聚合模式,当然更复杂的将这几种融合起来的查询方式也是有的,等待后续的探索和学习啦。