类型

ES 中聚合的类型主要有以下 3 种:

  • Metric Aggregations: 提供求 sum(求总和)、average(求平均数) 等数学运算,可以对字段进行统计分析。
  • Bucket Aggregations:分桶类型,对满足特定条件的文档进行分组,例如将 A 出版社的书本分为一组,将 B 出版社的书本分为一组,类似于 SQL 里的 Group By 功能。
  • Pipeline Aggregations:对其他聚合输出的结果进行再次聚合。
  • Matrⅸ,矩阵分析类型

ES 的聚合可以进行多种组合来构建的统计查询,从而解决复杂的统计分析的需求
image.png

简介

聚合分析是数据库中重要的功能特性,完成对一个查询的数据集中数据的聚合计算,如:找出某字段 (或计算表达式的结果)的最大值、最小值,计算和、平均值等

  • 聚合的两个主要的概念,分别是 桶 和 指标
    • 桶(Buckets) : 满足特定条件的文档的集合
      • 当聚合开始被执行,每个文档会决定符合哪个桶的条件,如果匹配到,文档将放入相应的桶并接着进行聚合操作
        • 像是一个员工属于男性桶或者女性桶,日期2014-10-28属于十月桶,也属于2014年桶
      • 桶可以被嵌套在其他桶里面
        • 像是北京能放在中国桶裡,而中国桶能放在亚洲桶裡
      • Elasticsearch提供了很多种类型的桶,像是时间、最受欢迎的词、年龄区间、地理位置桶等等,不过他们在根本上都是通过同样的原理进行操作,也就是基于条件来划分文档,一个文档只要符合条件,就可以加入那个桶,因此一个文档可以同时加入很多桶
    • 指标(Metrics) : 对桶内的文档进行统计计算
      • 桶能让我们划分文档到有意义的集合, 但是最终我们需要的是对这些桶内的文档进行一些指标的计算
      • 指标通常是简单的数学运算(像是min、max、avg、sum),而这些是通过当前桶中的文档的值来计算的,利用指标能让你计算像平均薪资、最高出售价格、95%的查询延迟这样的数据

aggs 聚合的模板

  • 当query和aggs一起存在时,会先执行query的主查询,主查询query执行完后会搜出一批结果,而这些结果才会被拿去aggs拿去做聚合
    • 另外要注意aggs后面会先接一层自定义的这个聚合的名字,然后才是接上要使用的聚合桶
    • 如果有些情况不在意查询结果是什麽,而只在意aggs的结果,可以把size设为0,如此可以让返回的hits结果集是0,加快返回的速度
  • 一个aggs裡可以有很多个聚合,每个聚合彼此间都是独立的,因此可以一个聚合拿来统计数量、一个聚合拿来分析数据、一个聚合拿来计算标准差…,让一次搜索就可以把想要做的事情一次做完
  • aggs可以嵌套在其他的aggs裡面,而嵌套的桶能作用的文档集范围,是外层的桶所输出的结果集

语法:
在查询请求体中以aggregations节点按如下语法定义聚合分析:
image.png
aggregations 也可简写为 aggs

  1. "aggregations": {
  2. "<aggregation_name>": { <!--聚合的名字 -->
  3. "<aggregation_type>" : { <!--聚合的类型 -->
  4. <aggregation_body> <!--聚合体:对哪些字段进行聚合 -->
  5. }
  6. [,"meta" : { [<meta_data_body>] } ]? <!--元 -->
  7. [,"aggregations" : { [<sub_aggregation>]+ } ]? <!--在聚合里面在定义子聚合 -->
  8. }
  9. [, "<aggregation_name_2>": { ...}] * <!--聚合的名字 -->
  10. }
  1. GET 127.0.0.1/mytest/doc/_search
  2. {
  3. "query": { ... },
  4. "size": 0, //建议设置为0,这样不会返回 _source
  5. "aggs": { //和query 同级别的关键词
  6. "custom_name1": { //aggs后面接著的是一个自定义的name,会从聚合结果中返回
  7. "桶": { ... } //再来才是接桶
  8. },
  9. "custom_name2": { //一个aggs裡可以有很多聚合,可以进行多个同级别的聚合查询
  10. "aggs_type": { //聚合的定义:聚合类型 + 聚合body
  11. aggs body
  12. }
  13. },
  14. "custom_name3": { //自定义的聚合名字,会从聚合结果中返回
  15. "桶": {
  16. .....
  17. },
  18. "aggs": { //aggs可以嵌套在别的aggs裡面, 子聚合
  19. "in_name": { //记得使用aggs需要先自定义一个name,会从聚合结果中返回
  20. "桶": { ... } //in_name的桶作用的文档是custom_name3的桶的结果
  21. }
  22. }
  23. }
  24. }
  25. }
  26. # 返回
  27. {
  28. "hits": {
  29. "total": 8,
  30. "max_score": 0,
  31. "hits": [] //因为size设为0,所以没有查询结果返回
  32. },
  33. "aggregations": {
  34. "custom_name1": {
  35. ...
  36. },
  37. "custom_name2": {
  38. ...
  39. },
  40. "custom_name3": {
  41. ... ,
  42. "in_name": {
  43. ....
  44. }
  45. }
  46. }
  47. }

