ES聚合分析

1.metric(指标)聚合

1.1 单值分析

  • min 求指定字段的最小值

    # 求价格的最小值
    {
    "size":0,
    "aggs":{
    "min_price":{
    "min":{
    "field":"price"
    }
    }
    }
    }
  • max 求指定字段的最大值

    # 求价格的最大值
    {
    "size":0,
    "aggs":{
    "max_price":{
    "max":{
    "field":"price"
    }
    }
    }
    }
  • avg 求指定字段的平均值

    # 求价格的平均值
    {
    "size":0,
    "aggs":{
    "avg_price":{
    "avg":{
    "field":"price"
    }
    }
    }
    }
  • sum 求指定字段的总和

    # 求价格的总和
    {
    "size":0,
    "aggs":{
    "sum_price":{
    "sum":{
    "field":"price"
    }
    }
    }
    }
  • value_count 统计某字段有值的文档数

    # 求price有值的个数
    {
    "aggs":{
    "price_count":{
    "value_count":{
    "field":"price"
    }
    }
    }
    }
  • cardinality 去重计数

    #查询有多少种job(不分词下进行去重)
    {
    "aggs":{
    "job_count":{
    "cardinality":{
    "field":"job.keyword"
    }
    }
    }
    }

1.2 多值分析

  • stats 包含多种返回结果:min,max,avg,sum,count

    # 可以分析查询出以上的所有关于价格的单值结果
    {
    "aggs":{
    "price_stats":{
    "stats":{
    "field":"price"
    }
    }
    }
    }
  • Extended stats 高级统计,比stats查询多四个结果:平方和、方差、标准差、平均值加/减两个标准差的区间

    {
    "aggs":{
    "extended_salary":{
    "extended_stats":{
    "field":"salary"
    }
    }
    }
    }
  • Percentiles:占比百分位数统计

    # 统计salay每个值所在的百分比
    {
    "aggs":{
    "percentiles_salary":{
    "percentiles":{
    "field":"salary"
    }
    }
    }
    } # 统计指定分位值的数据是多少
    {
    "aggs":{
    "percentiles_salary":{
    "percentiles":{
    "field":"salary",
    "percents" : [75, 99, 99.9]
    }
    }
    }
    }
  • Percentiles rank 统计值小于等于指定值的文档占比

    {
    "aggs":{
    "gge_perc_rank":{
    "percentile_ranks":{
    "field":"price",
    "values":[
    100,
    200
    ]
    }
    }
    }
    }
  • top_hits:用于分桶后获取该桶内最匹配的顶部文档列表,即详情数据

    {
    "aggs":{
    "jobs":{
    "terms":{
    "field":"job.keyword",
    "size":10
    },
    "aggs":{
    "top_jobs":{
    "top_hits":{
    "size":10,
    "sort":[
    {
    "age":{
    "order":"desc"
    }
    }
    ]
    }
    }
    }
    }
    }
    }

2.bucket(桶)聚合

2.1 Bucket的分桶策略

  • Terms:按照指定字段进行分桶

    {
    "size":0,
    "aggs":{
    "terms_jobs":{
    "terms":{
    "field":"job.keyword",
    "size":5
    }
    }
    }
    }
  • Range:按照指定字段的值的范围进行分桶

    {
    "size":0,
    "aggs":{
    "group_by_price":{
    "range":{
    "field":"price",
    "ranges":[
    {
    "key":"<200",
    "to":200
    },
    {
    "from":200,
    "to":400
    },
    {
    "key":">400",
    "from":400
    }
    ]
    }
    }
    }
    }
  • Date_Range:按照日期字段的日期范围进行分桶

    {
    "size":0,
    "aggs":{
    "group_by_birth":{
    "date_range":{
    "field":"birth",
    "format":"yyyy",
    "ranges":[
    {
    "key":"2000年以前",
    "to":"2000"
    },
    {
    "key":"2000年 - 2020年",
    "from":"2000",
    "to":"2020"
    },
    {
    "key":"2020年以后",
    "from":"2020"
    }
    ]
    }
    }
    }
    }
  • Histogram:直方图,以固定间隔的策略来分割数据

    {
    "size":0,
    "aggs":{
    "salary_hist":{
    "histogram":{
    "field":"salary",
    "interval":5000,
    "extended_bounds":{
    "min":0,
    "max":40000
    }
    }
    }
    }
    }
  • Date_Histogram:针对日期的直方图或柱状图,时序数据分析常用的。

    {
    "size":0,
    "aggs":{
    "by_year":{
    "date_histogram":{
    "field":"birth",
    "interval":"year",
    "format":"yyyy"
    }
    }
    }
    }

