转自:https://blog.csdn.net/u013089490/article/details/84322556

1、聚合中基本概念

ES中的聚合,包含多种类型,最常用的两种,一个叫桶,一个叫度量。

1.1、桶bucket

  1. 桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个`桶`,例如我们根据国籍对人划分,可以得到`中国桶``英国桶``日本桶`……或者我们按照年龄段对人进行划分:0~10,10~20,20~30,30~40等。<br />Elasticsearch中提供的划分桶的方式有很多:<br />- Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组;<br />- Histogram Aggregation:根据数值阶梯分组,与日期类似;<br />- Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组;<br />- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组;

1.2、度量metrics

分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为度量
比较常用的一些度量聚合方式:
- Avg Aggregation:求平均值
- Max Aggregation:求最大值
- Min Aggregation:求最小值
- Percentiles Aggregation:求百分比
- Stats Aggregation:同时返回avg、max、min、sum、count等
- Sum Aggregation:求和
- Top hits Aggregation:求前几
- Value Count Aggregation:求总数
- ……

1.3、测试数据

  1. POST /cars/transactions/_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" }

初始化数据

  1. POST /cars/transactions/_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" }

2、聚合为桶

首先按照汽车的颜色color来划分桶。

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

【注意】
(1)size:查询条数,这里设置为0,因为不关心搜索到的结果,只关心聚合结果,提供效率;
(2)aggs:声明这是一个聚合查询,是aggregations的缩写;

  • popular_colors:这次聚合的名称,可以自定义;
  • terms:划分桶的方式,这里是根据词条划分;
  • field:划分桶的字段。

【查询结果分析】

  1. {
  2. "took": 20,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": 8,
  12. "max_score": 0,
  13. "hits": []
  14. },
  15. "aggregations": {
  16. "popular_colors": {
  17. "doc_count_error_upper_bound": 0,
  18. "sum_other_doc_count": 0,
  19. "buckets": [
  20. {
  21. "key": "red",
  22. "doc_count": 4
  23. },
  24. {
  25. "key": "blue",
  26. "doc_count": 2
  27. },
  28. {
  29. "key": "green",
  30. "doc_count": 2
  31. }
  32. ]
  33. }
  34. }
  35. }
  1. hits:查询结果为空,因为我们设置了size为0
  2. aggregations:聚合的结果;
  3. popular_colors:我们定义的聚合名称;
  4. buckets:查找到的桶,每个不同的color字段值都会形成一个桶;
  5. key:这个桶对应的color字段的值;
  6. doc_count:这个桶中的文档数量;

    3、桶内度量

    前面的例子告诉我们每个桶里面的文档数量,这很有用。 但通常,我们的应用需要提供更复杂的文档度量。 例如,每种颜色汽车的平均价格是多少?因此,我们需要告诉Elasticsearch使用哪个字段使用何种度量方式进行运算,这些信息要嵌套在内,度量的运算会基于内的文档进行;现在,我们为刚刚的聚合结果添加 求价格平均值的度量: ```bash GET /cars/_search { “size” : 0, “aggs” : {
    1. "popular_colors" : {
    2. "terms" : {
    3. "field" : "color"
    4. },
    5. "aggs":{
    6. "avg_price": {
    7. "avg": {
    8. "field": "price"
    9. }
    10. }
    11. }
    12. }
    } }
    #
  • aggs:我们在上一个aggs(popular_colors)中添加新的aggs。可见度量也是一个聚合,度量是在桶内的聚合
  • avg_price:聚合的名称
  • avg:度量的类型,这里是求平均值
  • field:度量运算的字段
    1. 【结果】
    2. ```json
    3. {
    4. "took": 13,
    5. "timed_out": false,
    6. "_shards": {
    7. "total": 1,
    8. "successful": 1,
    9. "skipped": 0,
    10. "failed": 0
    11. },
    12. "hits": {
    13. "total": 8,
    14. "max_score": 0,
    15. "hits": []
    16. },
    17. "aggregations": {
    18. "popular_colors": {
    19. "doc_count_error_upper_bound": 0,
    20. "sum_other_doc_count": 0,
    21. "buckets": [
    22. {
    23. "key": "red",
    24. "doc_count": 4,
    25. "avg_price": {
    26. "value": 32500
    27. }
    28. },
    29. {
    30. "key": "blue",
    31. "doc_count": 2,
    32. "avg_price": {
    33. "value": 20000
    34. }
    35. },
    36. {
    37. "key": "green",
    38. "doc_count": 2,
    39. "avg_price": {
    40. "value": 21000
    41. }
    42. }
    43. ]
    44. }
    45. }
    46. }
    47. #####################################################
    48. 可以看到每个桶中都有自己的`avg_price`字段,这是度量聚合的结果

