七、ElasticSearch的聚合分析
1.聚合分析简介
聚合分析,英文Aggregation,是ES除了搜索功能之外提供的针对ES数据进行统计分析的功能。
特点:①功能丰富,可满足大部分分析需求;②实时性高,所有计算结果实时返回。
#聚合分析格式:
GET my_index/_search
{
"size":0,
"aggs":{ #关键词
"":{ #自定义聚合分析名称,一般起的有意义
"":{ #聚合分析类型
"" #聚合分析主体
}
}
[,"aggs":{[]+}] #可包含多个子聚合分析
}
}
基于分析规则的不同,ES将聚合分析主要划分为以下4种:
A、Metric。指标分析类型,如:计算最值,平均值等;
B、Bucket。分桶类型,类似于group by语法,根据一定规则划分为若干个桶分类;
C、Pipeline。管道分析类型,基于上一级的聚合分析结果进行再分析;
D、Matrix。矩阵分析类型。
2.Metric聚合分析
主要分为两类:单值分析(输出单个结果)和多值分析(输出多个结果)。
1)单值分析
A、min。返回数值类型字段的最小值:
#min关键字
GET my_index/_search
{
"size": 0,
"aggs":{
"min_age":{
"min":{ #关键字
"field":"age"
}
}
}
}
B、max。返回数值类型字段的最大值。
#max关键字
GET my_index/_search
{
"size": 0,
"aggs":{
"max_age":{
"max":{ #关键字
"field":"age"
}
}
}
}
C、avg。返回数值类型字段的平均值。
#avg关键字
GET my_index/_search
{
"size": 0,
"aggs":{
"avg_age":{
"avg":{ #关键字
"field":"age"
}
}
}
}
D、sum。返回数值类型字段值的总和。
#sum关键字
GET my_index/_search
{
"size": 0,
"aggs":{
"sum_age":{
"sum":{ #关键字
"field":"age"
}
}
}
}
E、cardinality。返回字段的基数。
#cardinality关键字
GET my_index/_search
{
"size": 0,
"aggs":{
"cardinality_age":{
"cardinality":{ #关键字
"field":"age"
}
}
}
}
F、使用多个单值分析关键词,返回多个结果。
#使用多个单值分析关键词,返回多个分析结果
GET my_index/_search
{
"size": 0,
"aggs": {
"min_age":{
"min":{ #求最大年龄
"field":"age"
}
},
"max_age":{
"max":{ #求最小年龄
"field":"age"
}
},
"avg_age":{
"avg":{ #求平均年龄
"field":"age"
}
},
"sum_age":{
"sum":{ #求年龄总和
"field":"age"
}
}
}
}
2)多值分析
A、stats。返回所有单值结果:
#使用stats关键词
GET my_index/_search
{
"size": 0,
"aggs":{
"stats_age":{
"stats":{ #关键字
"field":"age"
}
}
}
}
B、extended stats。对stats进行扩展,包含更多,如:方差,标准差,标准差范围等:
#使用extended_stats关键词
GET my_index/_search
{
"size": 0,
"aggs":{
"stats_age":{
"extended_stats":{ #关键字
"field":"age"
}
}
}
}
C、Percentile。百分位数统计:
#使用percentiles关键词
GET my_index/_search
{
"size": 0,
"aggs":{
"per_age":{
"percentiles":{ #关键字
"field":"age"
}
}
}
}
#使用percentiles关键词进行百分位数预测。
GET my_index/_search
{
"size": 0,
"aggs":{
"per_age":{
"percentiles":{ #关键字
"field":"age",
"values":[
20,
25 #判断20和25分别在之前的年轻区间的什么位置,以百分数显示
]
}
}
}
}
D、Top hits。一般用于分桶之后获取该桶内最匹配的定不稳当列表,即详情数据:
#使用top_hits关键词
GET my_index/_search
{
"size":0,
"aggs":{
"jobs":{
"terms":{
"match":{
"field":"job.keyword", #按job.keyword进行分桶聚合
"size":10
},
"aggs":{
"top_employee":{
"top_hits":{
"size":10, #返回文档数量
"sort":[
{
"age":{
"order":"desc" #按年龄倒叙排列
}
}
]
}
}
}
}
}
}
}
3.Bucket聚合分析
Bucket,意为桶。即:按照一定规则,将文档分配到不同的桶中,达分类的目的。常见的有以下五类:
1)Terms。直接按term进行分桶,如果是text类型,按分词后的结果分桶:
#使用terms关键词
GET my_index/_search
{
"size": 0,
"aggs":{
"terms_job":{
"terms":{ #关键字
"field":"job.keyword", #按job.keyword进行分桶
"size":5 #返回五个文档
}
}
}
}
2)Range。按指定数值范围进行分桶:
#使用range关键词
GET my_index/_search
{
"size": 0,
"aggs":{
"number_ranges":{
"range":{ #关键字
"field":"age", #按age进行分桶
"ranges":[
{
"key":">=19 && < 25", #第一个桶: 19<=年龄<25
"from":19,
"to":25
},
{
"key":"< 19", #第二个桶: 年龄<19
"to":19
},
{
"key":">= 25", #第三个桶: 年龄>=25
"from":25
}
]
}
}
}
}
3)Date Range。按指定日期范围进行分桶:
#使用date_range关键词
GET my_index/_search
{
"size": 0,
"aggs":{
"date_ranges":{
"date_range":{ #关键字
"field":"birth", #按age进行分桶
"format":"yyyy",
"ranges":[
{
"key":">=1980 && < 1990", #第一个桶: 1980<=出生日期<1990
"from":"1980",
"to":"1990"
},
{
"key":"< 1980", #第二个桶: 出生日期<1980
"to":1980
},
{
"key":">= 1990", #第三个桶: 出生日期>=1990
"from":1990
}
]
}
}
}
}
4)Histogram。直方图,按固定数值间隔策略进行数据分割:
#使用histogram关键词
GET my_index/_search
{
"size": 0,
"aggs":{
"age_hist":{
"histogram":{ #关键词
"field":"age",
"interval":3, #设定间隔大小为2
"extended_bounds":{ #设定数据范围
"min":0,
"max":30
}
}
}
}
}
5)Date Histogram。日期直方图,按固定时间间隔进行数据分割:
#使用date_histogram关键词
GET my_index/_search
{
"size": 0,
"aggs":{
"birth_hist":{
"date_histogram":{ #关键词
"field":"birth",
"interval":"year", #设定间隔大小为年year
"format":"yyyy",
"extended_bounds":{ #设定数据范围
"min":"1980",
"max":"1990"
}
}
}
}
}
4.Bucket+Metric聚合分析
Bucket聚合分析允许通过添加子分析来进一步进行分析,该子分析可以是Bucket,也可以是Metric。
1)分桶之后再分桶(Bucket+Bucket),在数据可视化中一般使用千层饼图进行显示。
#分桶之后再分桶——Bucket+Bucket
GET my_index/_search
{
"size":0,
"aggs":{
"jobs":{
"terms":{ #第一层Bucket
"match":{
"field":"job.keyword",
"size":10
},
"aggs":{
"age_range":{
"range":{ #第二层Bucket
"field":"age",
"ranges":[
{"to":20},
{"from":20,"to":30},
{"from":30}
]
}
}
}
}
}
}
}
2)分桶之后再数据分析(Bucket+Metric)
#分桶之后再数据分析——Bucket+Metric
GET my_index/_search
{
"size":0,
"aggs":{
"jobs":{
"terms":{ #第一层Bucket
"match":{
"field":"job.keyword",
"size":10
},
"aggs":{
"stats_age":{
"stats":{ #第二层Metric
"field":"age"
}
}
}
}
}
}
}
5.Pipeline聚合分析
针对聚合分析的结果进行再分析,且支持链式调用:
#使用pipeline聚合分析,计算订单月平均销售额。
GET my_index/_search
{
"size": 0,
"aggs":{
"sales_per_month":{
"date_histogram":{
"field":"date",
"interval":"month"
},
"aggs":{
"sales":{
"sum":{
"field":"price"
}
}
}
},
"avg_monthly_sales":{
"avg_bucket":{ #bucket类型
"buckets_path":"sales_per_month>sales" #使用buckets_path参数,表明是pipeline
}
}
}
}
pipeline的分析结果会输出到原结果中,由输出位置不同,分为两类:Parent和Sibling。
1)Parent。结果内嵌到现有聚合分析结果中,如:Derivate、Moving Average、Cumulative Sum。
2)Sibling。结果与现有聚合分析结果同级,如:Max/Min/Sum/Avg Bucket、Stats/Extended Stats Bucket、Percentiles Bucket。
A、Sibling——————min_bucket,其他同理:
#Sibling聚合分析
GET my_index/_search
{
"size": 0,
"aggs":{
"jobs":{
"terms":{ #根据job.keyword进行分桶
"field":"job.keyword",
"size":10
},
"aggs":{
"avg_salary":{
"avg":{ #之后Metric中求工资的平均数
"field":"salary"
}
}
}
},
"min_salary_by_job":{
"min_bucket":{ #关键词
"buckets_path":"jobs>avg_salary" #按工资平均数,排列每个桶中的job
}
}
}
B、Parent——————Derivate,其他同理:
#Parent聚合分析
GET my_index/_search
{
"size":0,
"aggs":{
"bitrh":{
"date_histogram":{
"field":"birth",
"interval":"year",
"min_doc_count":0
},
"aggs":{
"avg_salary":{
"avg":{
"field":"salary"
}
},
"derivative_avg_salary":{
"derivative":{ #关键词
"buckets_path":"avg_salary"
}
}
}
}
}
}
6.聚合分析的作用范围
ES聚合分析默认作用范围是query的结果集:
#ES中聚合分析的默认作用范围是query的结果集
GET my_index/_search
{
"size":0,
"query":{
"match":{
"username":"alfred"
}
},
"aggs":{
"jobs":{
"terms":{
"match":{ #此时,只在username字段中包含alfred的文档中进行分桶
"field":"job.keyword",
"size":10
}
}
}
}
}
可通过以下方式修改:
①filter。
②post_filter。
③global。
1)filter,为某个结合分析设定过滤条件,从而在不改变整体query语句的情况下修改范围。
#使用filter进行过滤
GET my_index/_search
{
"size":0,
"aggs":{
"jobs_salary_small":{
"filter":{
"range":{
"salary":{
"to":10000
}
}
},
"aggs":{
"jobs":{
"terms":{ #在salary小于10000的文档中对工作进行分桶
"field":"job.keyword"
}
}
}
}
}
}
2)post_filter,作用于文档过滤,但在聚合分析之后才生效。
#使用post_filter进行过滤
GET my_index/_search
{
"size":0,
"aggs":{
"jobs":{
"terms":{ #在salary小于10000的文档中对工作进行分桶
"field":"job.keyword"
}
}
},
"post_filter":{ #在集合分析之后才生效
"match":{
"job.keyword":"java engineer"
}
}
}
3)global,无视query条件,基于所有文档进行分析。
#使用global进行过滤
GET my_index/_search
{
"query":{
"match":{
"job.keyword":"java engineer"
}
},
"aggs":{
"java_avg_salary":{
"avg":{
"field":"salary"
}
},
"all":{
"global":{ #关键词
"aggs":{
"avg_salary":{
"avg":{
"field":"salary" #依然是对所有的文档进行查询,而不会去管query
}
}
}
}
}
}
}
7.聚合分析中的排序
1)可使用自带的关键数据排序,如:
_count 文档数、_key按key值
#使用自带的数据进行排序
GET my_index/_search
{
"size":0,
"aggs":{
"jobs":{
"terms":{
"field":"job.keyword",
"size":10,
"order":[
{
"_count":"asc" #默认按_count倒叙排列
},
{
"_key":"desc" 使用多个排序值,从上往下的顺序进行排列
}
]
}
}
}
}
1)也可使用聚合结果进行排序,如:
#使用聚合结果进行排序
GET my_index/_search
{
"size":0,
"aggs":{
"salary_hist":{
"histogram":{
},
"aggs":{
"age":{
"filter":{
"range":{
"age":{
"gte":10
}
}
},
"aggs":{
"avg_age":{
"field":"age"
}
}
}
}
}
}
}
8.计算精准度
每个Shard上分别计算,由coordinating Node做聚合,多个Shard上分散着若干数据,而coordinating Node无法得知,那么在取数据的时候,比如:top hits的时候,结果有偏差,造成精准度不准确。解决办法有两种:
1)直接设置shard数量为1;
2)设置shard_size大小,即每次从shard上额外多获取数据,从而提升精准度
#使用shard_size额外获取数据
GET my_index/_search
{
"size":0,
"aggs":{
"jobs":{
"terms":{
"field":"job.keyword",
"size":1,
"shard_size":10
}
}
}
}
terms返回聚合结果中有两个统计值:
doc_count_error_upper_bound:被一楼的term可能的最大值;
sum_other_doc_count:返回结果bucket的term外其他term的文档总数。
三者只能取其二。