一、聚合分析的简单介绍
- 聚合分析,英文为 Aggregation,是es 除搜索功能外提供的针对es数据做统计分析的功能。
- es聚合分析功能丰富,提供 Bucket、Metric、Pipeline等多种分析方式,可以满足大部分的分析需求;
- 实时性高,所有的计算结果是即时返回的,而Hadoop 等大数据系统一般都是 T+1 级别的。
- 语法如下:
GET testindex/_search
{
"size": 0,
"aggs": { // 关键词,与query 同级
"agg_name": { // 自定义的聚合名称
"agg_type": { // 聚合分析的定义,包含type 和 body 定义
"agg_body"
},
[,"aggs": {"sub_agg"}] // 子聚合
},
"agg_name2": {....} // 可以包含多个聚合
}
}
二、聚合分析之分类
- Bucket,分桶类型,类似 SQL 中的 group by 语法;
- Metric,指标分析类型,如计算最大值、最小值、平均值等;
- Pipeline,管道分析类型,基于上一级的聚合分析结果进行再分析;
- Matrix,矩阵分析类型。
三、Bucket 聚合分析
Bucket,意味 ’桶‘,即按照一定的规则将文档分配到不同的桶中,达到分类的目的。
按照Bucket 的分桶策略,常见的Bucket 聚合分析如下:
-
Terms:直接按照term来分桶。如果是text 类型(需要开启fielddata),则按照分词后的结果分桶。
-
Range:通过制定数值的范围内设定分桶规则
// *-10000,10000-20000,20000-* 三个区间的文档数聚合 GET testinde/_search { "aggs": { "salary_range": { "range": { "field": "salary", "ranges": [ { "key": "<10000", // 自定义key 值的名字 "to": 10000 }, { "from": 10000, "to": 20000, }, { "from": 20000 } ] } } } }
-
Date Range:通过制定日期的范围来设定分桶规则
GET testindex/_search
{
"aggs": {
"date_range": {
"range": {
"field": "birth",
"format": "yyyy-MM-dd", // 指定返回结果的日期格式
"ranges": [
{
"from": "2019-09-01", // 指定日期范围,可以使用date match
"to": "2019-09-30"
}
]
}
}
}
}
- Histogram:直方图,以固定间隔的策略来分割数据
GET testindex/_search
{
"aggs": {
"salary_hist": {
"histogram": { // 关键词
"field": "salary",
"interval": 5000, // 指定间隔大小
"extended_bounds": { // 指定数据范围
"min": 0,
"max": 50000
}
}
}
}
}
- Date Histogram:针对日期的直方图或者柱状图,是时序数据分析中的常用聚合分析类型
GET testindex/_search
{
"aggs": {
"by_year": {
"date_histogram": { // 关键词
"field": "birth",
"interval": "year", // 指定间隔大小
"format": "yyyy"
}
}
}
}
四、Metric 聚合分析
主要分如下2类:
- 单值分析,只输出一个分析结果,常见的有min ,max ,avg ,sum , cardinality
- 多值分析,输出多个分析结果,常见的有stats ,extended stats ,percentile , percentile rank ,top hits
1、cardinality
集合的势,或者基数,是指不同数值的个数。可以类比成 SQL 中的 distinct count意思。分组求和,比如某列的数值有多个不同的值。
2、stats
返回一系列数值类型的统计值,包含了min、max、avg、sum和count。一次做了多种分析。
3、extended_stats
顾名思义,是对stats的扩展,包含了更多的统计数据,如方差、标准差等。
4、percentiles与percentile_ranks
百分位数统计。如下:
// 比如:第一行的意思是18岁以下的人数占1.0%
{
"took": 55,
......
"aggregations": {
"per_age": {
"1.0": 18,
"5.0": 25,
"25.0": 29,
"50.0": 31,
"75.0": 40,
"95.0": 60,
"99.0": 80
}
}
}
// 指定展示的百分位数(只展示95%,99%,99.9%的数据)
GET testindex/_search
{
"aggs": {
"per_salary": {
"percentiles": {
"field": "salary",
"percents": [
95,
99,
99.9
]
}
}
}
}
5、top_hits
一般用于分桶聚合后,获取该桶内最匹配的顶部文档列表,即详情数据。如下:
GET testindex/_search
{
"aggs": {
"group_jobs": {
"terms": { // 分桶聚合
"field": "job.keyword"
},
"aggs": {
"top_employee": {
"top_hits": { // 关键词
"size": 10,
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
}
}
}
}
}
五、Bucket + Metric 聚合分析
Bucket 聚合分析允许通过添加子分析来进一步分析,该子分析可以是 Bucket 也可以是 Metric。
六、pipeline 聚合分析
pipeline的分析结果会输出到原结果中,根据输出位置的不同,可以分为2类。
1、parent 结果内嵌到现有的聚合分析结果中:
- Derivative:计算Bucket 值的导数。
- Moving Average:计算Bucket值的移动平均值。
- Cumulative Sum:计算Bucket值的累计加和。
2、Sibling 结果与现有聚合分析结果同级
- Max/Min/Avg/Sum Bucket
- Stats/Extended Stats Bucket
- Percentiles Bucket
七、聚合分析作用范围
es聚合分析默认作用范围是 query 的结果集,可以通过 filter、post_filter、global 改变其作用范围。
filter
它是为某个聚合分析设定过滤条件的,从而在不更改整体query 语句的情况下修改作用范围。
post_filter
作用于文档过滤,但在聚合分析后生效,过滤输出的结果。
global
无视query的过滤条件,基于全部文档进行分析。比如你在query里面进行了文档过滤,但是global还是会基于全部文档去分析。
八、排序
可以使用自带的关键数据进行排序,比如:
- _count 文档数
- _key 按照key值排序
GET testindex/_search
{
"aggs": {
"group_jobs": {
"terms": { // 分桶聚合
"field": "job.keyword",
"order": { // 排序
"_count": "asc"
}
}
}
}
}
九、聚合分析精度问题
max、min这种是精确地,但是 terms 是不一定精确地。可以通过参数来优化它。
- 设置shard 数为1,消除数据分散的问题,但缺点比较明显,无法承载大数据量。
- 合理设置 shard_size 大小,即每次从shard 上额外多获取数据,以提升精确度。
terms 聚合返回结果中有如下2个统计值:
- doc_count_error_upper_bound 被遗漏的 term 可能的最大值
- sum_other_doc_count 返回结果 bucket 的term外,其他term的文档总数
注:shard_size大小的设定方法
设定show_term_doc_count_error=true可以查看每个bucket误算的最大值。当其返回结果的doc_count_error_upper_bound=0,则说明计算是准确的,不存在误差。
GET testindex/_search
{
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"show_term_doc_count_error": true
}
}
}
}
shard_size默认大小如下:shard_size=size * 1.5 + 10
通过调整shard_size 的大小降低doc_count_error_upper_bound来提升准确度(随之而来的影响是增大了整体的计算量,从而降低了响应时间)
GET testindex/_search
{
"aggs": {
"jobs": {
"terms": {
"field": "job.keyword",
"shard_size": 10, // 关键词
"show_term_doc_count_error": true
}
}
}
}
十、热词分析(对热词内容先分词再聚合)
第一步,设置mapping(指定分词器和开启fielddata,因为你想要分词,所以type毫无疑问是text)
PUT test
{
"mappings": {
"doc": {
"properties": {
"hello": {
"type": "text",
"analyzer": "ik_max_word",
"fielddata": true
}
}
}
}
}
第二步,插入测试数据
PUT test/doc/1
{
"hello": "北京加油"
}
第三步,聚合查询
GET test/_search
{
"query": {
"match_all": {}
},
"aggs": {
"NAME": {
"terms": {
"field": "hello",
"size": 10
}
}
}
}
结果如下:
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "test123",
"_type": "doc",
"_id": "1",
"_score": 1,
"_source": {
"hello": "北京加油"
}
}
]
},
"aggregations": {
"NAME": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "北京",
"doc_count": 1
},
{
"key": "加油",
"doc_count": 1
}
]
}
}
}