4、桶内嵌套桶

刚刚的案例中,我们在桶内嵌套度量运算。事实上桶不仅可以嵌套运算, 还可以再嵌套其它桶。也就是说在每个分组中,再分更多组。比如:我们想统计每种颜色的汽车中,分别属于哪个制造商,按照make字段再进行分桶。

  1. GET /cars/_search
  2. {
  3. "size" : 0,
  4. "aggs" : {
  5. "popular_colors" : {
  6. "terms" : {
  7. "field" : "color"
  8. },
  9. "aggs":{
  10. "avg_price": {
  11. "avg": {
  12. "field": "price"
  13. }
  14. },
  15. "maker":{
  16. "terms":{
  17. "field":"make"
  18. }
  19. }
  20. }
  21. }
  22. }
  23. }
  24. ############################################
  25. - 原来的color桶和avg计算我们不变
  26. - maker:在嵌套的aggs下新添一个桶,叫做maker
  27. - terms:桶的划分类型依然是词条
  28. - filed:这里根据make字段进行划分

5、划分桶

常见桶的划分
- Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
- Histogram Aggregation:根据数值阶梯分组,与日期类似
- Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组
- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组

5.1、阶梯分桶Histogram

histogram是把数值类型的字段,按照一定的阶梯大小进行分组。你需要指定一个阶梯值(interval)来划分阶梯大小。

  1. GET /cars/_search
  2. {
  3. "size":0,
  4. "aggs":{
  5. "price":{
  6. "histogram": {
  7. "field": "price",
  8. "interval": 5000
  9. }
  10. }
  11. }
  12. }
  13. #结果
  14. {
  15. "took": 21,
  16. "timed_out": false,
  17. "_shards": {
  18. "total": 5,
  19. "successful": 5,
  20. "skipped": 0,
  21. "failed": 0
  22. },
  23. "hits": {
  24. "total": 8,
  25. "max_score": 0,
  26. "hits": []
  27. },
  28. "aggregations": {
  29. "price": {
  30. "buckets": [
  31. {
  32. "key": 10000,
  33. "doc_count": 2
  34. },
  35. {
  36. "key": 15000,
  37. "doc_count": 1
  38. },
  39. {
  40. "key": 20000,
  41. "doc_count": 2
  42. },
  43. {
  44. "key": 25000,
  45. "doc_count": 1
  46. },
  47. {
  48. "key": 30000,
  49. "doc_count": 1
  50. },
  51. {
  52. "key": 35000,
  53. "doc_count": 0
  54. },
  55. {
  56. "key": 40000,
  57. "doc_count": 0
  58. },
  59. {
  60. "key": 45000,
  61. "doc_count": 0
  62. },
  63. {
  64. "key": 50000,
  65. "doc_count": 0
  66. },
  67. {
  68. "key": 55000,
  69. "doc_count": 0
  70. },
  71. {
  72. "key": 60000,
  73. "doc_count": 0
  74. },
  75. {
  76. "key": 65000,
  77. "doc_count": 0
  78. },
  79. {
  80. "key": 70000,
  81. "doc_count": 0
  82. },
  83. {
  84. "key": 75000,
  85. "doc_count": 0
  86. },
  87. {
  88. "key": 80000,
  89. "doc_count": 1
  90. }
  91. ]
  92. }
  93. }
  94. }

你会发现,中间有大量的文档数量为0 的桶,看起来很丑。我们可以增加一个参数min_doc_count为1,来约束最少文档数量为1,这样文档数量为0的桶会被过滤;示例:

  1. GET /cars/_search
  2. {
  3. "size":0,
  4. "aggs":{
  5. "price":{
  6. "histogram": {
  7. "field": "price",
  8. "interval": 5000,
  9. "min_doc_count": 1
  10. }
  11. }
  12. }
  13. }

5.2、范围分桶range

范围分桶与阶梯分桶类似,也是把数字按照阶段进行分组,只不过range方式需要你自己指定每一组的起始和结束大小。