聚合结果排序

对嵌套计算出的avg(balance),这里是average_balance,进行排序

  1. GET /bank/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "group_by_state": {
  6. "terms": {
  7. "field": "state.keyword",
  8. "order": {
  9. "average_balance": "desc"
  10. }
  11. },
  12. "aggs": {
  13. "average_balance": {
  14. "avg": {
  15. "field": "balance"
  16. }
  17. }
  18. }
  19. }
  20. }
  21. }

例子

准备数据

  1. POST /test-agg-cars/_bulk
  2. { "index": {}}
  3. { "price" : 10000, "color" : "red", "make" : "honda", "sold" : "2014-10-28" }
  4. { "index": {}}
  5. { "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
  6. { "index": {}}
  7. { "price" : 30000, "color" : "green", "make" : "ford", "sold" : "2014-05-18" }
  8. { "index": {}}
  9. { "price" : 15000, "color" : "blue", "make" : "toyota", "sold" : "2014-07-02" }
  10. { "index": {}}
  11. { "price" : 12000, "color" : "green", "make" : "toyota", "sold" : "2014-08-19" }
  12. { "index": {}}
  13. { "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
  14. { "index": {}}
  15. { "price" : 80000, "color" : "red", "make" : "bmw", "sold" : "2014-01-01" }
  16. { "index": {}}
  17. { "price" : 25000, "color" : "blue", "make" : "ford", "sold" : "2014-02-12" }

标准的聚合

有了数据,开始构建我们的第一个聚合。汽车经销商可能会想知道哪个颜色的汽车销量最好,用聚合可以轻易得到结果,用 terms 桶操作:

  1. 聚合操作被置于顶层参数 aggs 之下(如果你愿意,完整形式 aggregations 同样有效)。
  2. 然后,可以为聚合指定一个我们想要名称,本例中是: popular_colors 。
  3. 最后,定义单个桶的类型 terms 。

    1. GET /test-agg-cars/_search
    2. {
    3. "size" : 0,
    4. "aggs" : {
    5. "popular_colors" : {
    6. "terms" : {
    7. "field" : "color.keyword"
    8. }
    9. }
    10. }
    11. }

    image.png

  4. 因为我们设置了 size 参数,所以不会有 hits 搜索结果返回。

  5. popular_colors 聚合是作为 aggregations 字段的一部分被返回的。
  6. 每个桶的 key 都与 color 字段里找到的唯一词对应。它总会包含 doc_count 字段,告诉我们包含该词项的文档数量。
  7. 每个桶的数量代表该颜色的文档数量。

    多个聚合

    同时计算两种桶的结果:对color和对make
    1. GET /test-agg-cars/_search
    2. {
    3. "size" : 0,
    4. "aggs" : {
    5. "popular_colors" : {
    6. "terms" : {
    7. "field" : "color.keyword"
    8. }
    9. },
    10. "make_by" : {
    11. "terms" : {
    12. "field" : "make.keyword"
    13. }
    14. }
    15. }
    16. }

    聚合的嵌套

    这个新的聚合层让我们可以将 avg 度量嵌套置于 terms 桶内。实际上,这就为每个颜色生成了平均价格。
    1. GET /test-agg-cars/_search
    2. {
    3. "size" : 0,
    4. "aggs": {
    5. "colors": {
    6. "terms": {
    7. "field": "color.keyword"
    8. },
    9. "aggs": {
    10. "avg_price": {
    11. "avg": {
    12. "field": "price"
    13. }
    14. }
    15. }
    16. }
    17. }
    18. }
    image.png
    正如 颜色 的例子,我们需要给度量起一个名字( avg_price )这样可以稍后根据名字获取它的值。最后,我们指定度量本身( avg )以及我们想要计算平均值的字段( price )

    动态脚本的聚合

    这个例子告诉你,ElasticSearch还支持一些基于脚本(生成运行时的字段)的复杂的动态聚合。
    1. GET /test-agg-cars/_search
    2. {
    3. "runtime_mappings": {
    4. "make.length": {
    5. "type": "long",
    6. "script": "emit(doc['make.keyword'].value.length())"
    7. }
    8. },
    9. "size" : 0,
    10. "aggs": {
    11. "make_length": {
    12. "histogram": {
    13. "interval": 1,
    14. "field": "make.length"
    15. }
    16. }
    17. }
    18. }
    image.png