ES基础内容--聚合

346 阅读7分钟

聚合

一、概念

  1. ES除提供搜索功能外,还提供了针对ES数据进行统计分析的功能,这个功能就是聚合;
  2. 通过聚合,我们会得到一个数据的概览,对全套数据进行分析和总结;
  3. 高性能,实时性高,只需要一条语句,就可以从ES获取分析结果,无需客户端实现分析逻辑;

二、Bucket Aggregation — 一些列满足特定条件的文档的集合

  • 类似于sql中的group by 分组功能;
  • 常见Bucket类型
  1. terms,根据字段进行常规分组,适用于keyword数据类型
  2. Data Range,指定数据范围内分组,适用于数字类型数据;
  3. Data Histogram,固定间隔范围内分组,适用于数字类型数据;
  • 允许通过添加子聚合,支持BucketMetric子聚合;
  • Terms聚合
  1. 适用于keyword数据类型,如果需要text类型字段分组,则需要通过设置mapping打开分组字段的fielddata,才能terms分组
  2. Terms聚合性能优化:对分组字段设置mapping字段属性"eager_global_ordinals"为true,新数据写入Term就会被加载到Cache中;
  3. Demo
#bucket例子
GET kibana_sample_data_flights/_search
{
  //一般设置为0,这样返回结果仅有统计的结果
  "size": 0,
  "aggs": {
    //自定义聚合名称
    "flight_dest": {
      //需要分组统计的字段 DestCountry
      "terms": {
        "field": "DestCountry",
        //指定返回分组个数
        "size": 5
      }
    }
  }
}
  • Range聚合
  1. Demo
GET employees/_search
{
  "size": 0,
  "aggs": {
    "salary_range": {
      //设置聚合类型为range
      "range": {
        "field": "salary",
        "ranges": [
          {
            //to设置的是最大值
            "to": 10000
          },
          {
            //from设置的是最小值
            "from": 10000,
            "to": 20000
          },
          {
            //可以自定义返回的key值,不设置的话,es会提供默认的返回key
            "key": ">20000", 
            "from": 20000
          }
        ]
      }
    }
  }
}
  • Histogram聚合
  1. Demo
GET employees/_search
{
  "size": 0,
  "aggs": {
    "salary_histogram": {
      //设置聚合类型为histogram
      "histogram": {
        "field": "salary",
        //设置固定间隔为5000
        "interval": 5000,
        //设置聚合范围为0-100000
        "extended_bounds": {
          "min": 0,
          "max": 100000
        }
      }
    }
  }
}

三、Metric Aggregation — 一些数学运算,可以对文档字段进行统计分析

  1. 类似于sql中的 count,max,min等数据函数等实现的数字统计功能;
  2. 基于数据集计算结果,除了支持在字段上进行计算同样也支持在脚本产生的结果之上进行计算
  3. 大多数Metric是数学计算仅输出一个值,min/max/sum/avg/cardinality(类似distinct Count);
  4. 部分Metric支持输出多个值,stats/percentiles/percentile_ranks/top hits(排在前面的示例);
  5. 单值输出Demo
#bucket并加入metrics例子
GET kibana_sample_data_flights/_search
{
  "size": 0,
  "aggs": {
    "flight_dest": {
      "terms": {
        "field": "DestCountry"
      },
      "aggs": {
        //求每个bucket的 AvgTicketPrice 字段平均值
        "avg_price": {
          "avg": {
            "field": "AvgTicketPrice"
          }
        },
        //求每个bucket的 AvgTicketPrice 字段最大值
        "max_price": {
          "max": {
            "field": "AvgTicketPrice"
          }
        },
        //求每个bucket的 AvgTicketPrice 字段最小值
        "min_price": {
          "min": {
            "field": "AvgTicketPrice"
          }
        }
      }
      //自定义聚合名称
    "DestCountryCount": {
      //对DestCountry字段分组数,进行去重统计
      "cardinality": {
        "field": "DestCountry"
      }
    }
    }
  }
}
​
#嵌套聚合
GET kibana_sample_data_flights/_search
{
  "size": 0,
  "aggs": {
    "flight_dest": {
      "terms": {
        "field": "DestCountry"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "AvgTicketPrice"
          }
        },
        //在每个DestCountry字段结果上,嵌套了一个根据天气分组的统计
        "weather":{
          "terms": {
            "field": "DestWeather"
          }
        }
      }
    }
  }
}
​
  1. 多值输出(top_hits)Demo
