类型
ES 中聚合的类型主要有以下 3 种:
- Metric Aggregations: 提供求 sum(求总和)、average(求平均数) 等数学运算,可以对字段进行统计分析。
- Bucket Aggregations:分桶类型,对满足特定条件的文档进行分组,例如将 A 出版社的书本分为一组,将 B 出版社的书本分为一组,类似于 SQL 里的 Group By 功能。
- Pipeline Aggregations:对其他聚合输出的结果进行再次聚合。
- Matrⅸ,矩阵分析类型
ES 的聚合可以进行多种组合来构建的统计查询,从而解决复杂的统计分析的需求
简介
聚合分析是数据库中重要的功能特性,完成对一个查询的数据集中数据的聚合计算,如:找出某字段 (或计算表达式的结果)的最大值、最小值,计算和、平均值等
- 聚合的两个主要的概念,分别是 桶 和 指标
- 桶(Buckets) : 满足特定条件的文档的集合
- 当聚合开始被执行,每个文档会决定符合哪个桶的条件,如果匹配到,文档将放入相应的桶并接着进行聚合操作
- 像是一个员工属于男性桶或者女性桶,日期2014-10-28属于十月桶,也属于2014年桶
- 桶可以被嵌套在其他桶里面
- 像是北京能放在中国桶裡,而中国桶能放在亚洲桶裡
- Elasticsearch提供了很多种类型的桶,像是时间、最受欢迎的词、年龄区间、地理位置桶等等,不过他们在根本上都是通过同样的原理进行操作,也就是基于条件来划分文档,一个文档只要符合条件,就可以加入那个桶,因此一个文档可以同时加入很多桶
- 当聚合开始被执行,每个文档会决定符合哪个桶的条件,如果匹配到,文档将放入相应的桶并接着进行聚合操作
- 指标(Metrics) : 对桶内的文档进行统计计算
- 桶能让我们划分文档到有意义的集合, 但是最终我们需要的是对这些桶内的文档进行一些指标的计算
- 指标通常是简单的数学运算(像是min、max、avg、sum),而这些是通过当前桶中的文档的值来计算的,利用指标能让你计算像平均薪资、最高出售价格、95%的查询延迟这样的数据
- 桶(Buckets) : 满足特定条件的文档的集合
aggs 聚合的模板
- 当query和aggs一起存在时,会先执行query的主查询,主查询query执行完后会搜出一批结果,而这些结果才会被拿去aggs拿去做聚合
- 另外要注意aggs后面会先接一层自定义的这个聚合的名字,然后才是接上要使用的聚合桶
- 如果有些情况不在意查询结果是什麽,而只在意aggs的结果,可以把size设为0,如此可以让返回的hits结果集是0,加快返回的速度
- 一个aggs裡可以有很多个聚合,每个聚合彼此间都是独立的,因此可以一个聚合拿来统计数量、一个聚合拿来分析数据、一个聚合拿来计算标准差…,让一次搜索就可以把想要做的事情一次做完
- aggs可以嵌套在其他的aggs裡面,而嵌套的桶能作用的文档集范围,是外层的桶所输出的结果集
语法:
在查询请求体中以aggregations节点按如下语法定义聚合分析:
aggregations 也可简写为 aggs
"aggregations": {
"<aggregation_name>": { <!--聚合的名字 -->
"<aggregation_type>" : { <!--聚合的类型 -->
<aggregation_body> <!--聚合体:对哪些字段进行聚合 -->
}
[,"meta" : { [<meta_data_body>] } ]? <!--元 -->
[,"aggregations" : { [<sub_aggregation>]+ } ]? <!--在聚合里面在定义子聚合 -->
}
[, "<aggregation_name_2>": { ...}] * <!--聚合的名字 -->
}
GET 127.0.0.1/mytest/doc/_search
{
"query": { ... },
"size": 0, //建议设置为0,这样不会返回 _source
"aggs": { //和query 同级别的关键词
"custom_name1": { //aggs后面接著的是一个自定义的name,会从聚合结果中返回
"桶": { ... } //再来才是接桶
},
"custom_name2": { //一个aggs裡可以有很多聚合,可以进行多个同级别的聚合查询
"aggs_type": { //聚合的定义:聚合类型 + 聚合body
aggs body
}
},
"custom_name3": { //自定义的聚合名字,会从聚合结果中返回
"桶": {
.....
},
"aggs": { //aggs可以嵌套在别的aggs裡面, 子聚合
"in_name": { //记得使用aggs需要先自定义一个name,会从聚合结果中返回
"桶": { ... } //in_name的桶作用的文档是custom_name3的桶的结果
}
}
}
}
}
# 返回
{
"hits": {
"total": 8,
"max_score": 0,
"hits": [] //因为size设为0,所以没有查询结果返回
},
"aggregations": {
"custom_name1": {
...
},
"custom_name2": {
...
},
"custom_name3": {
... ,
"in_name": {
....
}
}
}
}
聚合结果排序
对嵌套计算出的avg(balance),这里是average_balance,进行排序
GET /bank/_search
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"order": {
"average_balance": "desc"
}
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
例子
准备数据
POST /test-agg-cars/_bulk
{ "index": {}}
{ "price" : 10000, "color" : "red", "make" : "honda", "sold" : "2014-10-28" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 30000, "color" : "green", "make" : "ford", "sold" : "2014-05-18" }
{ "index": {}}
{ "price" : 15000, "color" : "blue", "make" : "toyota", "sold" : "2014-07-02" }
{ "index": {}}
{ "price" : 12000, "color" : "green", "make" : "toyota", "sold" : "2014-08-19" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 80000, "color" : "red", "make" : "bmw", "sold" : "2014-01-01" }
{ "index": {}}
{ "price" : 25000, "color" : "blue", "make" : "ford", "sold" : "2014-02-12" }
标准的聚合
有了数据,开始构建我们的第一个聚合。汽车经销商可能会想知道哪个颜色的汽车销量最好,用聚合可以轻易得到结果,用 terms 桶操作:
- 聚合操作被置于顶层参数 aggs 之下(如果你愿意,完整形式 aggregations 同样有效)。
- 然后,可以为聚合指定一个我们想要名称,本例中是: popular_colors 。
最后,定义单个桶的类型 terms 。
GET /test-agg-cars/_search
{
"size" : 0,
"aggs" : {
"popular_colors" : {
"terms" : {
"field" : "color.keyword"
}
}
}
}
因为我们设置了 size 参数,所以不会有 hits 搜索结果返回。
- popular_colors 聚合是作为 aggregations 字段的一部分被返回的。
- 每个桶的 key 都与 color 字段里找到的唯一词对应。它总会包含 doc_count 字段,告诉我们包含该词项的文档数量。
- 每个桶的数量代表该颜色的文档数量。
多个聚合
同时计算两种桶的结果:对color和对makeGET /test-agg-cars/_search
{
"size" : 0,
"aggs" : {
"popular_colors" : {
"terms" : {
"field" : "color.keyword"
}
},
"make_by" : {
"terms" : {
"field" : "make.keyword"
}
}
}
}
聚合的嵌套
这个新的聚合层让我们可以将 avg 度量嵌套置于 terms 桶内。实际上,这就为每个颜色生成了平均价格。GET /test-agg-cars/_search
{
"size" : 0,
"aggs": {
"colors": {
"terms": {
"field": "color.keyword"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
正如 颜色 的例子,我们需要给度量起一个名字( avg_price )这样可以稍后根据名字获取它的值。最后,我们指定度量本身( avg )以及我们想要计算平均值的字段( price )动态脚本的聚合
这个例子告诉你,ElasticSearch还支持一些基于脚本(生成运行时的字段)的复杂的动态聚合。GET /test-agg-cars/_search
{
"runtime_mappings": {
"make.length": {
"type": "long",
"script": "emit(doc['make.keyword'].value.length())"
}
},
"size" : 0,
"aggs": {
"make_length": {
"histogram": {
"interval": 1,
"field": "make.length"
}
}
}
}