聚合

聚合

ES Aggs 该如何根据查询结果的数量进行过滤

Elasticsearchlaoyang360 回复了问题 • 3 人关注 • 2 个回复 • 156 次浏览 • 4 天前 • 来自相关话题

ES Aggs根据聚合的结果(数值)进行过滤

Elasticsearchziyou 发表了文章 • 1 个评论 • 143 次浏览 • 5 天前 • 来自相关话题

前言

我们在使用聚合时总是有各种各样的聚合需求,其中一个比较常用的就是根据聚合的结果过滤聚合的桶,例如:1、每个IP登录次数超过5次的IP;2、每个IP登录人数超过2的IP。 还有我之前的一个案例,访问量超过1000的人数,这些都是很常见的统计需求。

案例需求

我们在使用聚合计算的时候一般都有两类,一种是计算文档的数量,另一种是计算文档内字段的值的数量(去重计算)或者值的数学计算。两种聚合计算在过滤的时候采用不同的方法来计算。

我们使用以下案例来说明两种过滤的不同: 用户每次登录都会记录一个登录记录:

{"userID":"a","IP":"10.70.25.1","time":"2019-10-10 12:12:12.222"}

然后提出以下两个需求: 1、每个IP登录次数超过5次的IP; 2、每个IP登录人数超过2的IP。

实现

每个IP登录次数超过5次的IP

这个是对登录记录个数的桶聚合统计,然后过滤。使用IP做term聚合,就可以得出每个IP的登录次数,然后term聚合中有一个参数min_doc_count这个字段就可以对文档数量进行过滤,具体的语句如下: 查询语句

{
  "aggs": {
    "IP": {
      "terms": {
        "field": "IP",
        "size": 3000,
        "order": {
          "_count": "desc"
        },
        "min_doc_count": 5
      }
    }
  },
  "size": 0
}

结果

{
  "took" : 614,
  "timed_out" : false,
  "num_reduce_phases" : 3,
  "_shards" : {
    "total" : 1105,
    "successful" : 1105,
    "skipped" : 75,
    "failed" : 0
  },
  "hits" : {
    "total" : 2826,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "IP" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "10.25.90.139",
          "doc_count" : 61
        },
        {
          "key" : "10.25.78.146",
          "doc_count" : 45
        },
        {
          "key" : "10.25.94.22",
          "doc_count" : 21
        },
        {
          "key" : "10.25.75.52",
          "doc_count" : 18
        },
        {
          "key" : "10.25.89.32",
          "doc_count" : 13
        },
        {
          "key" : "10.25.93.243",
          "doc_count" : 10
        },
        {
          "key" : "10.25.78.189",
          "doc_count" : 9
        },
        {
          "key" : "10.25.90.82",
          "doc_count" : 8
        },
        {
          "key" : "10.25.91.240",
          "doc_count" : 8
        },
        {
          "key" : "10.25.90.57",
          "doc_count" : 7
        },
        {
          "key" : "10.25.91.251",
          "doc_count" : 7
        },
        {
          "key" : "10.25.95.166",
          "doc_count" : 6
        },
        {
          "key" : "10.25.89.33",
          "doc_count" : 5
        },
        {
          "key" : "10.25.90.88",
          "doc_count" : 5
        },
        {
          "key" : "10.25.92.53",
          "doc_count" : 5
        }
      ]
    }
  }
}

每个IP登录人数超过2的IP

这个是对登录记录用户ID的去重数聚合,然后过滤。对用户ID进行去重可以使用Cardinality Aggregation聚合,然后再使用Bucket Selector Aggregation聚合过滤器过滤数据。具体内容如下: 查询语句

{
  "aggs": {
    "IP": {
      "terms": {
        "field": "IP",
        "size": 3000,
        "order": {
          "distinct": "desc"
        },
        "min_doc_count": 5
      },
      "aggs": {
        "distinct": {
          "cardinality": {
            "field": "IP.keyword"
          }
        },
        "dd":{
          "bucket_selector": {
            "buckets_path": {"userCount":"distinct"},
            "script": "params.userCount > 2"
          }
        }
      }
    }
  },
  "size": 0
}

