面试官问我会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你 ...
随机推荐
- 几种方法验证unity是否为development build
我在月初接入了uwa的性能测试SDK,需要提交一个development build的游戏安装包给uwa进行真人真机测试,本文说下如何判断安装包是否为development build. 直观上判断 ...
- SqlSugar的查询函数SqlFunc
用法 我们可以使用SqlFunc这个类调用Sql函数,用法如下: db.Queryable<Student>().Where(it => SqlFunc.ToLower(it.Nam ...
- 释放搜索潜力:基于ES(ElasticSearch)打造高效的语义搜索系统,让信息尽在掌握
释放搜索潜力:基于ES(ElasticSearch)打造高效的语义搜索系统,让信息尽在掌握[1.安装部署篇--简洁版],支持Linux/Windows部署安装 效果展示 PaddleNLP Pipel ...
- C/C++ 内存遍历与KMP特征搜索
内存遍历,枚举数据,实现特征码扫描. 内存遍历: 每次读入4096字节,然后每16个字符换一次行,遍历内存 0x00401000 - 0x7FFFFFFF. #include <stdio.h& ...
- 从嘉手札<2023-11-20>
写给十年如一日的偶像--Faker "我看了一下,觉得视频还不够清晰,等我换一个清晰点的摄像头再回来直播,不要走开~" 繁星满天,流光飞逝. 世界是一场盛大的表演, 舞台上熙熙攘攘 ...
- Java多线程-ThreadLocal(六)
为了提高CPU的利用率,工程师们创造了多线程.但是线程们说:要有光!(为了减少线程创建(T1启动)和销毁(T3切换)的时间),于是工程师们又接着创造了线程池ThreadPool.就这样就可以了吗?-- ...
- Windows批处理文件初始化数据库
前提是MySQL服务必须启动,Windows添加了MySQL的环境变量. 批处理文件: @ECHO OFF SET dbhost=127.0.0.1 SET dbuser=root SET dbpas ...
- NC19158 失衡天平
题目链接 题目 题目描述 终于Alice走出了大魔王的陷阱,可是现在傻傻的她忘了带武器了,这可如何是好???这个时候,一个神秘老人走到她面前答应无偿给她武器,但老人有个条件,需要将所选武器分别放在天平 ...
- Shiro 框架的MD5加密算法实现原理
直接上代码:该代码可以直接用于项目中做MD5加密,加盐加密,多层散列加密 import java.io.UnsupportedEncodingException; import java.securi ...
- Java官方文档
https://www.oracle.com/java/technologies/ https://www.oracle.com/java/technologies/downloads/archive ...