//指定size,不同工种中,年纪最大的3个员工的具体信息
POST employees/_search
{
  "size": 0,
  "aggs": {
    "jobs": {
      "terms": {
        "field":"job.keyword"
      },
      //top_hits,需要嵌套
      "aggs":{
        "old_employee":{
          //设置聚合类型为top_hits
          "top_hits":{
            //设置返回每组排序前三个文档
            "size":3,
            //设置分组内的排序规则
            "sort":[
              {
                "age":{
                  "order":"desc"
                }
              }
            ]
          }
        }
      }
    }
  }
}

四、Pipeline Aggregation — 对其他的聚合结果进行二次聚合分析

  • Pipeline 分析结果会输出到原结果中,根据位置不同,分为以下两类:
  1. Sibling-结果和现有分析结果同级,max/min/avg/sun/stats/percentiles;
//求每个工种的平均工资,并获取平均工资的stats结果
GET employees/_search
{
  "size": 0,
  "aggs": {
    "jobs": {
      "terms": {
        "field": "job.keyword",
        "size": 10
      },
      "aggs": {
        "avg_salary": {
          "avg": {
            "field": "salary"
          }
        }
      }
    },
    //与jobs聚合同级,对jobs聚合的结果,进行二次聚合
    //自定义聚合名称
    "stats_salary_by_job": {
      //指定再聚合类型
      "stats_bucket": {
        //指定再聚合针对的数据,该路径通过'>'进行层级指定(该示例表示,对jobs聚合下的avg_salary子聚合结果进行再聚合分析)
        "buckets_path": "jobs>avg_salary"
      }
    }
  }
}
  1. Parent-结果内嵌到现有聚合分析结果之中,derivative(求导)/cumultive sum(累计求和)/moving function(滑动窗口);
//每个年龄段的平均工资,求累计求和
GET employees/_search
{
  "size": 0,
  "aggs": {
    "age": {
      "histogram": {
        "field": "age",
        "interval": 1,
        "min_doc_count": 0
      },
      "aggs": {
        "avg_salary": {
          "avg": {
            "field": "salary"
          }
        },
        //与avg_salary子聚合同级,自定义名称
        "cumulative_salary": {
          //指定在聚合方式为cumulative_sum,累计求和
          "cumulative_sum": {
            //对再聚合的字段进行指定,由于与avg_salary同级,则直接指定为avg_salary即可
            "buckets_path": "avg_salary"
          }
        }
      }
    }
  }
}

五、Matrix Aggregation — 支持对多个字段的操作并提供一个结果矩阵

六、聚合的作用范围与排序

  • ES聚合分析默认作用范围是query的查询结果集
  • ES还支持以下方式改变聚合的作用范围:
  1. Filter(在聚合内部对数据进行筛选后再分桶,该筛选不会对其他同级聚合起作用);
POST employees/_search
{
  "size": 0,
  "aggs": {
     //聚合1
    "older_person": {
      //该筛选,仅对older_person中的聚合起作用
      "filter":{
        "range":{
          "age":{
            "from":35
          }
        }
      },
      //子聚合1,对筛选后的数据job进行分桶
      "aggs":{
         "jobs":{
           "terms": {
        "field":"job.keyword"
      }
      }
    }},
    //聚合2,对全部数据job分桶
    "all_jobs": {
      "terms": {
        "field":"job.keyword"
        
      }
    }
  }
}
  1. Post Filter(通过对分桶后的数据进行筛选查询,获取分桶中满足条件的文档详情);