2.2 Bucket + Metric

  • 案例一:按照不同年龄段分桶,求每个年龄段的工资平均值

    {
    "size":0,
    "aggs":{
    "group_by_age":{
    "range":{
    "field":"age",
    "ranges":[
    {
    "key":"<20",
    "to":20
    },
    {
    "key":"20 - 50",
    "from":20,
    "to":50
    },
    {
    "key":">50",
    "from":50
    }
    ]
    },
    "aggs":{
    "avg_salary":{
    "avg":{
    "field":"salary"
    }
    }
    }
    }
    }
    }
  • 案例二:分桶再分桶:先根据job分桶,再按照不同年龄划分

    {
    "size":0,
    "aggs":{
    "group_by_job":{
    "terms":{
    "field":"job",
    "size":10
    },
    "aggs":{
    "range_age":{
    "range":{
    "field":"age",
    "ranges":[
    {
    "key":"<20",
    "to":20
    },
    {
    "key":"20 - 50",
    "from":20,
    "to":50
    },
    {
    "key":">50",
    "from":50
    }
    ]
    }
    }
    }
    }
    }
    }
  • 案例三:分桶后进行数据分析

    # 求出不同种工作的平均薪资
    {
    "size":0,
    "aggs":{
    "group_by_job":{
    "terms":{
    "field":"job",
    "size":10
    },
    "aggs":{
    "avg_salary":{
    "stats":{
    "field":"salary"
    }
    }
    }
    }
    }
    }

3.pipeline(管道)聚合

3.1 Parent结果内嵌到现有的聚合分析结果中

  • Derivative(导数)
  • Moving Average(移动平均)
  • Cumulative Sum(累计求和)

案例一:根据生日按月分组,求出每组的平均值,以及导数

{
"size":0,
"aggs":{
"group_by_birth":{
"date_histogram":{
"field":"birth",
"interval":"mounth",
"format":"yyyy"
},
"aggs":{
"avg_salary":{
"avg":{
"field":"salary"
}
},
"derivative_avg_salary":{
"derivative":{
"buckets_path":"avg_salary"
}
}
}
}
}
}

3.2 Sibing结果与现有聚合分析结果同级

  • Max/Min/Avg/Sum Bucket
  • Stats/Extended Stats Bucket
  • Percentiles Bucket

案例一:根据job进行分组,求出每组的平均工资,找出这些组平均工资的最小值

{
"size":0,
"aggs":{
"group_by_job":{
"terms":{
"field":"job",
"size":10
},
"aggs":{
"avg_salary":{
"avg":{
"field":"salary"
}
}
}
},
"min_salary_by_job":{
"min_bucket":{
"buckets_path":"group_by_job>avg_salary"
}
}
}
}

4.ES中的Java API

