ES中的聚合被分为两大类:Metric度量和bucket桶。说的通俗点,metric很像SQL中的avg、max、min等方法,而bucket就有点类似group by了。



1. Metric聚合

metric的聚合按照值的返回类型可以分为两种:单值聚合 和 多值聚合



1.1 单值聚合



1.1.1 Sum 求和

这个聚合返回的是单个值,dsl可以参考如下:

"aggs" : {
sum" : { "field" : "change" } }
  }
intraday_return是聚合的名字,同时也会作为请求返回的id值。另外,聚合中是支持脚本的。
{
    ...
    "aggregations": {
intraday_return": {
           "value": 2.18
        }
    }
}



1.1.2 Min 求最小值

{
    "aggs" : {
        "min_price" : { "min" : { "field" : "price" } }
    }
}



1.1.3 Max 求最大值

{
    "aggs" : {
        "max_price" : { "max" : { "field" : "price" } }
    }
}



1.1.4 avg 求平均值

{
    "aggs" : {
        "avg_grade" : { "avg" : { "field" : "grade" } }
    }
}



1.1.5 cardinality 求唯一值,即不重复的字段有多少

{
    "aggs" : {
        "author_count" : {
cardinality" : {
                "field" : "author"
            }
        }
    }
}



1.2 多值聚合



1.2.1 percentiles 求百分比

{
    "aggs" : {
        "load_time_outlier" : {
percentile_ranks" : {
                "field" : "load_time", 
                "values" : [15, 30]
            }
        }
    }
}


返回结果:

{
    ...
   "aggregations": {
      "load_time_outlier": {
         "values" : {
            "15": 92,
            "30": 100
         }
      }
   }
}



1.2.2 stats 统计

{
    "aggs" : {
stats" : { "field" : "grade" } }
    }
}


返回结果:

{
    ...
    "aggregations": {
        "grades_stats": {
            "count": 6,
            "min": 60,
            "max": 98,
            "avg": 78.5,
            "sum": 471
        }
    }
}



1.2.3 extend stats 扩展统计

{
    "aggs" : {
extended_stats" : { "field" : "grade" } }
    }
}


返回结果:

{
    ...
    "aggregations": {
        "grade_stats": {
           "count": 9,
           "min": 72,
           "max": 99,
           "avg": 86,
           "sum": 774,
           "sum_of_squares": 67028,
           "variance": 51.55555555555556,
           "std_deviation": 7.180219742846005,
           "std_deviation_bounds": {
            "upper": 100.36043948569201,
            "lower": 71.63956051430799
           }
        }
    }
}



2. Bucket聚合

Bucket可以理解为一个桶,它会遍历文档中的内容,凡是符合要求的就放入按照要求创建的桶中。

它是按照某个字段中的值来分类:比如性别有男、女,就会创建两个桶,分别存放男女的信息。默认会搜集doc_count的信息,即记录有多少男生,有多少女生,然后返回给客户端,这样就完成了一个terms的统计。



 2.1 terms聚合

{
    "aggs" : {
        "genders" : {
            "terms" : { "field" : "gender" }
        }
    }
}


返回结果:

{
    ...
    "aggregations" : {
        "genders" : {
            "doc_count_error_upper_bound": 0, 
            "sum_other_doc_count": 0, 
            "buckets" : [ 
                {
                    "key" : "male",
                    "doc_count" : 10
                },
                {
                    "key" : "female",
                    "doc_count" : 10
                },
            ]
        }
    }
 
   
  
}

2.1.1 terms聚合分析:

2.1.1.1 数据的不确定性

使用terms聚合,结果可能带有一定的偏差与错误性。

举个例子:

我们想要获取name字段中出现频率最高的前5个。

此时,客户端向ES发送聚合请求,主节点接收到请求后,会向每个独立的分片发送该请求。
分片独立的计算自己分片上的前5个name,然后返回。当所有的分片结果都返回后,在主节点进行结果的合并,再求出频率最高的前5个,返回给客户端。

这样就会造成一定的误差,比如最后返回的前5个中,有一个叫A的,有50个文档;B有49。但是由于每个分片独立的保存信息,信息的分布也是不确定的。有可能第一个分片中B的信息有2个,但是没有排到前5,所以没有在最后合并的结果中出现。这就导致B的总数少计算了2,本来可能排到第一位,却排到了A的后面。

2.1.1.2 size与shard_size

为了改善上面的问题,就可以使用size和shard_size参数。

  • size参数规定了最后返回的term个数(默认是10个)
  • shard_size参数规定了每个分片上返回的个数
  • 如果shard_size小于size,那么分片也会按照size指定的个数计算

