Elasticsearch作为分布式搜索引擎,可支持各种数据类型(结构化/非结构化文本、数值等)的存储和快速查询,具有良好的可扩展性,可以支持不断增长的数据量。Elasticsearch不仅可以进行多种场景的数据查询,还提供了强大的聚合查询功能,可实现各种复杂的数据分析需求。 下面重点介绍ES中常用的聚合查询方法,并以系统中具体的功能实现为例,进行详细说明。
ES聚合分类概述
Bucket Aggergations
分桶聚合可以将文档按照一定规则划分为多个集合,并统计出各个集合中的文档个数。分桶聚合可以分级使用,每个桶中的文档可以再次进行桶聚合(sub-aggregations)。 分桶聚合包括很多种类型(Adjacency matrix aggregation, Chiildren, composite, Date histogram, Filter,Sampler, Terms等),对应不同的分桶策略。每种类型根据需要,可能定义单个桶、固定数量的多个桶,或统计过程中动态创建桶。
Metrics Aggregations
可以基于文档数据,计算各种统计指标,计算数据可以是文档中的已有字段,也可以为脚本的执行结果。包括Avg,Cardinality,Geo-bounds,Max,Rate,Scripted metric,Top hits 等多种类型。 数值的聚合统计是一种特殊的metrics aggregation,输出结果为单个值或多个值。可作为分桶聚合的子级聚合(sub-aggregations),部分分桶聚合支持使用各桶中的统计指标对桶进行排序。但是metrics aggregations下面不能再包含子级聚合操作(sub-aggregations)。
Pipeline Aggregations
管道聚合根据其他聚合结果,而不是索引中的文档数据进行计算,计算结果会添加到结果树中。包含很多类型,都可以概括为两大类:
Parent
通过父级聚合输出结果,计算出新的分桶结果,并加入到现有结果中。
Sibling
利用同级聚合的输出结果,计算出新的结果,加入到结果中去,输出和输入的并集,作为最终的聚合结果。
ES聚合应用
下面以业务系统中的具体实现,举例说明一些常见的应用场景,及实现方法。
趋势图
查询语句
代码语言:javascript复制GET my-index/_search
{
"size": 0,
"query": {
"bool": {
"must": [
{
"range": {
"createdTime": {
"gte": "2022-11-10 00:00:00",
"lte": "2022-11-13 00:00:00"
}
}
},
{
"term": {
"accountId": 1223445
}
}
]
}
},
"aggs": {
"agg_name1": {
"date_histogram": {
"field": "createdTime",
"interval": "day",
"min_doc_count": 0,
"extended_bounds": {
"min": "2022-11-10",
"max": "2022-11-15"
}
}
}
}
}
说明
- 查询趋势数据使用聚合类型date_histogram,用interval字段设置时间间隔(minute, hour, day etc.)
- 限定统计数据的范围,可以使用query语句进行过滤;如果只对某个聚合统计进行数据过滤,或者对多个聚合统计限定不同的数据范围,可使用Filter aggregation。
- extended_bounds用来指定返回数据桶的范围,如果不指定,只返回有数据的桶,可以和min_doc_count配合使用。
- size 为返回数据中的文档数,默认10,如果仅需要聚合统计结果,可设置为0。
结果示例
代码语言:javascript复制{
"took": 4,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 276,
"max_score": 0,
"hits": []
},
"aggregations": {
"agg_name1": {
"buckets": [
{
"key_as_string": "2022-11-10 00:00:00",
"key": 1668038400000,
"doc_count": 1
},
{
"key_as_string": "2022-11-11 00:00:00",
"key": 1668124800000,
"doc_count": 65
},
{
"key_as_string": "2022-11-12 00:00:00",
"key": 1668211200000,
"doc_count": 48
},
{
"key_as_string": "2022-11-13 00:00:00",
"key": 1668297600000,
"doc_count": 148
},
{
"key_as_string": "2022-11-14 00:00:00",
"key": 1668384000000,
"doc_count": 0
},
{
"key_as_string": "2022-11-15 00:00:00",
"key": 1668470400000,
"doc_count": 0
}
]
}
}
}
分布图
查询语句
代码语言:javascript复制GET my_index/_search
{
"size": 0,
"query": {
"bool": {
"must": [
{
"range": {
"createdTime": {
"gte": "2022-11-15 00:00:00",
"lte": "2022-12-01 00:00:00"
}
}
},
{
"term": {
"accountId": 12345644
}
}
]
}
},
"aggs": {
"aggs_name1": {
"terms": {
"field": "ownerId",
"size": 2
},
"aggs": {
"aggs_sub_name1": {
"terms": {
"field": "leadsTouchTag",
"size": 10
}
}
}
}
}
}
说明
- 使用Terms aggregation可以统计文档分布情况,field用于指定分桶字段。
- aggs中的size用于指定返回的最大桶数,默认返回包含文档数最多的10个。最大不超过search.max_buckets设置。如果桶数不超过1000,可以考虑增加aggs.size的值。如果需要返回的桶数较大,考虑计算所需内存资源,及search.max_buckets的限制,推荐使用composite aggregation(使用前需对性能进行评估)。
- Terms aggregation可嵌套多层使用,可以实现图标中的多层级的统计。
- 注意:设置min_doc_count=0时需谨慎,查询时会进行全量数据的扫描,可能导致性能问题。设置后,query语句中的过滤条件仅用来统计有数据的分桶,ES需要进行全量扫描,来返回所有无数据(文档数为0)的分桶。
结果示例
代码语言:javascript复制{
"took": 8,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 468,
"max_score": 0,
"hits": []
},
"aggregations": {
"aggs_name1": {
"doc_count_error_upper_bound": 18,
"sum_other_doc_count": 399,
"buckets": [
{
"key": 24994363,
"doc_count": 35,
"aggs_sub_name1": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 0,
"doc_count": 17
},
{
"key": 3,
"doc_count": 14
},
{
"key": 1,
"doc_count": 3
},
{
"key": 2,
"doc_count": 1
}
]
}
},
{
"key": 24427834,
"doc_count": 34,
"aggs_sub_name1": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 3,
"doc_count": 16
},
{
"key": 0,
"doc_count": 15
},
{
"key": 1,
"doc_count": 3
}
]
}
}
]
}
}
}
指标统计及嵌套聚合
查询语句
代码语言:javascript复制GET my_index/_search
{
"size": 0,
"query": {
"bool": {
"must": [
{
"range": {
"createdTime": {
"gte": "2022-11-01 00:00:00",
"lte": "2022-12-01 00:00:00"
}
}
},
{
"term": {
"accountId": 312353212
}
}
]
}
},
"aggs": {
"agg_name1": {
"terms": {
"field": "ownerId",
"size": 3
},
"aggs": {
"aggs_nested_name1": {
"nested": {
"path": "callStatInfo"
},
"aggs": {
"aggs_sub_name1": {
"stats": {
"field": "callStatInfo.totalCallOutDuration"
}
},
"aggs_sub_name2": {
"avg": {
"field": "callStatInfo.totalCallOutNum"
}
}
}
}
}
}
}
}
说明
- 使用Metric aggregations进行指标计算:max和avg等类型,用于计算单个指标值;stats可以同时计算多个指标值。
- 对多个字段进行聚合统计,可以并列定义多个aggs(例如:agg_sub_name1, agg_sub_name2,可以在任意层级)
- 对于嵌套文档,需要使用nested agg进行统计,在path中指定Nested类型字段的名称,在nested agg中可定义多种对嵌套字段的聚合统计。
结果示例
代码语言:javascript复制{
"took": 6,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 898,
"max_score": 0,
"hits": []
},
"aggregations": {
"agg_name1": {
"doc_count_error_upper_bound": 26,
"sum_other_doc_count": 728,
"buckets": [
{
"key": 24994363,
"doc_count": 58,
"aggs_nested_name1": {
"doc_count": 32,
"aggs_sub_name1": {
"count": 32,
"min": 0,
"max": 207,
"avg": 38.5,
"sum": 1232
},
"aggs_sub_name2": {
"value": 2.4375
}
}
},
{
"key": 24427834,
"doc_count": 57,
"aggs_nested_name1": {
"doc_count": 28,
"aggs_sub_name1": {
"count": 28,
"min": 0,
"max": 182,
"avg": 56.75,
"sum": 1589
},
"aggs_sub_name2": {
"value": 2.1785714285714284
}
}
},
{
"key": 22858878,
"doc_count": 55,
"aggs_nested_name1": {
"doc_count": 0,
"aggs_sub_name1": {
"count": 0,
"min": null,
"max": null,
"avg": null,
"sum": null
},
"aggs_sub_name2": {
"value": null
}
}
}
]
}
}
}
结论
ES提供了强大的聚合查询功能,可以实现复杂的数据查询统计,且表现出良好的性能。业务系统,如果数据量不是特别大的话,进行数据的实时统计分析,使用ES也是不错的选择。