结果

{
  "took" : 317,
  "timed_out" : false,
  "num_reduce_phases" : 3,
  "_shards" : {
    "total" : 1105,
    "successful" : 1105,
    "skipped" : 75,
    "failed" : 0
  },
  "hits" : {
    "total" : 2826,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "IP" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "10.25.75.52",
          "doc_count" : 18,
          "distinct" : {
            "value" : 4
          }
        },
        {
          "key" : "10.25.78.146",
          "doc_count" : 45,
          "distinct" : {
            "value" : 3
          }
        },
        {
          "key" : "10.25.90.139",
          "doc_count" : 61,
          "distinct" : {
            "value" : 3
          }
        },
        {
          "key" : "10.25.91.240",
          "doc_count" : 8,
          "distinct" : {
            "value" : 3
          }
        },
        {
          "key" : "10.25.94.22",
          "doc_count" : 21,
          "distinct" : {
            "value" : 3
          }
        }
      ]
    }
  }
}

桶聚合选择器: https://www.elastic.co/guide/en/elasticsearch/reference/6.8/search-aggregations-pipeline-bucket-selector-aggregation.html

我要对两个字段进行计算,然后对计算出来对新字段进行聚合

Elasticsearchdoom 回复了问题 • 6 人关注 • 3 个回复 • 221 次浏览 • 2019-09-27 18:20 • 来自相关话题

大佬们,聚合交集该怎么做呢

Elasticsearchdoom 回复了问题 • 2 人关注 • 1 个回复 • 128 次浏览 • 2019-09-27 16:49 • 来自相关话题

如何对聚合得到的结果再次统计

Elasticsearchziyou 回复了问题 • 4 人关注 • 2 个回复 • 1101 次浏览 • 2019-08-13 14:58 • 来自相关话题

对min_bucket聚合结果再次排序怎么做?

回复

Elasticsearchdlliwei 发起了问题 • 2 人关注 • 0 个回复 • 141 次浏览 • 2019-08-10 17:25 • 来自相关话题

elasticsearch同时对多个字段聚合(两个字段值类型一致)

ElasticsearchGod_lockin 回复了问题 • 2 人关注 • 1 个回复 • 278 次浏览 • 2019-08-03 18:20 • 来自相关话题

Elasticsearch使用类似having查询满足条件的数据

Elasticsearchmedcl 回复了问题 • 6 人关注 • 5 个回复 • 654 次浏览 • 2019-07-18 12:13 • 来自相关话题

关于elasticsearch二次聚合的问题

Elasticsearchbellengao 回复了问题 • 4 人关注 • 2 个回复 • 441 次浏览 • 2019-07-11 21:15 • 来自相关话题

请问如何按每天的时间段统计条目数量

Elasticsearchliutianhao 回复了问题 • 6 人关注 • 5 个回复 • 6436 次浏览 • 2019-05-10 16:31 • 来自相关话题

请教一个关于es的多字段聚合查询问题

ElasticsearchES1314 回复了问题 • 3 人关注 • 1 个回复 • 650 次浏览 • 2019-05-09 22:22 • 来自相关话题

有没老哥知道根据一个字段分组根据另一个字段聚合的玩法?

ElasticsearchGod_lockin 回复了问题 • 4 人关注 • 3 个回复 • 498 次浏览 • 2019-04-15 19:30 • 来自相关话题

请问聚合查询如何将buckets里面每个key的hits平铺并且按照_score进行排序

Elasticsearchlaoyang360 回复了问题 • 2 人关注 • 2 个回复 • 380 次浏览 • 2019-04-11 21:35 • 来自相关话题

es 是否可以实现在一条查询中支持 多个同级别的terms agg

Elasticsearchrochy 回复了问题 • 3 人关注 • 1 个回复 • 631 次浏览 • 2019-03-14 16:11 • 来自相关话题

初试es6.5.4的composite出错

