数据聚合
聚合(Aggregations):可以实现对文档数据的统计、分析、运算。
常见的聚合有三类:
- 桶(Bucket)聚合:用来对文档做分组
TermAggtegation:按照文档字段值分组
Date Histogram:按照日期阶梯分组
- 度量(Metric)聚合:
Avg:求平均值
Max:求最大值
Min:求最小值
Status:同时求max、min、avg、sum等
-
管道(pipeline)聚合:其他聚合的结果为基础做聚合
具体可查阅官方文档[Aggregations | Elasticsearch Guide 8.3] | Elastic
DSL实现Bucket聚合
按照品牌名聚合
#聚合功能
GET /hotel/_search
{
"size":0, #设置size为0,结果中不包含文档,只包含聚合结果
"aggs": {
"brandAgg": { #给聚合起的名字
"terms": {
"field": "brand",
"size": 10 #希望获取的聚合结果的数量
}
}
}
}
默认情况下,Bucker聚合会统计Bucket内文档的数量,记为_count,并按照__count降序排序
#聚合功能
GET /hotel/_search
{
"size":0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 10,
"order": {
"_count": "asc"
}
}
}
}
}
限定聚合范围
只要添加query条件即可
#聚合功能
GET /hotel/_search
{
"query": {
"range": {
"price": {
"lte": 200
}
}
},
"size":0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 10,
"order": {
"_count": "asc"
}
}
}
}
}
聚合必须的三要素:
- 聚合名称
- 聚合类型
- 聚合字段
聚合可配置的属性有:
- size:指定聚合结果数量
- order:指定聚合结果排序方式
- field:指定聚合字
DSL实现Metrics聚合
取每个品牌的用户评分的min,max,avg
#嵌套聚合metric
GET /hotel/_search
{
"size":0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20
},
"aggs": {
"scoreAgg": {
"stats": {
"field": "score"
}
}
}
}
}
}
对平均分进行排序
#嵌套聚合metric
GET /hotel/_search
{
"size":0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20,
"order": {
"scoreAgg.avg": "desc"
}
},
"aggs": {
"scoreAgg": {
"stats": {
"field": "score"
}
}
}
}
}
}
RestAPI实现聚合
@Test
void testAgg() throws IOException {
//准备request
SearchRequest request=new SearchRequest("hotel");
//准备dsl
request.source().size(0);
request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(10));
//发送请求
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
//对结果处理
Aggregations aggregations = search.getAggregations();
//注意是Terms
Terms brandAgg = aggregations.get("brandAgg");
List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
//遍历
for (Terms.Bucket bucket : buckets) {
String keyAsString = bucket.getKeyAsString();
System.out.println(keyAsString);
}
}
案例:在IUserService中定义方法,实现对品牌、城市、星级的聚合
@Override
public Map<String, List<String>> agg(RequestParams params) {
try {
//获取过滤条件
//准备request
SearchRequest request=new SearchRequest("hotel");
//这一部分可以封装为一个函数
request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(100));
request.source().aggregation(AggregationBuilders.terms("cityAgg").field("city").size(100));
request.source().aggregation(AggregationBuilders.terms("starAgg").field("starName").size(100));
boolQuery(params,request);
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
Aggregations aggregations = search.getAggregations();
Terms brandAgg = aggregations.get("brandAgg");
Terms cityAgg = aggregations.get("cityAgg");
Terms starAgg = aggregations.get("starAgg");
List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
List<? extends Terms.Bucket> buckets1 = cityAgg.getBuckets();
List<? extends Terms.Bucket> buckets2 = starAgg.getBuckets();
List <String>list=new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
String keyAsString = bucket.getKeyAsString();
list.add(keyAsString);
}
Map<String, List<String>> map=new HashMap<>();
map.put("brand",list);
List <String>list1=new ArrayList<>();
for (Terms.Bucket bucket : buckets1) {
String keyAsString = bucket.getKeyAsString();
list1.add(keyAsString);
}
map.put("city",list1);
List <String>list2=new ArrayList<>();
for (Terms.Bucket bucket : buckets2) {
String keyAsString = bucket.getKeyAsString();
list2.add(keyAsString);
}
map.put("starName",list2);
return map;
}catch (Exception e){
throw new RuntimeException(e);
}
}
测试这个方法
@SpringBootTest
class HotelDemoApplicationTests {
@Autowired
private HotelService hotelService;
@Test
void contextLoads() {
Map<String, List<String>> filters = hotelService.filters();
System.out.println(filters);
}
}
实现前端的过滤效果
在IHotelService接口中定义过滤的方法
Map<String, List<String>> filters (RequestParams params);
在上面的代码新增参数params
其中的boolQuery()就是查询实现的过滤
private static void boolQuery(RequestParams params, SearchRequest request) {
String key = params.getKey();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
if (key ==null||"".equals(key)){
//key为空,直接查询所有
// request.source().query(QueryBuilders.matchAllQuery());
boolQueryBuilder.must(QueryBuilders.matchAllQuery());
}
else{
// request.source().query(QueryBuilders.matchQuery("all",key));
boolQueryBuilder.must(QueryBuilders.matchQuery("all", key));
}
//城市
if (params.getCity()!=null&&!"".equals(params.getCity())){
boolQueryBuilder.filter(QueryBuilders.termQuery("city", params.getCity()));
}
//星级
if (params.getStarName()!=null&&!"".equals(params.getStarName())){
boolQueryBuilder.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
//品牌
if (params.getBrand()!=null&&!"".equals(params.getBrand())){
boolQueryBuilder.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
//价格范围
if (params.getMinPrice()!=null&& params.getMaxPrice()!=null){
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(params.getMaxPrice()).gte(params.getMinPrice()));
}
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder,new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD",true),ScoreFunctionBuilders.weightFactorFunction(10))
});
request.source().query(functionScoreQueryBuilder);
}