3.4 聚合
聚合(aggregations)可以看作是对查询结果的汇总,比如先查询出某个时间段的HTTP请求,然后统计每天的数据。aggregations真正强大的功能在于它能嵌套并实现多级汇总,每一种aggregation都有自己的目的和输出,它们通常分为四类:metric、bucket、pipeline和matrix。这一节主要对aggregations进行介绍,涉及bucket aggregations、metric aggregations、pipeline aggregations、matrix aggregations等。
在aggregations中会用到“bucket”的概念。所谓的“bucket”,是满足某个条件的文档集合,它和关系型数据库中的SQL语句中的“groupby”子句的作用相似(但又不一样)。例如,在校大学生要么属于本科生群的bucket,要么属于研究生群的bucket。
metric是为某个bucket中的文档计算得到的统计信息,它和关系型数据库中的SQL语句中的集函数如count()、max()等的作用相似。可见,aggregations聚合是由一个或多个buckets、零个或者多个metrics组合而成的统计结果。每个文档中的值会被计算来决定它们是否匹配了某些bucket的条件,如果匹配成功,那么该文档会被置入该bucket中。一个bucket也能够嵌套在其他的bucket中(即bucket是可以嵌套的)。对metrics而言,多数仅使用文档中的值进行简单计算。另外,aggregations也支持排序等属性,本书将会在相关例子中进行说明。
Matrix是一种在多字段上操作、从请求的文档字段中提取信息、返回矩阵结果的聚合方式。与bucket和metric不同的是,这一方式尚不支持script语句。
pipeline是将其他aggregations的输出及其关联度量进行聚合的方式。
3.4.1 metrics aggregations
metrics aggregations基于从文档中提取的值来计算指标。这些值通常使用字段数据,也可以使用脚本生成。这一节将对metrics aggregations中的min、max、sum、avg、stats、extended_stats、value_count等聚合进行介绍。
1.min、max、sum、avg聚合
在aggregations中可以方便地完成对最值、求和、均值等的统计。代码段3.33完成对指定字段的最小值聚合,示例结果如图3.8所示(当统计最大值时,将代码段3.33中的统计函数换成max即可,这里不再赘述)。
//代码段3.33:执行最小值聚合 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "min_size": { //aggs的名称 "min": { //统计最小值。当统计最大值时这里换成max即可。 "field": "log_size" //统计字段 } } } }'
图3.8 最小值聚合结果
类似地,求和、求平均统计只需在相应函数处写上sum及avg即可。代码段3.34完成对指定字段的平均值聚合,而针对返回HTTP响应大小size字段的实际效果如图3.9所示。
//代码段3.34:执行平均值聚合 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "avg_size": { //aggs的名称 "avg": { //统计平均值。当求和时这里换成sum即可。 "field": "log_size" } } } }'
图3.9 平均值聚合结果
在完成上述计算时,同样支持script脚本的使用。代码段3.35中使用了script,用来计算log_size的最大值,示例如图3.10所示。
//代码段3.35:执行平均值聚合 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "max_with_script": { "max": { "script": { "inline": "doc['log_size'].value", //在脚本中指定log_size "lang": "painless" } } } } }'
图3.10 使用script执行最大值聚合
2.stats、extended_stats聚合
stats aggregation是一个多值统计,返回值包括计数、最小值、最大值、平均值、求和等。代码段3.36演示了对相应字段进行多值统计的方法,针对类型文件log的返回结果如图3.11所示。同样,stats aggregation也支持使用脚本script和参数,不再赘述。
//代码段3.36:执行 stats聚合 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "log_size_stats": { "stats": { "field": "log_size" } } } }'
图3.11 stats聚合多值统计结果
extended_stats aggregation则是对一般的stats aggregation的功能扩展,可以在上述输出结果上添加平方和、方差和标准差等指标,参见代码段3.37,运行结果如图3.12所示。同样地,extended_stats aggregation也支持script和参数,不再赘述。
//代码段3.37:执行 extended_stats聚合 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "log_size_extended_stats": { "extended_stats": { "field": "log_size" } } } }'
图3.12 extended_stats聚合结果
3.value_count聚合
value_count aggregation是一种单值指标聚合,用来计算文档中某个字段的统计数量。首先执行代码段3.38,为text类型的custom_ip字段开启fielddata功能,然后执行代码段3.39对该字段执行value_count聚合,统计某服务器日志记载的访问次数,结果如图3.13所示。
//代码段3.38:为字段 custom_ip开启 fielddata功能 curl-XPUT localhost:9200/whale/_mapping/log-d'{ //这里使用PUT方法,索引/_mapping/类型 "properties": { "custom_ip": { //要开启fielddata功能的字段 "type": "keyword", //字段的类型,应与实际类型一致 "fielddata": true } } }'
//代码段3.39:执行 value_count聚合,统计访问次数
curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "custom_ip_count": { "value_count": { "field": "custom_ip" } } } }'
图3.13 访问次数统计结果
3.4.2 bucket aggregations
1.terms聚合
terms aggregation用于对指定字段的内容进行分布统计。聚合过程中会动态构建多个bucket,并对每个bucket计算出一个特定的值。代码段3.40实现了对某服务器上的不同访问用户使用的不同操作系统的统计,结果如图3.14所示。需要注意,代码中的os字段也需要开启fielddata,方法参照代码段3.38。
//代码段3.40:执行 terms聚合,统计访问者操作系统类型 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "usage": { "terms": { "field": "os" } } } }'
图3.14 对访问用户的操作系统的统计结果
2.range聚合
range aggregation基于多个bucket,每个bucket中定义一组范围,用于统计字段在某个范围的值。在聚合过程中,从每个文档提取的值将针对每个范围进行检查,并且返回“相关”或“匹配”的文档。代码段3.41实现了对log_size字段分别在三种范围内的数量统计,结果如图3.15所示。
//代码段3.41:执行三种不同范围的 range聚合 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "log_size_ranges": { "range": { "field": "log_size", "ranges": [ {"to":300}, {"from":222, "to":500}, {"from":222} ] } } } }'
图3.15 range聚合统计结果
可以使用keyed参数并将这个参数置为true,这样可以将返回值中的key作为这个JSON对象的名称。也可以使用key参数来自定义key的名称,如代码段3.42所示,它演示了针对类型文件log,分析字段log_size在三个不同区段内的统计值,结果如图3.16所示。同样,range aggregation也支持script和script参数,不再赘述。
//代码段3.42:使用 keyed参数,执行三种不同范围的 range聚合 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "log_size_ranges": { "range": { "field": "log_size", "keyed": true, "ranges": [ {"key": "short", "to":222}, {"key": "middle", "from":222, to":300}, {"key": "long", "from":300} ] } } } }'
图3.16 带有keyed参数的range聚合统计结果
3.histogram聚合
histogram aggregation是一种可以根据其返回值(针对数值型或日期型的字段)生成将来可生成柱状图的聚合数据。代码段3.43是计算log_size字段每间隔1000的统计分布情况,针对类型文件log的返回结果如图3.17所示。通过上面的聚合分析,可以得到log_size在各个区段的数量。在这个基础上,可以利用一些可视化软件对上述数据生成一张图表。
//代码段3.43:执行柱状图聚合,统计 log_size字段在固定间隔不同区段中的数量 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "log_size_histogram": { "histogram": { "field": "log_size", "interval":1000, //固定区间 1000 "order": { "_key": "desc" //降序排序 } } } } }'
图3.17 为柱状图执行聚合统计数据
也可以在histogram aggregations中添加子聚合来实现在现有的聚合中再次嵌套进行统计。代码段3.44实现了对每一个聚合中再次进行stats aggregation并按照子聚合中的最小值字段(即代码中的size_stats.min)进行降序排序的实现方法,针对类型文件log的实际运行效果如图3.18所示。
//代码段3.44:执行柱状图聚合,统计 log_size字段在固定间隔不同区段中的数量 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "outer_bucket": { "histogram": { "field": "log_size", "interval":500, "order": {"size_stats.min": "asc"} }, "aggs": { "size_stats": { "stats": {"field": "log_size"} } } } } }'
在上述例子中可以为中间结果取名,只需使用keyed参数并将其设置为true,即代码段3.44的field和interval语句中间加入语句"keyed":true并在末尾加逗号。这样,前端程序可以通过这个名字来取得相应的统计结果并进行展示,该聚合的查询结果如图3.19所示。请注意图3.18和图3.19的区别:图3.18的buckets结果是以数组方式给出的,而图3.19的buckets则是以键值对的形式给出的。
图3.18 执行嵌套的聚合
图3.19 对中间结果取名的histogramaggregations
4.date_histogram聚合
date_histogram aggregation是一个增强型的专门针对于日期型字段统计的histogram aggregation,它允许使用year、month、week、day、hour、minute等常量作为interval属性的取值。在代码段3.45中,实现了在field中填写一个日期类型的字段名称、在interval中写一个步长值、通过format参数设置时间格式的方法,结果如图3.20所示。
//代码段3.45:执行时间柱状图聚合,统计日志记录在不同时间段中的数量 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "logs_over_time": { "date_histogram": { "field": "timestamp", "interval": "3h", //步长为三小时 "format":"dd/MM/yyyy HH:mm:ss" //根据Elasticsearch中实际数据设置时间格式 } } } }'
图3.20 各个时间段的时间柱状图聚合结果
也可以在其中添加子aggregations(即嵌套)来实现更丰富的统计。代码段3.46实现了以3小时为步长单位,统计每小时各个状态码的出现次数。实际运行效果如图3.21所示。
//代码段3.46:执行嵌套的聚合,统计状态码在不同时间段中的数量 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "logs_over_time": { "date_histogram": { "field": "timestamp", "interval": "3h", //步长为 3小时 "format": "dd/MM/yyyy HH:mm:ss" //根据Elasticsearch中实际数据设置时间格式 }, "aggs": { "status_code": { "terms": { "field": "status_code", "order": {"_term": "asc"} } } } } } }'
图3.21 状态码出现次数的部分统计结果
5.date_range聚合
date_range aggregations是专门对于时间类型的字段进行区段统计的聚合。下面的代码段3.47介绍了其基本使用方法,在特定时间区段中日志数量的统计结果如图3.22所示。
//代码段3.47:执行特定时间范围内的聚合统计 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "range": { "date_range": { "field": "timestamp", "format": "dd/MM/yyyy HH:mm:ss", //根据Elasticsearch中实际数据设置时间格式 "ranges": [ {"to": "14/12/2016 14:00:00"}, //截止时间 {"from": "14/12/2016 07:00:00"} //起始时间 ] } } } }'
图3.22 指定时间段内的日志数量统计结果
6.filter聚合
类似于SQL语句中where子句的作用,filter aggregation可以为当前文档集合定义一个过滤条件来缩小现有的数据集,凡满足定义的过滤条件(filter)的文档(document)都会被放入这个bucket中。代码段3.48展示了对类型文件log中所有size字段大于300的文档进行平均值统计(均值统计是在嵌套的aggregations中实现的),相当于满足指定条件后再进行的统计。针对类型文件log的实际效果如图3.23所示。
//代码段3.48:执行带有过滤的聚合统计 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "filter_aggregation": { "filter": { "range": { "log_size": {"gt":300} //规定日志信息长度大于 300 } }, "aggs": { "avg_log_size": { "avg": {"field": "log_size"} //聚合中求平均值 } } } } }'
图3.23 带有过滤的聚合统计结果
3.4.3 pipeline aggregations
pipeline aggregations处理的对象是其他聚合的输出(而不是文档),这种聚合方式可向输出树添加信息。pipeline aggregations包含很多形式,能够处理不同的任务,大致分为两类:
(1)parent:接收其父聚合的输出,并计算出新的buckets或新的聚合来添加到现有的buckets中。
(2)sibling:接收同级聚合的输出,并计算出新的同级聚合。
pipeline aggregations一般无子聚合,但是它可以在buckets_path中引用另一个管道,这样pipeline aggregations就可以被链接在一起。例如,可以将两个计算导数的pipeline aggregations链接在一起以计算二阶导数。这一节将对pipeline aggregations中的min_bucket、max_bucket、sum_bucket、avg_bucket、stats_bucket、extended_stats_bucket等聚合进行介绍。
1.min_bucket、max_bucket、sum_bucket、avg_bucket聚合
min_bucket和max_bucket聚合是上文提到的聚合的同级聚合,这样的聚合以同级聚合中特定指标的最小值来识别buckets,并且输出bucket的值及其key。这里的指标必须为数字类型,同级聚合必须是支持多个buckets的聚合。代码段3.49实现了对每小时出现日志记录长度最小值的计算,结果如图3.24所示。
//代码段3.49:计算每小时出现的日志记录长度最小值 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "access_per_hour": { //外围聚合,统计每小时访问量 "date_histogram": { "field": "timestamp", "interval": "hour" }, "aggs": { //子聚合,统计log_size总数 "access": { "sum": {"field": "log_size"} } } }, "min_access_per_hour": { //管道聚合,链接上面两种聚合 "min_bucket": { "buckets_path": "access_per_hour>access" } } } }'
计算每小时出现日志记录长度最大值时,只需将代码段3.49中的min_bucket改为max_bucket即可(可以与聚合的名字一并修改),得到的结果如图3.25所示。另外sum_bucket和avg_bucket聚合也是这样的修改和执行方法,这里不再一一赘述。
图3.24 每小时日志记录长度最小值统计结果
图3.25 每小时日志记录长度最大值统计结果
2.stats_bucket、extended_stats_bucket聚合
与上面提到的四种管道聚合类似,stats_bucket聚合能够返回包含计数、最小值、最大值、平均值、求和的多值统计结果。代码段3.50实现了每小时日志记录长度的多值统计,结果如图3.26所示。
//代码段3.50:计算每小时出现的日志记录长度计数、最小值、最大值、平均值、求和 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "access_per_hour": { //外围聚合,统计每小时访问量 "date_histogram": { "field": "timestamp", "interval": "hour" }, "aggs": { //子聚合,统计log_size总数 "access": { "sum": {"field": "log_size"} } } }, "min_access_per_hour": { //管道聚合,链接上面两种聚合 "min_bucket": { "buckets_path": "access_per_hour>access" } } } }'
图3.26 每小时日志记录长度的多值统计结果
extended_stats_bucket聚合能够在stats_bucket聚合计算出的结果上添加平方和、方差和标准差等指标,只需将代码段3.50中的stats_bucket改为extended_stats_bucket即可(可以与聚合的名字一并修改),得到的结果如图3.27所示。
图3.27 基于extended_stats_bucket aggregation的每小时日志记录长度的多值统计结果
3.4.4 matrix aggregations
matrix aggregations根据从所请求的文档字段提取的值,对多个字段进行操作,并返回矩阵结果。这一聚合方式与前面提到的metric aggregations、bucket aggregations等均不同,它不支持script操作。这一节将对matrix aggregations中的matrix_stats聚合进行介绍。
matrix_stats聚合是一种面向数值的聚合,用于计算一组文档字段中的以下统计信息:
· 计数:计算过程中每种字段的样本数量;
· 平均值:每个字段数据的平均值;
· 方差:每个字段样本数据偏离平均值的程度;
· 偏度:量化每个字段样本数据在平均值附近的非对称分布情况;
· 峰度:量化每个字段样本数据分布的形状;
· 协方差:一种量化描述一个字段数据随另一个字段数据变化程度的矩阵;
· 相关性:描述两个字段数据之间的分布关系,其协方差矩阵取值在[-1,1]之间。
它主要用于计算两个数值型字段之间的关系,代码段3.51实现了对日志记录长度和HTTP状态码之间关系的计算,结果如图3.28所示。
//代码段3.51:计算日志记录长度和 HTTP状态码之间的关系 curl-XPOST localhost:9200/whale/log/_search-d'{ "aggs": { "matrixstats": { "matrix_stats": { "fields": [ "log_size", "status_code" ] } } } }'
图3.28 使用matrix_stats aggregation计算log_size和status_code之间的关系