面试官问我会ES么,我说不会,抓紧学起【ES(一)聚合分析篇】
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(一)聚合分析篇】的更多相关文章
- 面试官问我会不会Elasticsearch,我语塞了...
少点代码,多点头发 本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues. https://github.com/midou-tech/articles 从今天开始准备给大家带来全新 ...
- 「干货」面试官问我如何快速搜索10万个矩形?——我说RBush
「干货」面试官问我如何快速搜索10万个矩形?--我说RBUSH 前言 亲爱的coder们,我又来了,一个喜欢图形的程序员,前几篇文章一直都在教大家怎么画地图.画折线图.画烟花,难道图形就是这样嘛,当 ...
- 每日一问:面试结束时面试官问"你有什么问题需要问我呢",该如何回答?
面试结束时面试官问"你有什么问题需要问我呢",该如何回答?
- 面试官问我,Redis分布式锁如何续期?懵了。
前言 上一篇[面试官问我,使用Dubbo有没有遇到一些坑?我笑了.]之后,又有一位粉丝和我说在面试过程中被虐了.鉴于这位粉丝是之前肥朝的粉丝,而且周一又要开启新一轮的面试,为了回馈他长期以来的支持,所 ...
- 面试官问,说一个你在工作非常有价值的bug
如果你去参考面试,做足了准备,面对面试官员从容不迫,吐沫横飞的大谈自己的工作经历.突然,面试官横插一句:说一个你在工作非常有价值的bug.顿时,整个空气都仿佛都凝固了!“What?”... 我想没几个 ...
- 面试官问:JS的this指向
前言 面试官出很多考题,基本都会变着方式来考察this指向,看候选人对JS基础知识是否扎实.读者可以先拉到底部看总结,再谷歌(或各技术平台)搜索几篇类似文章,看笔者写的文章和别人有什么不同(欢迎在评论 ...
- 当面试官问你sql优化的时候。。。
当面试官问你有关sql优化的问题时,直接拿笔写给他: 8-select 9-distinct<column_list> 1-from left_table 3-<join_type& ...
- 面试官问你JS基本类型时他想知道什么?
面试的时候我们经常会被问答js的数据类型.大部分情况我们会这样回答包括:1.基本类型(值类型或者原始类型): Number.Boolean.String.NULL.Undefined以及ES6的Sym ...
- 面试官问线程安全的List,看完再也不怕了!
最近在Java技术栈知识星球里面有球友问到了线程安全的 List: 扫码查看答案或加入知识星球 栈长在之前的文章<出场率比较高的一道多线程安全面试题>里面讲过 ArrayList 的不安全 ...
- 美团面试官问我一个字符的String.length()是多少,我说是1,面试官说你回去好好学一下吧
本文首发于微信公众号:程序员乔戈里 public class testT { public static void main(String [] args){ String A = "hi你 ...
随机推荐
- 升级到win11 22h2的体验
win11 22h2更稳定了 在win11 22h2发布后没多久,我就升级到了这个版本,截止目前已经使用半个月了,谈谈我的使用感受. 总体要比之前的版本更稳定,表现为笔记本风扇不会突然响,突然卡顿,不 ...
- TienChin 活动管理-添加活动接口
ActivityController @PreAuthorize("hasPermission('tienchin:activity:create')") @Log(title = ...
- 给你一颗“定心丸”——记一次由线上事故引发的Log4j2日志异步打印优化分析
一.内容提要 自知是人外有人,天外有天,相信对于Log4j2的异步日志打印早有老师或者同学已是熟稔于心,优化配置更是信手拈来,为了防止我在这里啰里八嗦的班门弄斧,我先将谜底在此公布:log4j2.as ...
- Advanced Installer设置安装最后一步启动软件
左侧用户界面中选择对话框-ExitDialog 在完成操作项中勾选"安装结束时启动应用程序",在弹出的对话框中选择需要启动的exe文件
- System V|共享内存基本通信框架搭建|【超详细的代码解释和注释】
前言 那么这里博主先安利一下一些干货满满的专栏啦! 手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014. ...
- Java并发(八)----使用线程避免cpu占用100%
1.sleep 实现 在没有利用 cpu 来计算时,不要让 while(true) 空转浪费 cpu,这时可以使用 yield 或 sleep 来让出 cpu 的使用权给其他程序 while(true ...
- MySQL执行函数时报错:Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation 'find_in_set'
执行函数时报错: Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) f ...
- DP的各种优化小结
动态规划算法(简称动规,DP),是IO中最为常见的,也是最为重要的算法之一.这也就意味着,在各种题目与比赛中它会有很多稀奇古怪的算法和优化,时不时地在你的面前出现一个TLE,MLE和RE来搞你的心态. ...
- 从零开始手写 mybatis(四)- mybatis 事务管理机制详解
前景回顾 第一节 从零开始手写 mybatis(一)MVP 版本 中我们实现了一个最基本的可以运行的 mybatis. 第二节 从零开始手写 mybatis(二)mybatis interceptor ...
- fold命令
fold命令 fold命令用于限制文件列宽,其会从指定的文件里读取内容,将超过限定列宽的列加入增列字符后,输出到标准输出设备.若不指定任何文件名称,或是所给予的文件名为-,则fold命令会从标准输入设 ...