ElasticSearch - 聚合 aggs 详解

聚合概念

  • ElasticSearch除了致力于搜索之外,也提供了聚合实时分析数据的功能

    • 如果把搜索比喻为大海捞针(从海量的文档中找出符合条件的那一个),那麽聚合就是去分析大海中的针们的特性,像是

      • 在大海里有多少针?

      • 针的平均长度是多少?

      • 按照针的製造商来划分,针的长度中位值是多少?

      • 每月加入到海中的针有多少?

      • 这里面有异常的针麽?

    • 因此透过聚合,我们可以得到一个数据的概览,聚合能做的是分析和总结全套的数据,而不是查找单个文档(这是搜索做的事)

  • 聚合允许我们向数据提出一些複杂的问题,虽然他的功能完全不同于搜索,但他们其实使用了相同的数据结构,这表示聚合的执行速度很快,并且就像搜索一样几乎是实时的

    • 并且由于聚合和搜索是使用同样的数据结构,因此聚合和搜索可以是一起执行的

    • 这表示我们可以在一次json请求裡,同时对相同的数据进行 搜索/过滤 + 分析,两个愿望一次满足

  • 聚合的两个主要的概念,分别是 桶 和 指标

    • 桶(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裡可以有很多个聚合,每个聚合彼此间都是独立的,因此可以一个聚合拿来统计数量、一个聚合拿来分析数据、一个聚合拿来计算标准差...,让一次搜索就可以把想要做的事情一次做完

      • 像是此例就定义了3个聚合,分别是custom_name1、custom_name2、custom_name3

    • aggs可以嵌套在其他的aggs裡面,而嵌套的桶能作用的文档集范围,是外层的桶所输出的结果集

     
    1. GET 127.0.0.1/mytest/doc/_search

    2. {

    3.    "query": { ... },

    4.    "size": 0,

    5.    "aggs": {

    6.        "custom_name1": {  //aggs后面接著的是一个自定义的name

    7.       "桶": { ... }  //再来才是接桶

    8.       },

    9.        "custom_name2": {  //一个aggs裡可以有很多聚合

    10.            "桶": { ... }

    11.       },

    12.        "custom_name3": {

    13.            "桶": {

    14.               .....

    15.           },

    16.            "aggs": {  //aggs可以嵌套在别的aggs裡面

    17.                "in_name": { //记得使用aggs需要先自定义一个name

    18.                    "桶": { ... } //in_name的桶作用的文档是custom_name3的桶的结果

    19.               }

    20.           }

    21.       }

    22.   }

    23. }

    • 结果

       
      1. {

      2.   "hits": {

      3.       "total": 8,

      4.       "max_score": 0,

      5.       "hits": [] //因为size设为0,所以没有查询结果返回

      6.   },

      7.   "aggregations": {

      8.       "custom_name1": {

      9.           ...

      10.       },

      11.       "custom_name2": {

      12.           ...

      13.       },

      14.       "custom_name3": {

      15.           ... ,

      16.           "in_name": {

      17.              ....

      18.           }

      19.       }

      20.   }

      21. }

聚合中常用的桶 terms、filter、top_hits

  • terms桶 : 针对某个field的值进行分组,field有几种值就分成几组

    • terms桶在进行分组时,会爲此field中的每种值创建一个新的桶

    • 要注意此 "terms桶" 和平常用在主查询query中的 "查找terms" 是不同的东西

    • 具体实例

      • 首先插入几笔数据,其中color是一个keyword类型

         
        1. { "color": "red" }

        2. { "color": "green" }

        3. { "color": ["red", "blue"] }

      • 执行terms聚合

         
        1. GET 127.0.0.1/mytest/doc/_search

        2. {

        3.    "query": {

        4.        "match_all": {}

        5.   },

        6.    "size": 0,

        7.    "aggs": {

        8. "my_name": {

        9.            "terms": {

        10.           "field": "color" //使用color来进行分组

        11.           }

        12.       }

        13.   }

        14. }

      • 结果

        • 因为color总共有3种值,red、blue、green,所以terms桶为他们产生了3个bucket,并计算了每个bucket中符合的文档有哪些

        • bucket和bucket间是独立的,也就是说一个文档可以同时符合好几个bucket,像是{"color": ["red", "blue"]}就同时符合了red和blue bucket

         
        1. "aggregations": {

        2.    "my_name": {

        3.        "doc_count_error_upper_bound": 0,

        4.        "sum_other_doc_count": 0,

        5.        "buckets": [

        6.           {

        7.                "key": "blue",

        8.                "doc_count": 1

        9.           },

        10.           {

        11.                "key": "red",

        12.                "doc_count": 2  //表示color为red的文档有2个,此例中就是 {"color": "red"} 和 {"color": ["red", "blue"]}这两个文档

        13.           },

        14.           {

        15.                "key": "green",

        16.                "doc_count": 1

        17.           }

        18.       ]

        19.   }

        20. }

    • 具体实例二

      • 将terms桶搭配度量指标(avg、min、max、sum...)一起使用

        • 其实度量指标也可以看成一种"桶",他可以和其他正常的桶们进行嵌套作用,差别只在指标关注的是这些文档中的某个数值的统计,而桶关注的是文档

      • 首先准备数据,color一样为keyword类型,而price为integer类型

         
        1. { "color": "red", "price": 100 }

        2. { "color": "green", "price": 500 }

        3. { "color": ["red", "blue"], "price": 1000 }

      • 将avg指标嵌套在terms桶裡一起使用

         
        1. GET 127.0.0.1/mytest/doc/_search

        2. {

        3.    "query": {

        4.        "match_all": {}

        5.   },

        6.    "size": 0,

        7.    "aggs": {

        8.        "my_name": {

        9.            "terms": {

        10.                "field": "color"

        11.           },

        12.            "aggs": {  //嵌套两个指标avg、min在terms桶中

        13.                "my_avg_price": { //my_avg_price计算每个bucket的平均price

        14.                    "avg": {

        15.                        "field": "price"

        16.                   }

        17.               },

        18.                "my_min_price": { //my_min_price计算每个bucket中的最小price

        19.               "min": {

        20.                        "field": "price"

        21.                   }

        22.               }

        23.           }

        24.       }

        25.   }

        26. }

      • 结果

         
        1. "aggregations": {

        2.    "my_name": {

        3.        "doc_count_error_upper_bound": 0,

        4.        "sum_other_doc_count": 0,

        5.        "buckets": [ //terms桶中的每个bucket都会计算avg和min两个指标

        6.           {

        7.                "key": "blue",

        8.                "doc_count": 1,

        9.                "my_avg_price": { //avg指标

        10.                    "value": 1000

        11.               },

        12.                "my_min_price": { //min指标

        13.                    "value": 100

        14.               }

        15.           },

        16.           {

        17.                "key": "red",

        18.                "doc_count": 2,

        19.                "my_avg_price": {  //avg指标计算的值,因为符合color为red的文档有两笔,所以平均price为100+1000/2 = 550

        20.                    "value": 550

        21.               },

        22.                "my_min_price": {

        23.                    "value": 100

        24.               }

        25.           },

        26.           {

        27.                "key": "green",

        28.                "doc_count": 1,

        29.                "my_avg_price": {

        30.                    "value": 500

        31.               },

        32.                "my_min_price": {

        33.                    "value": 500

        34.               }

        35.           }

        36.       ]

        37.   }

        38. }

  • filter桶 : 一个用来过滤的桶

    • 要注意此处的 "filter桶" 和用在主查询query的 "过滤filter" 的用法是一模一样的,都是过滤

    • 不过差别是 "filter桶" 会自己给创建一个新的桶,而不会像 "过滤filter" 一样依附在query下

      • 因为filter桶毕竟还是一个聚合桶,因此他可以和别的桶进行嵌套,但他不是依附在别的桶上

    • 具体实例

      • 取得color为red或是blue的文档

         
        1. GET 127.0.0.1/mytest/doc/_search

        2. {

        3.    "query": {

        4.        "match_all": {}

        5.   },

        6.    "size": 0,

        7.    "aggs": {

        8.        "my_name": {

        9.            "filter": { //因为他用法跟一般的过滤filter一样,所以也能使用bool嵌套

        10.                "bool": {

        11.                    "must": {

        12.                        "terms": { //注意此terms是查找terms,不是terms桶

        13.                            "color": [ "red", "blue" ]

        14.                       }

        15.                   }

        16.               }

        17.           }

        18.       }

        19.   }

        20. }

      • 结果

         
        1. "aggregations": {

        2.    "my_name": {

        3.        "doc_count": 2 //filter桶计算出来的文档数量

        4.   }

        5. }

    • 具体实例二

      • filter桶和terms桶嵌套使用,先过滤出color为red以及blue的文档,再对这些文档进行color分组

         
        1. GET 127.0.0.1/mytest/doc/_search

        2. {

        3.    "query": {

        4.        "match_all": {}

        5.   },

        6.    "size": 0,

        7.    "aggs": {

        8.        "my_name": { //my_name聚合

        9.            "filter": { //filter桶

        10.                "bool": {

        11.                    "must": {

        12.                        "terms": {

        13.                            "color": [ "red", "blue" ]

        14.                       }

        15.                   }

        16.               }

        17.           },

        18.            "aggs": {

        19.                "my_name2": { //my_name2聚合,嵌套在my_name聚合裡

        20.                    "terms": { //terms桶

        21.                        "field": "color"

        22.                   }

        23.               }

        24.           }

        25.       }

        26.   }

        27. }

      • 结果

        • 因为terms桶嵌套在filter桶内,所以query查询出来的文档们会先经过filter桶,如果符合filter桶,才会进入到terms桶内

        • 此处通过filter桶的文档只有两笔,分别是{"color": "red"}以及{"color": ["red", "blue"]},所以terms桶只会对这两笔文档做分组

        • 这也是为什麽terms桶裡没有出现color为green的分组,因为这个文档在filter桶就被挡下来了

         
        1. "aggregations": {

        2.    "my_name": {

        3.        "doc_count": 2, //filter桶计算的数量,通过此处的文档只有2笔

        4.        "my_name2": {

        5.            "doc_count_error_upper_bound": 0,

        6.            "sum_other_doc_count": 0,

        7.            "buckets": [

        8.               {

        9.                    "key": "red",

        10.                    "doc_count": 2  //terms桶计算的数量

        11.               },

        12.               {

        13.                    "key": "blue",

        14.                    "doc_count": 1  //terms桶计算的数量

        15.               }

        16.           ]

        17.       }

        18.   }

        19. }

  • top_hits桶 : 在某个桶底下找出这个桶的前几笔hits,返回的hits格式和主查询query返回的hits格式一模一样

    • top_hits桶支持的参数

      • fromsize

      • sort : 设置返回的hits的排序

        • 要注意,假设在主查询query裡已经对数据设置了排序sort,此sort并不会对aggs裡面的数据造成影响,也就是说主查询query查找出来的数据会先丢进aggs而非先经过sort,因此就算主查询设置了sort,也不会影响aggs数据裡的排序

        • 因此如果在top_hits桶裡的返回的hits数据想要排序,需要自己在top_hits桶裡设置sort

        • 如果没有设置sort,默认使用主查询query所查出来的_score排序

      • _source : 设置返回的字段

    • 具体实例

      • 首先准备数据,color是keyword类型

         
        1. { "color": "red", "price": 100 }

        2. { "color": ["red", "blue"], "price": 1000 }

      • 使用terms桶分组,再使用top_hits桶找出每个group裡面的price最小的前5笔hits

         
        1. GET 127.0.0.1/mytest/doc/_search

        2. {

        3.    "query": {

        4.        "match_all": {}

        5.   },

        6.    "size": 0,

        7.    "aggs": {

        8.        "my_name": {

        9.            "terms": {

        10.                "field": "color"

        11.           },

        12.            "aggs": {

        13.                "my_top_hits": {

        14.                    "top_hits": {

        15.                        "size": 5,

        16.                        "sort": {

        17.                            "price": "asc"

        18.                       }

        19.                   }

        20.               }

        21.           }

        22.       }

        23.   }

        24. }

      • 结果

         
        1. "aggregations": {

        2.    "my_name": {

        3.        "doc_count_error_upper_bound": 0,

        4.        "sum_other_doc_count": 0,

        5.        "buckets": [

        6.           {

        7.                "key": "red",

        8.                "doc_count": 2,  //terms桶计算出来的color为red的文档数

        9.                "my_top_hits": {

        10.                    "hits": {  //top_hits桶找出color为red的这些文档中,price从小到大排序取前5笔

        11.                        "total": 2,

        12.                        "max_score": null,

        13.                        "hits": [

        14.                           {

        15.                                "_score": null,

        16.                                "_source": { "color": "red", "price": 100 },

        17.                                "sort": [ 100 ]

        18.                           },

        19.                           {

        20.                                "_score": null,

        21.                                "_source": { "color": [ "red", "blue" ], "price": 1000 },

        22.                                "sort": [ 1000 ]

        23.                           }

        24.                       ]

        25.                   }

        26.               }

        27.           },

        28.           {

        29.                "key": "blue",

        30.                "doc_count": 1,  //terms桶计算出来的color为blue的文档数

        31.                "my_top_hits": {

        32.                    "hits": { //top_hits桶找出的hits

        33.                        "total": 1,

        34.                        "max_score": null,

        35.                        "hits": [

        36.                           {

        37.                                "_source": {

        38.                                    "color": [ "red", "blue" ], "price": 1000 },

        39.                                "sort": [ 1000 ]

        40.                           }

        41.                       ]

        42.                   }

        43.               }

        44.           }

        45.       ]

        46.   }

        47. }

多桶排序

  • terms桶、histogram桶、data_histogram桶这些桶属于多值桶,也就是说他们会动态生成很多桶,对于这些生成出来的桶们,Elasticsearch默认会使用doc_value进行降序排序,也就是说哪个生成桶的doc_value文档数较多,哪个生成桶就排在前面

    • 如果想要改变这个生成桶与生成桶之间的排序,可以在使用terms桶、histogram桶、data_histogram桶时,使用order进行排序

    • order支持的参数

      • _count : 按照文档数排序

      • _key : 按照每个桶的字符串值的字母顺序排序

  • 具体实例

    • 准备数据,color是keyword类型

       
      1. { "color": "red", "price": 100 }

      2. { "color": ["red", "blue"], "price": 1000 }

    • 使用terms桶进行分组,并且规定按照桶的字母顺序升序,因此a生成桶会排在最前面而z生成桶会排在最后面

       
      1. GET 127.0.0.1/mytest/doc/_search

      2. {

      3.    "query": {

      4.        "match_all": {}

      5.   },

      6.    "size": 0,

      7.    "aggs": {

      8.        "my_name": {

      9.            "terms": {

      10.                "field": "color",

      11.                "order": {

      12.                    "_key": "asc"

      13.               }

      14.           }

      15.       }

      16.   }

      17. }

    • 结果

       
      1. "aggregations": {

      2.    "my_name": {

      3.        "doc_count_error_upper_bound": 0,

      4.        "sum_other_doc_count": 0,

      5.        "buckets": [

      6.           {

      7.                "key": "blue",

      8.                "doc_count": 1

      9.           },

      10.           {

      11.                "key": "red",

      12.                "doc_count": 2

      13.           }

      14.       ]

      15.   }

      16. }

    • 更多精彩文章请访问:百考汇

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值