4.1 terms,range,date_range等聚合的演示

  • ES相关依赖

    <dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.3.2</version>
    </dependency>
    <dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>7.3.2</version>
    </dependency>
    <dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>7.3.2</version>
    </dependency>
    <!-- Java High Level REST Client -->
    <dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.3.2</version>
    </dependency>
  • ES相关聚合部分的演示代码

    package com.lenovo.btit.elasticsearch;
    import org.apache.commons.lang3.StringUtils;
    import org.elasticsearch.action.search.SearchRequest;
    import org.elasticsearch.action.search.SearchResponse;
    import org.elasticsearch.client.RequestOptions;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.elasticsearch.search.aggregations.AggregationBuilders;
    import org.elasticsearch.search.aggregations.bucket.range.DateRangeAggregationBuilder;
    import org.elasticsearch.search.aggregations.bucket.range.Range;
    import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
    import org.elasticsearch.search.aggregations.bucket.terms.Terms;
    import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
    import org.elasticsearch.search.aggregations.metrics.Avg;
    import org.elasticsearch.search.builder.SearchSourceBuilder; import java.io.IOException;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map; /**
    * Es聚合分析使用
    *
    * @author: zangchuanlei
    * @date: 2021/7/11
    * @time: 15:18
    */
    public class EsServiceBucket { private RestHighLevelClient highLevelClient;
    // terms某个字段进行分组标识
    private final String BUCKET_TERMS = "1";
    // range按照某个范围进行分组(double,int)
    private final String BUCKET_RANGE = "2";
    // date_range按照时间范围进行分组
    private final String BUCKET_DATA_RANGE = "3"; /**
    * @param bucketType 桶聚合类型
    * @param resultName 结果集名称
    * @param size 每页几个数据
    * @param indices 文档库名
    * @description 执行ES聚合分析查询
    * @author zangchuanlei
    * @date 2021/7/11 16:15
    */
    private Map<String, Long> query(String bucketType, String resultName, int size, String... indices) {
    // 获取搜索构建器
    SearchSourceBuilder sourceBuilder;
    switch (bucketType) {
    case BUCKET_TERMS:
    sourceBuilder = getSourceBuilderOfTerms(resultName, size);
    break;
    case BUCKET_RANGE:
    sourceBuilder = getSourceBuilderOfRange(resultName, size);
    break;
    case BUCKET_DATA_RANGE:
    sourceBuilder = getSourceBuilderOfDataRange(resultName, size);
    break;
    default:
    sourceBuilder = new SearchSourceBuilder();
    break;
    } //建立关于指定文档库的搜索请求
    SearchRequest searchRequest = new SearchRequest()
    .indices(indices)
    .source(sourceBuilder); //返回处理结果
    return getResultMapByBucket(bucketType,searchRequest, resultName);
    } /**
    * @param searchRequest 搜索请求
    * @param resultName 结果集名称
    * @description 处理搜索响应数据
    * @author zangchuanlei
    * @date 2021/7/11 16:41
    */
    private Map<String, Long> getResultMapByBucket(String bucketType, SearchRequest searchRequest, String resultName) {
    // 创建返回结果map容器
    Map<String, Long> rtnMap = new HashMap<>();
    try {
    SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT); // 字段分组
    if (StringUtils.equals(BUCKET_TERMS, bucketType)) {
    // 根据结果集名称获取Terms
    Terms aggsTerm = searchResponse.getAggregations().get(resultName);
    //获得分组后的桶信息
    List<? extends Terms.Bucket> termBuckets = aggsTerm.getBuckets();
    termBuckets.forEach(b -> {
    rtnMap.put(b.getKeyAsString(), b.getDocCount());
    System.out.println("key:" + b.getKeyAsString());
    System.out.println("count:" + b.getDocCount());
    // 处理子聚合,打印每组的平均价格
    Avg averageBalance = b.getAggregations().get("price_avg");
    System.out.println("key = "+b.getKeyAsString()+"的价格平均值为:"+averageBalance.getValue());
    }); // 范围分组
    } else if (StringUtils.equals(BUCKET_RANGE, bucketType) ||
    StringUtils.equals(BUCKET_DATA_RANGE, bucketType)) {
    Range aggsRange = searchResponse.getAggregations().get(resultName);
    List<? extends Range.Bucket> rangeBuckets = aggsRange.getBuckets();
    rangeBuckets.forEach(b->{
    rtnMap.put(b.getKeyAsString(), b.getDocCount());
    });
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    return rtnMap;
    } /**
    * @param resultName 结果集名称
    * @param size 显示个数
    * @description 构建根据商品品牌分组,并计算每组的价格平均值
    * @author zangchuanlei
    * @date 2021/7/11 16:24
    */
    private SearchSourceBuilder getSourceBuilderOfTerms(String resultName, int size) {
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    // step1:先根据品牌进行分组
    TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms(resultName)
    .field("bank.keyword"); // step2:计算每个品牌的的平均价格
    aggregationBuilder.subAggregation(AggregationBuilders.avg("price_avg").field("price")); sourceBuilder.aggregation(aggregationBuilder);
    sourceBuilder.size(size).trackTotalHits(true);
    return sourceBuilder;
    } /**
    * @param resultName 结果集名称
    * @param size 显示个数
    * @description 构建根据商品价格范围分组的聚合查询条件
    * @author zangchuanlei
    * @date 2021/7/11 16:52
    */
    private SearchSourceBuilder getSourceBuilderOfRange(String resultName, int size) {
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    RangeAggregationBuilder aggregationBuilder = AggregationBuilders.range(resultName)
    .field("price")
    // 分组范围:小于1000,1000-3000,大于3000
    .addUnboundedTo(1000).addRange(1000, 3000).addUnboundedFrom(3000);
    sourceBuilder.aggregation(aggregationBuilder);
    sourceBuilder.size(size).trackTotalHits(true);
    return sourceBuilder;
    } /**
    * @param resultName
    * @param size
    * @description 构建根据上市时间范围分组的聚合查询条件
    * @author zangchuanlei
    * @date 2021/7/11 17:05
    */
    private SearchSourceBuilder getSourceBuilderOfDataRange(String resultName, int size) {
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    // 按照上市时间进行分组
    DateRangeAggregationBuilder aggregationBuilder = AggregationBuilders.dateRange(resultName).format("yyyy-MM-dd")
    .field("launch_date")
    .addUnboundedTo("2000-01-01以前", "2000-01-01")
    .addRange("2000-01-01至2010-12-31", "2000-01-01", "2010-12-31")
    .addUnboundedFrom("2010-12-31以后", "2010-12-31"); sourceBuilder.aggregation(aggregationBuilder);
    sourceBuilder.size(size).trackTotalHits(true);
    return sourceBuilder;
    }
    }

面试官问我会ES么,我说不会,抓紧学起【ES(一)聚合分析篇】的更多相关文章

  1. 面试官问我会不会Elasticsearch,我语塞了...

    少点代码,多点头发 本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues. https://github.com/midou-tech/articles 从今天开始准备给大家带来全新 ...

  2. 「干货」面试官问我如何快速搜索10万个矩形?——我说RBush

    「干货」面试官问我如何快速搜索10万个矩形?--我说RBUSH 前言 亲爱的coder们,我又来了,一个喜欢图形的程序员‍,前几篇文章一直都在教大家怎么画地图.画折线图.画烟花,难道图形就是这样嘛,当 ...

  3. 每日一问:面试结束时面试官问"你有什么问题需要问我呢",该如何回答?

    面试结束时面试官问"你有什么问题需要问我呢",该如何回答?

  4. 面试官问我,Redis分布式锁如何续期?懵了。

    前言 上一篇[面试官问我,使用Dubbo有没有遇到一些坑?我笑了.]之后,又有一位粉丝和我说在面试过程中被虐了.鉴于这位粉丝是之前肥朝的粉丝,而且周一又要开启新一轮的面试,为了回馈他长期以来的支持,所 ...

  5. 面试官问,说一个你在工作非常有价值的bug

    如果你去参考面试,做足了准备,面对面试官员从容不迫,吐沫横飞的大谈自己的工作经历.突然,面试官横插一句:说一个你在工作非常有价值的bug.顿时,整个空气都仿佛都凝固了!“What?”... 我想没几个 ...

  6. 面试官问:JS的this指向

    前言 面试官出很多考题,基本都会变着方式来考察this指向,看候选人对JS基础知识是否扎实.读者可以先拉到底部看总结,再谷歌(或各技术平台)搜索几篇类似文章,看笔者写的文章和别人有什么不同(欢迎在评论 ...

  7. 当面试官问你sql优化的时候。。。

    当面试官问你有关sql优化的问题时,直接拿笔写给他: 8-select 9-distinct<column_list> 1-from left_table 3-<join_type& ...

  8. 面试官问你JS基本类型时他想知道什么?

    面试的时候我们经常会被问答js的数据类型.大部分情况我们会这样回答包括:1.基本类型(值类型或者原始类型): Number.Boolean.String.NULL.Undefined以及ES6的Sym ...

  9. 面试官问线程安全的List,看完再也不怕了!

    最近在Java技术栈知识星球里面有球友问到了线程安全的 List: 扫码查看答案或加入知识星球 栈长在之前的文章<出场率比较高的一道多线程安全面试题>里面讲过 ArrayList 的不安全 ...

  10. 美团面试官问我一个字符的String.length()是多少,我说是1,面试官说你回去好好学一下吧

    本文首发于微信公众号:程序员乔戈里 public class testT { public static void main(String [] args){ String A = "hi你 ...

随机推荐

  1. PostgreSQL中的B-TREE索引

    分析了解pgsql中的索引 前言 索引 B-tree B-Tree和B+Tree的区别: pgsql中B-Tree 实现 如果该节点不是最右节点 如果该节点是最右节点 参考 分析了解pgsql中的索引 ...

  2. 记录一次py2编码带来的坑

    "中文" == u"中文" # PY2 False"中文" == u"中文" # PY3 True

  3. 整个小东西,在IDEA中自动生成PO、DAO、Mapper

    作者:小傅哥 博客:https://bugstack.cn 源码:https://github.com/fuzhengwei/CodeGuide/wiki 沉淀.分享.成长,让自己和他人都能有所收获! ...

  4. 图解Java原理

    1:强制类型转换数据溢出 2:顺序结构的流程图 3:单if语句的流程图 4:标准if-else语句的流程图 5:扩展if-else语句的流程图 6:for循环流程图

  5. rpm安装卸载jdk

    安装 rpm -ivh jdk-7-linux-x64.rpm 卸载 先查看安装的包 rpm -qa | grep jdk 卸载 rpm -e --nodeps jdk-1.7.0-fcs.x86_6 ...

  6. Primo Ramdisk SCSI虚拟硬盘和Direct-IO虚拟硬盘

    Primo Ramdisk 使用不同的虚拟技术可创建两种类型的虚拟硬盘:SCSI 虚拟硬盘和 Direct-IO 虚拟硬盘. 本篇主要比较两者之间的差异. SCSI 虚拟硬盘遵循SCSI规范,行为上几 ...

  7. Linux-MySQL导入示例数据库employees

    1. 下载employees示例数据库employees是一个官方提供的简单数据库,在mysql的官方找到employees的说明页面,通过github下载该数据库. https://github.c ...

  8. 测距工具部分情况下无效的问题解决 - 高德JSAPI

    最近项目中新增了一个需求是在地图上新增一个测距工具,方便看一下距离 高德官方本身自带了有一个测距工具类的 RangingTool ,以插件的方式引入即可 问题一: 如果地图上有覆盖物的话(我这是 po ...

  9. PCG——程序化地形生成(1)

    前言 接触了半年多Houdini,佛系研究了一下PCG(Procedural Content Generation)相关的技术,这真是个好东西,赶在年前写个总结.Houdini 一款DCC软件,功能又 ...

  10. ftp 出现Passive mode refused 解决办法

    在shell中调用FTP出现下面错误时, Permission denied. Passive mode refused. Permission denied. Passive mode refuse ...