数据聚合
帮助我们对海量的数据做统计和分析,结合kibana还可以形成可视化的图形报表
聚合的种类
聚合的字段一定是不分词的,不能是text的
比如说按照酒店数据按照品牌做分组,这个就属于桶的聚合
按照品牌分组后想算算不同品牌的 酒店的价格的平均值,或最大最小值怎么样--度量的聚合
算完平均值,想给排序,或者看看不同品牌平均值的最大最小值--这就是对度量结果再次聚合了就是管道了
除了这几种还有很多聚合的种类,这里只学习常用的
总结
聚合就是对索引库的数据做统计、分析、计算
DSL实现聚合
aggs就是聚合的一个函数了
size是分页值,给0,显示的文档数据就是0条了,因为我的目的是看聚合,而不是看文档
聚合里的size如果不指定的话默认是10,就是控制显示的结果,比如说聚合结果又100多种,但是设置了这个size就是只显示前10条数据
三要素:聚合名称、聚合类型、聚合字段
聚合是对桶的聚合,是个数组,将来有很多个桶,默认排序规则是倒序排序 ,可以自定义排序规则
修改排序规则
这里我们的聚合名称是brandAgg,将来如果想要定义多个聚合,就再加就好了
可以限定文档的范围,只需要添加查询条件,我们加了酒店价格的范围,那么聚合范围也就少了,不用全查
DSL实现Metrics聚合(度量聚合)
聚合的嵌套,我们先对品牌做聚合,然后再他的基础上在对评分做聚合,评分的聚合aggs在第一个aggs里边
聚合的嵌套,在第一个统计聚合的里边加平均值聚合,这样桶里的数据就多了好多
我们再对得出结果的平均值做个排序,看看哪个酒店的评价最高
对桶里的数据做排序,以前是对_count排序,现在是对平均值排序
注意,这种度量聚合嵌套,是在桶里边,定义的名字里边做的度量聚合
RestAPI实现聚合
request.source代表的是最大的json了
brand_agg当初我们用的就是terms类型,所以返回值也就是terms类型,根据聚合名称获取聚合结果
逐层解析json就可以了,我们可以先打印response
@Test
void aggTest1() throws IOException {
SearchRequest request = new SearchRequest("hotel");
request.source().size(0);
request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(10));
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 解析结果
Aggregations aggregations = response.getAggregations();
// 根据聚合名称获取聚合结果,本来就terms类型,用terms接收那就
// 根据brand_agg去获取桶
Terms terms = aggregations.get("brandAgg");
// 获取数组桶的内容
List<? extends Terms.Bucket> buckets = terms.getBuckets();
// 遍历每一个桶位
for (Terms.Bucket bucket : buckets) {
String keyAsString = bucket.getKeyAsString();
System.out.println(keyAsString);
}
}
service
实现类
利用command+option+m封装重复代码,使得更优雅
@Override
public Map<String, List<String>> filters() {
// 查出城市 city, 品牌 brand, 星级 startName
try {
SearchRequest request = new SearchRequest("hotel");
// 针对多个字段做聚合,封装一下,就优雅了
buildAggregation(request);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 解析返回结果信息
Aggregations aggregations = response.getAggregations();
return buildResponse(aggregations);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void buildAggregation(SearchRequest request) {
request.source().aggregation(
AggregationBuilders.terms("cityAgg").field("city")
).aggregation(
AggregationBuilders.terms("brandAgg").field("brand")
).aggregation(
AggregationBuilders.terms("startNameAgg").field("starName")
);
}
private Map<String, List<String>> buildResponse(Aggregations aggregations) {
Map<String, List<String>> map = new HashMap<>();
List<String> brandAggList = getAggByName(aggregations, "brandAgg");
map.put("品牌", brandAggList);
List<String> cityAggList = getAggByName(aggregations, "cityAgg");
map.put("城市", cityAggList);
List<String> startAggList = getAggByName(aggregations, "startNameAgg");
map.put("星级", startAggList);
return map;
}
private List<String> getAggByName(Aggregations aggregations, String params) {
// 分别获取
Terms cityAgg = aggregations.get(params);
List<? extends Terms.Bucket> buckets = cityAgg.getBuckets();
List<String> list = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
String keyAsString = bucket.getKeyAsString();
list.add(keyAsString);
}
return list;
}
利用单元测试注册service 然后就可以调用了,因为没有参数更方便测试,不用postMan就可以
对接前端接口
前端页面向服务端发送请求,查询品牌、城市、星级等字段的聚合结果
修改原来的filters接口,将其添加一个查询query即可,这个query得和之前的查询一样,直接用就可以了
也就是说我们在做聚合的时候是要加一些过滤条件的,去限制聚合的范围,条件就是查询的条件一样的,到时候记得把查询的条件封装出来,复用
@Override
public Map<String, List<String>> filters(RequestParams requestParams) {
// 查出城市 city, 品牌 brand, 星级 startName
try {
SearchRequest request = new SearchRequest("hotel");
request.source().size(0);
// 添加查询功能
buildQueryBool(requestParams, request);
// 针对多个字段做聚合,封装一下,就优雅了
buildAggregation(request);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// json 有多个,那就是 jsonArr
Aggregations aggregations = response.getAggregations();
return buildResponse(aggregations);
} catch