Elasticsearch高级使用-数据聚合

该示例中用到的索引hotel和代码仓库请参考如下文章:
通过hotel-demo演示RestClient操作Elasticsearch

一、什么是聚合

1、官网

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html

2、简述

聚合是对文档数据的统计、分析、计算

二、聚合的分类

  • Bucket(桶):对文档数据分组,并统计每组数量
  • Metrc(度量):对文档数据做计算,例如avg
  • Pipeline(管道):基于其他聚合结果再做聚合
    在这里插入图片描述

参与聚合的字段类型

必须是:

  • keyword
  • 数值
  • 日期
  • 布尔

三、DSL实现聚合

1、基本概念

聚合必须的三要素

  • 聚合名称
  • 聚合类型
  • 聚合字段

聚合可配置属性有

  • size:指定聚合结果数量
  • order:指定聚合结果排序方式
  • field:指定聚合字段

2、DSL实现Bucket聚合

示例1 统计所有数据中酒店品牌有几种

思路

根据酒店品牌的名称做聚合,类型是term类型,DSL示例:
在这里插入图片描述

基本实现
# 聚合功能
GET /hotel/_search
{
  "size":0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "size": 20
      }
    }
  }
}

当size设置10时,表示只返回10个分组,这个时候可以看到**“sum_other_doc_count” : 39当我们吧size改为20之后,表示返回20个分组,则"sum_other_doc_count" : 0**;注意结果中默认是根据每组的文档数量降序
在这里插入图片描述

聚合结果排序

默认情况下,Bucket聚合会统计Bucket内的文档数量,记为_count,并按照_count降序排序
我们可以修改结果排序方式:
在这里插入图片描述

# 聚合功能,自定义排序规则
GET /hotel/_search
{
  "size":0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "order": {
          "_count": "asc"
        }, 
        "size": 10
      }
    }
  }
}

限定聚合范围

默认情况下,Bucket聚合是对索引库的所有文档做聚合,我们可以限定要聚合的文档范围,只要添加query条件即可
在这里插入图片描述
比如我们限定价格在200以上的


# 聚合功能,限定聚合范围
GET /hotel/_search
{
  "query":{
    "range": {
      "price": {
        "lte": 200
      }
    }
  },
  "size":0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "order": {
          "_count": "asc"
        }, 
        "size": 10
      }
    }
  }
}

3、DSL实现Metric聚合

示例 获取每个品牌的用户评分的min、max、avg等

思路

使用stats聚合
在这里插入图片描述

基本实现

# 聚合功能 嵌套聚合metric
GET /hotel/_search
{
  "size":0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "size": 10
      },
    "aggs":{
      "scoreAgg":{
        "stats": {
          "field": "score"
        }
      }
    }
    }
  }
}

在这里插入图片描述

聚合结果排序

按照打分的平均分降序

# 聚合功能 嵌套聚合metric,按照平均分降序
GET /hotel/_search
{
  "size":0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "size": 10,
        "order": {
          "scoreAgg.avg": "desc"
        }
      },
    "aggs":{
      "scoreAgg":{
        "stats": {
          "field": "score"
        }
      }
    }
    }
  }
}

四、RestAPI实现聚合

1、品牌聚合

在这里插入图片描述

聚合结果解析

在这里插入图片描述

代码

/**
 * 品牌聚合
 */
@Test
void testAggregation() throws IOException {
    // 1. 准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2. 准备DSL
    // 2.1. 设置size
    request.source().size(0);
    // 2.2. 聚合
    request.source().aggregation(AggregationBuilders
            .terms("brandAgg")
            .field("brand")
            .size(10)
    );
    // 3. 发出请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4. 解析结果
    Aggregations aggregations = response.getAggregations();
    // 4.1. 根据聚合名称获取聚合结果
    Terms brandTerms = aggregations.get("brandAgg");
    // 4.2. 获取buckets
    List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
    // 4.3. 遍历
    for(Terms.Bucket bucket: buckets){
        // 4.4. 获取可以
        String key = bucket.getKeyAsString();
        System.out.println(key);
    }

}

2、在IUserService中定义方法,实现对品牌、城市、星级的聚合

在这里插入图片描述

代码

@Override
public Map<String, List<String>> filters() {
    try{
        // 1. 准备Request
        SearchRequest request = new SearchRequest("hotel");
        // 2. 准备DSL
        // 2.1. 设置size
        request.source().size(0);
        // 2.2. 聚合
        buildAggregation(request);
        // 3. 发出请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4. 解析结果
        Map<String, List<String>> result = new HashMap<>();
        Aggregations aggregations = response.getAggregations();
        // 4.1 根据品牌名称,获取品牌结果
        List<String> brandList = getAggByName(aggregations,"brandAgg");
        result.put("品牌",brandList);
        // 4.2 根据城市名称,获取品牌结果
        List<String> cityList = getAggByName(aggregations,"cityAgg");
        result.put("城市",cityList);
        // 4.3 根据星级名称,获取品牌结果
        List<String> starList = getAggByName(aggregations,"starAgg");
        result.put("星级",starList);
        return result;
    }catch (IOException e){
        throw new RuntimeException(e);
    }
}

private List<String> getAggByName(Aggregations aggregations, String aggName) {
    // 4.1. 根据聚合名称获取聚合结果
    Terms brandTerms = aggregations.get(aggName);
    // 4.2. 获取buckets
    List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
    // 4.3. 遍历
    List<String> brandList = new ArrayList<>();
    for(Terms.Bucket bucket: buckets){
        // 4.4. 获取可以
        String key = bucket.getKeyAsString();
        brandList.add(key);
    }
    return brandList;
}

private void buildAggregation(SearchRequest request) {
    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)
    );
}

结果

{品牌=[7天酒店, 如家, 皇冠假日,8, 万怡, 华美达, 和颐, 万豪, 喜来登, 希尔顿, 汉庭, 凯悦, 维也纳, 豪生, 君悦, 万丽, 丽笙], 星级=[], 城市=[上海, 北京, 深圳]}

3、对接前端接口

在这里插入图片描述

代码

// controller
@PostMapping("/filters")
public Map<String, List<String>> getFilters(@RequestBody RequestParams params){
    return hotelService.filters(params);
}
...
...
...
// service
@Override
public Map<String, List<String>> filters(RequestParams params) {
    try{
        // 1. 准备Request
        SearchRequest request = new SearchRequest("hotel");
        // 2. 准备DSL
        // 2.1. query
        buildBasicQuery(params,request);
        // 2.2. 设置size
        request.source().size(0);
        // 2.3. 聚合
        buildAggregation(request);
        // 3. 发出请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4. 解析结果
        Map<String, List<String>> result = new HashMap<>();
        Aggregations aggregations = response.getAggregations();
        // 4.1 根据品牌名称,获取品牌结果
        List<String> brandList = getAggByName(aggregations,"brandAgg");
        result.put("品牌",brandList);
        // 4.2 根据城市名称,获取品牌结果
        List<String> cityList = getAggByName(aggregations,"cityAgg");
        result.put("城市",cityList);
        // 4.3 根据星级名称,获取品牌结果
        List<String> starList = getAggByName(aggregations,"starAgg");
        result.put("星级",starList);
        return result;
    }catch (IOException e){
        throw new RuntimeException(e);
    }
}

效果

注意搜索虹桥,品牌和星级范围会缩小
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值