POST employees/_search
{
  "aggs": {
    "jobs": {
      "terms": {
        "field": "job.keyword"
      }
    }
  },
  //对分桶后的数据进行筛选,从返回的hits中获得筛选后的分桶中的详细数据
  "post_filter": {
    "match": {
      "job.keyword": "Dev Manager"
    }
  }
}
  1. Global(对指定的global聚合执行筛选豁免,即聚合不参与外部的条件的筛选);
POST employees/_search
{
  "size": 0,
  //数据限制条件
  "query": {
    "range": {
      "age": {
        "gte": 40
      }
    }
  },
  "aggs": {
    //jobs的聚合,应用了外层的数据筛选条件
    "jobs": {
      "terms": {
        "field":"job.keyword"
        
      }
    },
    
    //all聚合,由于指定了global,所以该聚合是针对所有数据
    "all":{
      "global":{},
      "aggs":{
        "salary_avg":{
          "avg":{
            "field":"salary"
          }
        }
      }
    }
  }
}

七、聚合的排序

  • ES默认是对聚合的count值做降序排序;
  • ES也支持自定义对聚合后的数据进行排序;
//1.普通指定桶排序
POST employees/_search
{
  "size": 0,
  "query": {
    "range": {
      "age": {
        "gte": 20
      }
    }
  },
  "aggs": {
    "jobs": {
      "terms": {
        "field":"job.keyword",
        //通过在聚合中加入order字段,来指定排序方式
        "order":[
          {"_count":"asc"},
          {"_key":"desc"}
          ]
        
      }
    }
  }
}
​
//2.通过子聚合的数据进行桶排序
POST employees/_search
{
  "size": 0,
  "aggs": {
    "jobs": {
      "terms": {
        "field":"job.keyword",
        //指定子聚合的名称,通过子聚合的数据进行排序
        "order":[  {
            "avg_salary":"desc"
          }]
      },
    "aggs": {
      "avg_salary": {
        "avg": {
          "field":"salary"
        }
      }
    }
    }
  }
}
​
//3.通过多值子聚合数据进行桶排序
POST employees/_search
{
  "size": 0,
  "aggs": {
    "jobs": {
      "terms": {
        "field":"job.keyword",
        "order":[  {
            //对于多值聚合,指定排序=多值子聚合名称+".某个值"
            "stats_salary.min":"desc"
          }]
      },
     //子聚合
    "aggs": {
      "stats_salary": {
        //stats是多值聚合
        "stats": {
          "field":"salary"
        }
      }
    }
    }
  }
}
​

八、聚合原理及精准度问题

image_4EaThK5N-b.png

image_mbc_sCf7Qn.png

  • Terms聚合分析会出现不准确的原因数据分散多个分片上,Coordinating Node无法获取数据全貌
  • 解决方案:
  1. 数据量小时,可以设置主分片数为1,这样就能保证数据的准确性;
  2. 分布式数据上,设置shard_size参数,该参数每次从分片上额外多获取数据,提升准确率,会降低响应时间;ES默认设定该值=size*1.5+10;
  • demo解析:
GET my_flights/_search
{
  "size": 0,
  "aggs": {
    "weather": {
      "terms": {
        "field":"OriginWeather",
        "size":1,
        //关键参数1:每次从分片上额外多获取数据,提升准确率
        "shard_size":1,
        //关键参数2:打开该参数,助于分析聚合的结果是否是准确的(每个bucket会多返回一个doc_count_error_upper_bound参数)
        "show_term_doc_count_error":true
      }
    }
  }
}//返回结果
{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 20,
    "successful" : 20,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "weather" : {
      //关键参数3:被遗漏term分桶包含的文档,有可能的最大值;该值为0,表明所有文档都被涉及,返回的聚合结果是完全准确的;
      "doc_count_error_upper_bound" : 2495,
      //关键参数4:除了返回结果bucket的terms以外,其他terms文档总数(总返回数-返回的总数)
      "sum_other_doc_count" : 12189,
      "buckets" : [
        {
          "key" : "Cloudy",
          "doc_count" : 870,
          "doc_count_error_upper_bound" : 1625
        }
      ]
    }
  }
}
​
​