通过这两个参数,如果我们想要返回前5个,size=5;shard_size可以设置大于5,这样每个分片返回的词条信息就会增多,相应的误差几率也会减小

2.1.1.3 order排序

order指定了最后返回结果的排序方式,默认是按照doc_count排序。

{
    "aggs" : {
        "genders" : {
            "terms" : {
                "field" : "gender",
                "order" : { "_count" : "asc" }
            }
        }
    }
}


也可以按照字典方式排序:

{
    "aggs" : {
        "genders" : {
            "terms" : {
                "field" : "gender",
                "order" : { "_term" : "asc" }
            }
        }
    }
}


可以通过order指定一个单值的metric聚合,来排序。

{
    "aggs" : {
        "genders" : {
            "terms" : {
                "field" : "gender",
order" : { "avg_height" : "desc" }
            },
            "aggs" : {
                "avg_height" : { "avg" : { "field" : "height" } }
            }
        }
    }
}


多值的Metric聚合,要指定使用的多值字段:

{
    "aggs" : {
        "genders" : {
            "terms" : {
                "field" : "gender",
                "order" : { "height_stats.avg" : "desc" }
            },
            "aggs" : {
                "height_stats" : { "stats" : { "field" : "height" } }
            }
        }
    }
}


2.1.1.4 min_doc_count与shard_min_doc_count

聚合的字段可能存在一些频率很低的词条,如果这些词条数目比例很大,那么就会造成很多不必要的计算。
因此可以通过设置min_doc_count和shard_min_doc_count来规定最小的文档数目,只有满足这个参数要求的个数的词条才会被记录返回。

通过名字就可以看出:

  • min_doc_count:规定了最终结果的筛选
  • shard_min_doc_count:规定了分片中计算返回时的筛选

2.1.1.5 script

桶聚合也支持脚本的使用:

{
    "aggs" : {
        "genders" : {
            "terms" : {
                "script" : "doc['gender'].value"
            }
        }
    }
}


以及外部脚本文件:

{
    "aggs" : {
        "genders" : {
            "terms" : {
                "script" : {
                    "file": "my_script",
                    "params": {
                        "field": "gender"
                    }
                }
            }
        }
    }
}


2.1.1.6 filter

filter字段提供了过滤的功能,使用两种方式:include可以过滤出包含该值的文档;相反则使用exclude。

{
    "aggs" : {
        "tags" : {
            "terms" : {
                "field" : "tags",
                "include" : ".*sport.*",
                "exclude" : "water_.*"
            }
        }
    }
}



2.1.2 多字段聚合

通常情况,terms聚合都是仅针对于一个字段的聚合。因为该聚合是需要把词条放入一个哈希表中,如果多个字段就会造成n^2的内存消耗。

不过,对于多字段,ES也提供了下面两种方式:

    1. 使用脚本合并字段

    2. 使用copy_to方法,合并两个字段,创建出一个新的字段,对新字段执行单个字段的聚合。

例如:实现 select SEX,PROF,COUNT(*) from table group by SEX,PROF

{
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "sexprof": { //使用脚本合并字段并对新字段执行单字段的聚合
            "terms": {
                "script": {
                    "inline": "doc['SEX.keyword'].value +'-split-'+ doc['PROF.keyword'].value "
                }
            }
        }
    }
}


得到的结果:

{
     "aggregations": {
         "sexprof": {
             "doc_count_error_upper_bound": 5,
             "sum_other_doc_count": 379,
             "buckets": [
                 {
                     "key": "女-split-助教",
                     "doc_count": 2
                 },
                 {
                     "key": "男-split-讲师",
                     "doc_count": 1
                 },
                 {
                     "key": "男-split-教授",
                     "doc_count": 1
                 }
             ]
         }
     }
 }



2.1.3 collect模式

对于子聚合的计算,有两种方式:

  • depth_first 直接进行子聚合的计算
  • breadth_first 先计算出当前聚合的结果,针对这个结果在对子聚合进行计算。

默认情况下ES会使用深度优先,不过可以手动设置成广度优先,比如:

{
    "aggs" : {
        "actors" : {
             "terms" : {
                 "field" : "actors",
                 "size" : 10,
collect_mode" : "breadth_first"
             },
            "aggs" : {
                "costars" : {
                     "terms" : {
                         "field" : "actors",
                         "size" : 5
                     }
                 }
            }
         }
    }
}



2.1.4 缺省值Missing value

缺省值指定了缺省的字段的处理方式:

{
    "aggs" : {
        "tags" : {
             "terms" : {
                 "field" : "tags",
                 "missing": "N/A" 
             }
         }
    }
}



