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. c和c++开发工具之clion和vs

    个人体验结果 如果是CMake或者要跨平台的话,建议使用CLion 像我在看书写练习题的话,Clion使用cmake编译c/c++源码更简单上手使用. 如果项目不大,两者都可以.如果关联子项目或第三方 ...

  2. 21.9 Python 使用Selenium库

    Selenium是一个自动化测试框架,主要用于Web应用程序的自动化测试.它可以模拟用户在浏览器中的操作,如打开网页.点击链接.填写表单等,并且可以在代码中实现条件判断.异常处理等功能.Seleniu ...

  3. Cheat Engine 官方教程汉化

    CE修改器官方教程汉化版,区别于前款教程,官方教程中使用的是Tutorial64位程序,如下是经过翻译后的官方文档. 第一步:欢迎 当教程启动时,您应该会看到类似的东西,您只需在阅读帮助文本后单击&q ...

  4. CF1000F One Occurrence题解

    题目链接:CF 或者 洛谷 感觉很经典的题,而且给的 \(5e5\),虽然莫队之类的很好想,但完全没必要去考虑这类算法,这种数据范围常数又大又开盲盒.很显然的具有单 \(log\) 的算法. 回忆下经 ...

  5. 如何计算DG环境下Redo传输所需的带宽?

    MOS有篇文章: How To Calculate The Required Network Bandwidth Transfer Of Redo In Data Guard Environments ...

  6. IntelliJ IDEA 查看一个接口的实现类。

  7. ASP.NET Core分布式项目实战(oauth2与open id connect 对比)--学习笔记

    任务14:oauth2与open id connect 对比 以微博开放平台为例(微博登录接入--授权机制): https://open.weibo.com/wiki/授权机制 可以看到微博登录接入使 ...

  8. JS 前序遍历、中序遍历、后序遍历、层序遍历详解,深度优先与广度优先区别,附leetcode例题题解答案

    壹 ❀ 引 按照一天一题的速度,不知不觉已经刷了快两多月的leetcode了,因为本人较为笨拙,一道简单的题有时候也会研究很久,看着提交了两百多次,其实也才解决了70来道简单题,对于二分法,双指针等也 ...

  9. NVME(学习笔记七)—Atomicity Operation

    5.21.1.10 Write Atomicity Normal 这个特性控制AWUN和NAWUN参数的操作.设置的属性值在set Feature命令的Dword 11中表明. 如果提交Get Fea ...

  10. STM32F401CCU6与MFRC522接线及读取示例

    硬件准备 stm32f401ccu6最小开发板 rfid-rc522开发板 usb2ttl转接, 可以用pl2303, ch340, CP2102, FT232 Mifare 1K卡, UID长度4字 ...