Elasticsearchrochy 回复了问题 • 2 人关注 • 1 个回复 • 583 次浏览 • 2019-01-17 15:39 • 来自相关话题

条新动态, 点击查看
我知道了。
      "aggs": {
        "avg_rating": {
          "avg": {
            "script": &qu... 显示全部 »
我知道了。
      "aggs": {
        "avg_rating": {
          "avg": {
            "script": "_score"
          }
        }
      }
聚合部分这样写就可以了,如果script报
【scripts of type [inline], operation [aggs] and lang [groovy] are disabled】的错误
就在
就在配置文件elasticsearch.yml里

script.inline: on
script.indexed: on
script.engine.groovy.inline.aggs: on
script.engine.groovy.inline.update: on这几行配置加上然后重启els就可以了
 
我已经使用在了生产环境,是没有问题的。这个问题纠结了几天,终于找到方法了,跟帖在这里。
bsll

bsll 回答了问题 • 2016-11-16 17:14 • 2 个回复 不感兴趣

一个nested字段聚合父子字段

赞同来自:

写了一个demo,不过不知道你是不是这个意思。
DELETE /test_agg
PUT /test_agg
{
   "mappings": {
      "agg_type": {
          "... 显示全部 »
写了一个demo,不过不知道你是不是这个意思。
DELETE /test_agg
PUT /test_agg
{
   "mappings": {
      "agg_type": {
          "properties": {
          "all":{
              "type": "nested",
              "properties": {
                  "parent_id": {
                     "type": "integer"
                  },
                  "child_id": {
                     "type": "integer"
                  }
              }
          }
          }
      }
   }
}

POST /test_agg/agg_type/1
{
    "all":{
        "parent_id":1,
        "child_id":2
        
    }
}
POST /test_agg/agg_type/2
{
    "all":{
        "parent_id":1,
        "child_id":3
        
    }
}
POST /test_agg/agg_type/3
{
    "all":{
        "parent_id":2,
        "child_id":3
        
    }
}
POST /test_agg/_search
POST /test_agg/agg_type/_search
{
    "size": 0, 
   "aggs": {
      "category": {
         "aggs": {
            "term_list": {
               "terms": {
                  "field": "all.parent_id"
               },
               "aggs": {
                  "term_list": {
                     "terms": {
                        "field": "all.child_id"
                     }
                  }
               }
            }
         },
         "nested": {
            "path": "all"
         }
      }
   }
}
 
kennywu76

kennywu76 回答了问题 • 2017-06-28 11:16 • 4 个回复 不感兴趣

Elasticsearch聚合操作的时间复杂度是O(n)吗?

赞同来自:

terms聚合的时间复杂度不是一个简单的O能解释的,中间有好几个执行步骤,每个步骤的耗时也有多个决定因素。
 
最简单的单层terms聚合大致是下面这样一个执行步骤:
[list=1]
为要聚合的字段构造Global Ordinals。 (什么是Global... 显示全部 »
terms聚合的时间复杂度不是一个简单的O能解释的,中间有好几个执行步骤,每个步骤的耗时也有多个决定因素。
 
最简单的单层terms聚合大致是下面这样一个执行步骤:
[list=1]
为要聚合的字段构造Global Ordinals。 (什么是Global Ordinals参考 global-ordinals . ), 这个过程的速度不是单纯和文档数量有关系,更多的是取决于索引有多少个段文件,以及字段的不同唯一值的数量(cardinality)。  段文件的数量和磁盘IO能力决定了多快能将这些数据读入内存,而字段唯一值的多少决定了需要在内存里生成多少个分桶,唯一值越多,分桶占用的内存越高。
根据match查询的结果,也就是得到的文档ID集合,借助统计字段的doc values,拿到统计字段的值集合。
将统计字段的值集合映射到为global ordinals构建的分桶里。
统计各个分桶里的值个数.
根据聚合设置的size,返回top size的分桶数据。
[/list]
 
海量数据场景下,对Terms aggregation性能影响最大的还是对应字段的唯一值的多寡。 冷执行的情况下,由于需要读取各个segments的doc values,如果segments非常多,构造global ordinals可能耗时非常长。对于不再更新的索引,将其force merge成一个segment,可以免去global ordinals的构造过程,从而极大提速聚合速度。 对于一直在更新的索引,可以延长索引refresh周期,提高global ordinals缓存的有效期。 在查询聚合性能要求高于写入性能的场景下,也可以利用eager_global_ordinals来将构建时间移到索引阶段。
 
如果聚合的场景是从大量的数据中过滤出少量数据进行聚合(百万级),可以在执行参数里加入 execution_hint: map ,直接在结果集上用map的方式进行计算,对比默认的global ordinals的计算方式速度可能会高几倍到几十倍。
 
如果是多层聚合,则又要复杂得多,bucket构建过程分为depth first和breath first两种,建议仔细读一下相关文档,结合数据特性进行测试分析后,选用合适的执行方式。
 
总结来说对于terms aggregation,ES提供了多种执行方式,各种方式在内存使用方面,速度方面各有取舍。通常来说,默认的执行方式多数场景下都没有什么问题,只有一些比较极端的场景下,ES不会非常智能的自动选择最佳执行路径,需要使用者对数据和ES本身有一定熟悉程度,灵活选择。

ES Aggs 该如何根据查询结果的数量进行过滤

回复

Elasticsearchlaoyang360 回复了问题 • 3 人关注 • 2 个回复 • 156 次浏览 • 4 天前 • 来自相关话题

我要对两个字段进行计算,然后对计算出来对新字段进行聚合

回复

Elasticsearchdoom 回复了问题 • 6 人关注 • 3 个回复 • 221 次浏览 • 2019-09-27 18:20 • 来自相关话题

大佬们,聚合交集该怎么做呢

回复

Elasticsearchdoom 回复了问题 • 2 人关注 • 1 个回复 • 128 次浏览 • 2019-09-27 16:49 • 来自相关话题

如何对聚合得到的结果再次统计

回复

Elasticsearchziyou 回复了问题 • 4 人关注 • 2 个回复 • 1101 次浏览 • 2019-08-13 14:58 • 来自相关话题

对min_bucket聚合结果再次排序怎么做?

回复

Elasticsearchdlliwei 发起了问题 • 2 人关注 • 0 个回复 • 141 次浏览 • 2019-08-10 17:25 • 来自相关话题

elasticsearch同时对多个字段聚合(两个字段值类型一致)

回复

ElasticsearchGod_lockin 回复了问题 • 2 人关注 • 1 个回复 • 278 次浏览 • 2019-08-03 18:20 • 来自相关话题

Elasticsearch使用类似having查询满足条件的数据

回复

Elasticsearchmedcl 回复了问题 • 6 人关注 • 5 个回复 • 654 次浏览 • 2019-07-18 12:13 • 来自相关话题

关于elasticsearch二次聚合的问题

回复

Elasticsearchbellengao 回复了问题 • 4 人关注 • 2 个回复 • 441 次浏览 • 2019-07-11 21:15 • 来自相关话题

请问如何按每天的时间段统计条目数量

回复

Elasticsearchliutianhao 回复了问题 • 6 人关注 • 5 个回复 • 6436 次浏览 • 2019-05-10 16:31 • 来自相关话题

请教一个关于es的多字段聚合查询问题

回复

ElasticsearchES1314 回复了问题 • 3 人关注 • 1 个回复 • 650 次浏览 • 2019-05-09 22:22 • 来自相关话题

有没老哥知道根据一个字段分组根据另一个字段聚合的玩法?

回复

ElasticsearchGod_lockin 回复了问题 • 4 人关注 • 3 个回复 • 498 次浏览 • 2019-04-15 19:30 • 来自相关话题

请问聚合查询如何将buckets里面每个key的hits平铺并且按照_score进行排序

回复

Elasticsearchlaoyang360 回复了问题 • 2 人关注 • 2 个回复 • 380 次浏览 • 2019-04-11 21:35 • 来自相关话题

es 是否可以实现在一条查询中支持 多个同级别的terms agg

回复

Elasticsearchrochy 回复了问题 • 3 人关注 • 1 个回复 • 631 次浏览 • 2019-03-14 16:11 • 来自相关话题

初试es6.5.4的composite出错

回复

Elasticsearchrochy 回复了问题 • 2 人关注 • 1 个回复 • 583 次浏览 • 2019-01-17 15:39 • 来自相关话题

elasticsearach在聚合时为什么没有去除停用词

回复

Elasticsearchrochy 回复了问题 • 3 人关注 • 1 个回复 • 355 次浏览 • 2018-11-12 23:31 • 来自相关话题

ES Aggs根据聚合的结果(数值)进行过滤

Elasticsearchziyou 发表了文章 • 1 个评论 • 143 次浏览 • 5 天前 • 来自相关话题

前言

我们在使用聚合时总是有各种各样的聚合需求,其中一个比较常用的就是根据聚合的结果过滤聚合的桶,例如:1、每个IP登录次数超过5次的IP;2、每个IP登录人数超过2的IP。 还有我之前的一个案例,访问量超过1000的人数,这些都是很常见的统计需求。

案例需求

我们在使用聚合计算的时候一般都有两类,一种是计算文档的数量,另一种是计算文档内字段的值的数量(去重计算)或者值的数学计算。两种聚合计算在过滤的时候采用不同的方法来计算。

我们使用以下案例来说明两种过滤的不同: 用户每次登录都会记录一个登录记录:

{"userID":"a","IP":"10.70.25.1","time":"2019-10-10 12:12:12.222"}

然后提出以下两个需求: 1、每个IP登录次数超过5次的IP; 2、每个IP登录人数超过2的IP。

实现

每个IP登录次数超过5次的IP

这个是对登录记录个数的桶聚合统计,然后过滤。使用IP做term聚合,就可以得出每个IP的登录次数,然后term聚合中有一个参数min_doc_count这个字段就可以对文档数量进行过滤,具体的语句如下: 查询语句

{
  "aggs": {
    "IP": {
      "terms": {
        "field": "IP",
        "size": 3000,
        "order": {
          "_count": "desc"
        },
        "min_doc_count": 5
      }
    }
  },
  "size": 0
}

结果

{
  "took" : 614,
  "timed_out" : false,
  "num_reduce_phases" : 3,
  "_shards" : {
    "total" : 1105,
    "successful" : 1105,
    "skipped" : 75,
    "failed" : 0
  },
  "hits" : {
    "total" : 2826,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "IP" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "10.25.90.139",
          "doc_count" : 61
        },
        {
          "key" : "10.25.78.146",
          "doc_count" : 45
        },
        {
          "key" : "10.25.94.22",
          "doc_count" : 21
        },
        {
          "key" : "10.25.75.52",
          "doc_count" : 18
        },
        {
          "key" : "10.25.89.32",
          "doc_count" : 13
        },
        {
          "key" : "10.25.93.243",
          "doc_count" : 10
        },
        {
          "key" : "10.25.78.189",
          "doc_count" : 9
        },
        {
          "key" : "10.25.90.82",
          "doc_count" : 8
        },
        {
          "key" : "10.25.91.240",
          "doc_count" : 8
        },
        {
          "key" : "10.25.90.57",
          "doc_count" : 7
        },
        {
          "key" : "10.25.91.251",
          "doc_count" : 7
        },
        {
          "key" : "10.25.95.166",
          "doc_count" : 6
        },
        {
          "key" : "10.25.89.33",
          "doc_count" : 5
        },
        {
          "key" : "10.25.90.88",
          "doc_count" : 5
        },
        {
          "key" : "10.25.92.53",
          "doc_count" : 5
        }
      ]
    }
  }
}

每个IP登录人数超过2的IP

这个是对登录记录用户ID的去重数聚合,然后过滤。对用户ID进行去重可以使用Cardinality Aggregation聚合,然后再使用Bucket Selector Aggregation聚合过滤器过滤数据。具体内容如下: 查询语句

{
  "aggs": {
    "IP": {
      "terms": {
        "field": "IP",
        "size": 3000,
        "order": {
          "distinct": "desc"
        },
        "min_doc_count": 5
      },
      "aggs": {
        "distinct": {
          "cardinality": {
            "field": "IP.keyword"
          }
        },
        "dd":{
          "bucket_selector": {
            "buckets_path": {"userCount":"distinct"},
            "script": "params.userCount > 2"
          }
        }
      }
    }
  },
  "size": 0
}

结果

{
  "took" : 317,
  "timed_out" : false,
  "num_reduce_phases" : 3,
  "_shards" : {
    "total" : 1105,
    "successful" : 1105,
    "skipped" : 75,
    "failed" : 0
  },
  "hits" : {
    "total" : 2826,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "IP" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "10.25.75.52",
          "doc_count" : 18,
          "distinct" : {
            "value" : 4
          }
        },
        {
          "key" : "10.25.78.146",
          "doc_count" : 45,
          "distinct" : {
            "value" : 3
          }
        },
        {
          "key" : "10.25.90.139",
          "doc_count" : 61,
          "distinct" : {
            "value" : 3
          }
        },
        {
          "key" : "10.25.91.240",
          "doc_count" : 8,
          "distinct" : {
            "value" : 3
          }
        },
        {
          "key" : "10.25.94.22",
          "doc_count" : 21,
          "distinct" : {
            "value" : 3
          }
        }
      ]
    }
  }
}

桶聚合选择器: https://www.elastic.co/guide/en/elasticsearch/reference/6.8/search-aggregations-pipeline-bucket-selector-aggregation.html

ElasticSearch java API - 聚合查询

Elasticsearchcarlislelee 发表了文章 • 3 个评论 • 35481 次浏览 • 2016-09-20 17:16 • 来自相关话题

以球员信息为例,player索引的player type包含5个字段,姓名,年龄,薪水,球队,场上位置。 index的mapping为:
"mappings": {
	"player": {
		"properties": {
			"name": {
				"index": "not_analyzed",
				"type": "string"
			},
			"age": {
				"type": "integer"
			},
			"salary": {
				"type": "integer"
			},
			"team": {
				"index": "not_analyzed",
				"type": "string"
			},
			"position": {
				"index": "not_analyzed",
				"type": "string"
			}
		},
		"_all": {
			"enabled": false
		}
	}
}
索引中的全部数据:
微信截图_20160920171030.png
  首先,初始化Builder:
SearchRequestBuilder sbuilder = client.prepareSearch("player").setTypes("player");
接下来举例说明各种聚合操作的实现方法,因为在es的api中,多字段上的聚合操作需要用到子聚合(subAggregation),初学者可能找不到方法(网上资料比较少,笔者在这个问题上折腾了两天,最后度了源码才彻底搞清楚T_T),后边会特意说明多字段聚合的实现方法。另外,聚合后的排序也会单独说明。
  • group by/count
例如要计算每个球队的球员数,如果使用SQL语句,应表达如下:
select team, count(*) as player_count from player group by team;
ES的java api:
TermsBuilder teamAgg= AggregationBuilders.terms("player_count ").field("team");
sbuilder.addAggregation(teamAgg);
SearchResponse response = sbuilder.execute().actionGet();
 
  • group by多个field
例如要计算每个球队每个位置的球员数,如果使用SQL语句,应表达如下:
select team, position, count(*) as pos_count from player group by team, position;
ES的java api:
TermsBuilder teamAgg= AggregationBuilders.terms("player_count ").field("team");
TermsBuilder posAgg= AggregationBuilders.terms("pos_count").field("position");
sbuilder.addAggregation(teamAgg.subAggregation(posAgg));
SearchResponse response = sbuilder.execute().actionGet();
 
  • max/min/sum/avg
例如要计算每个球队年龄最大/最小/总/平均的球员年龄,如果使用SQL语句,应表达如下:
select team, max(age) as max_age from player group by team;
ES的java api:
TermsBuilder teamAgg= AggregationBuilders.terms("player_count ").field("team");
MaxBuilder ageAgg= AggregationBuilders.max("max_age").field("age");
sbuilder.addAggregation(teamAgg.subAggregation(ageAgg));
SearchResponse response = sbuilder.execute().actionGet();
  • 对多个field求max/min/sum/avg
例如要计算每个球队球员的平均年龄,同时又要计算总年薪,如果使用SQL语句,应表达如下:
select team, avg(age)as avg_age, sum(salary) as total_salary from player group by team;
ES的java api:
TermsBuilder teamAgg= AggregationBuilders.terms("team");
AvgBuilder ageAgg= AggregationBuilders.avg("avg_age").field("age");
SumBuilder salaryAgg= AggregationBuilders.avg("total_salary ").field("salary");
sbuilder.addAggregation(teamAgg.subAggregation(ageAgg).subAggregation(salaryAgg));
SearchResponse response = sbuilder.execute().actionGet();
 
  • 聚合后对Aggregation结果排序
例如要计算每个球队总年薪,并按照总年薪倒序排列,如果使用SQL语句,应表达如下:
select team, sum(salary) as total_salary from player group by team order by total_salary desc;
ES的java api:
TermsBuilder teamAgg= AggregationBuilders.terms("team").order(Order.aggregation("total_salary ", false);
SumBuilder salaryAgg= AggregationBuilders.avg("total_salary ").field("salary");
sbuilder.addAggregation(teamAgg.subAggregation(salaryAgg));
SearchResponse response = sbuilder.execute().actionGet();
需要特别注意的是,排序是在TermAggregation处执行的,Order.aggregation函数的第一个参数是aggregation的名字,第二个参数是boolean型,true表示正序,false表示倒序。 
  • Aggregation结果条数的问题
默认情况下,search执行后,仅返回10条聚合结果,如果想反悔更多的结果,需要在构建TermsBuilder 时指定size:
TermsBuilder teamAgg= AggregationBuilders.terms("team").size(15);
 
  • Aggregation结果的解析/输出
得到response后:
Map<String, Aggregation> aggMap = response.getAggregations().asMap();
StringTerms teamAgg= (StringTerms) aggMap.get("keywordAgg");
Iterator<Bucket> teamBucketIt = teamAgg.getBuckets().iterator();
while (teamBucketIt .hasNext()) {
Bucket buck = teamBucketIt .next();
//球队名
String team = buck.getKey();
//记录数
long count = buck.getDocCount();
//得到所有子聚合
Map subaggmap = buck.getAggregations().asMap();
//avg值获取方法
double avg_age= ((InternalAvg) subaggmap.get("avg_age")).getValue();
//sum值获取方法
double total_salary = ((InternalSum) subaggmap.get("total_salary")).getValue();
//...
//max/min以此类推
}
 
  • 总结
综上,聚合操作主要是调用了SearchRequestBuilder的addAggregation方法,通常是传入一个TermsBuilder,子聚合调用TermsBuilder的subAggregation方法,可以添加的子聚合有TermsBuilder、SumBuilder、AvgBuilder、MaxBuilder、MinBuilder等常见的聚合操作。   从实现上来讲,SearchRequestBuilder在内部保持了一个私有的 SearchSourceBuilder实例, SearchSourceBuilder内部包含一个List<AbstractAggregationBuilder>,每次调用addAggregation时会调用 SearchSourceBuilder实例,添加一个AggregationBuilder。 同样的,TermsBuilder也在内部保持了一个List<AbstractAggregationBuilder>,调用addAggregation方法(来自父类addAggregation)时会添加一个AggregationBuilder。有兴趣的读者也可以阅读源码的实现。   如果有什么问题,欢迎一起讨论,如果文中有什么错误,欢迎批评指正。   注:文中使用的Elastic Search API版本为2.3.2