2.2 Histogram 直方图聚合

Elasticsearch支持最直方图聚合,它在数字字段自动创建桶,并会扫描全部文档,把文档放入相应的桶中。这个数字字段既可以是文档中的某个字段,也可以通过脚本创建得出的。



2.2.1 桶的筛选规则



2.2.1.1 min_doc_count过滤

返回document数量大于等于1 的直方图聚合

{
    "aggs" : {
        "prices" : {
histogram" : {
                "field" : "price",
                "interval" : 50,
min_doc_count" : 1
            }
        }
    }
}



2.2.1.2 extend_bounds,指定最小值和最大值边界

默认情况下,ES中的histogram聚合起始都是自动的,比如price字段,如果没有商品的价钱在0-5之间,0这个桶就不会显示。如果最便宜的商品是11,那么第一个桶就是10.

可以通过设置extend_bounds强制规定最小值和最大值,但是要求必须min_doc_count不能大于0,不然即便是规定了边界,也不会返回。

可以通过设置extend_bounds强制规定最小值和最大值,但是要求必须min_doc_count不能大于0,不然即便是规定了边界,也不会返回。



2.2.1.3 order排序

排序大同小异,可以按照_key,_count,指定排序的聚合进行排序

{
    "aggs" : {
        "prices" : {
            "histogram" : {
                "field" : "price",
                "interval" : 50,
                "order" : { "_key" : "desc" }
            }
        }
    }
}



2.2.1.4 keyed设置返回的方式

正常返回的数据如上面所示,是按照数组的方式返回。如果要按照名字返回,可以设置keyed为true。

之前的返回方式:

设置keyed为true:

{
    "aggs" : {
        "prices" : {
            "histogram" : {
                "field" : "price",
                "interval" : 50,
                "keyed" : true
            }
        }
    }
}


 返回的数据为:

{
    "aggregations": {
        "prices": {
            "buckets": {
                "0": {
                    "key": 0,
                    "doc_count": 2
                },
                "50": {
                    "key": 50,
                    "doc_count": 4
                },
                "150": {
                    "key": 150,
                    "doc_count": 3
                }
            }
        }
    }
}



2.2.1.5 缺省的值

缺省值通过MissingValue设置:

{
    "aggs" : {
        "quantity" : {
             "histogram" : {
                 "field" : "quantity",
                 "interval": 10,
                 "missing": 0 
             }
         }
    }
}



2.3 Date Histogram聚合 

Date histogram的用法与histogram差不多,只不过区间上支持了日期的表达式。

{
 "aggs":{
    "articles_over_time":{
date_histogram":{
            "field":"date",
            "interval":"month"
            }
        }
    }
}


interval字段支持多种关键字:`year`, `quarter`, `month`, `week`, `day`, `hour`, `minute`, `second`

也支持对这些关键字进行扩展使用,比如一个半小时可以定义成如下::

{
    "aggs":{
        "articles_over_time":{
            "date_histogram":{
                "field":"date",
                "interval":"1.5h"
                "format":"yyyy-MM-dd" //对返回结果进行格式化
                }
            }
        }
}



2.3.1 time_zone时区的用法

在es中日期支持时区的表示方法,这样就相当于东八区的时间。

{
    "aggs":{
        "by_day":{
            "date_histogram":{
                "field":"date",
                "interval":"day",
time_zone":"+08:00"
            }
        }
    }
}



2.3.2 offset 使用偏移值,改变时间区间

默认情况是从凌晨0点到午夜24:00,如果想改变时间区间,可以通过下面的方式,设置偏移值:

{
"aggs":{
    "by_day":{
        "date_histogram":{
            "field":"date",
            "interval":"day",
            "offset":"+6h"
            }
        }
    }
}



2.3.3 Missing Value缺省字段

当遇到没有值的字段,就会按照缺省字段missing value来计算。

对于其他的一些用法,这里就不过多赘述了,比如脚本、Order、min_doc_count过滤,extended_bounds等都是支持的。

{
  "aggs": {
    "missing_address": {
      "missing": {
        "field": "address"
      }
    }
  }
}


结果只返回一个桶



2.4 嵌套聚合

{
   "aggs":{
      "color_type_max":{
         "terms":{ 
            "field": "color"
         },
         "aggs":{
            "max_age": {
               "max": { 
                 "field" : "age"
               }
             }
         }
      },
      "color_type_min":{
         "terms":{ 
            "field": "color"
         },
         "aggs":{
            "min_age": {
               "min": { 
                 "field" : "age"
               }
             }
         }
      }
   }
}