ElasticSearch - 基于 DSL 、JavaRestClient 实现数据聚合
发布日期:2025-03-29 17:30:38 浏览次数:5 分类:精选文章

本文共 4687 字,大约阅读时间需要 15 分钟。

数据聚合

1.1 基本概念

1.1.1 聚合分类

聚合(aggregations)是对文档数据进行统计分析和运算的重要功能。类似于 MySQL中的聚合函数(如 avg、max 等),Elasticsearch也提供了丰富的聚合功能。

Elasticsearch中的聚合分为三大类,分别是桶(Bucket)聚合、度量(Metric)聚合和管道(Pipeline)聚合。

桶(Bucket)聚合

桶聚合用于对文档进行分组,类似于 MySQL中的 group by 操作。常见的桶聚合类型有:

  • TermAggregation:按照文档字段值(如品牌名称)分组。
  • DataHistogram:按日期阶梯分组(如一天为一组、一周为一组)。

度量(Metric)聚合

度量聚合用于对分组后的文档数据进行计算(如求最大值、最小值、平均值等),与 MySQL中的聚合函数类似。Elasticsearch还提供一个特殊的度量聚合 stats,可以同时计算多个统计指标。

管道(Pipeline)聚合

管道聚合用于对其他聚合的结果进行聚合。例如,先对酒店按品牌分组(桶聚合),然后对每个品牌的酒店价格进行平均值计算(度量聚合),最后再对价格平均值进行排序(度量聚合)。

:管道聚合的使用较少,不是黑马旅游案例的重点。

1.1.2 特点

需要注意的是,参与聚合的字段必须是不可分词的字段(如字符串字段、日期字段、布尔字段等)。因此,日期、数值、布尔类型的字段不能参与聚合。


1.2 DSL 实现 Bucket 聚合

在 Elasticsearch 中,通过 DSL(Domain Specific Language)实现聚合功能。以下是 Bucket 聚合的基本语法和使用场景。

1.2.1 Bucket 聚合基础语法

GET /索引库名/_search {    "size": 0,    "aggs": {        "自定义聚合名": {            "terms": {                "field": "字段名",                "size": 20            }        }    }}
  • 聚合名称:自定义聚合名。
  • 聚合类型terms 表示按字段值分组。
  • 聚合字段:参与聚合的字段。
  • 聚合结果数量size 可设置为具体数字,值超过总数无影响。

示例:按酒店品牌进行分组,字段为 brand

1.2.2 Bucket 聚合结果排序

默认情况下,Bucket 聚合会统计每组文档数量(_count),并根据 _count 降序排序。

示例:按酒店品牌分组,并按照每个品牌的酒店数量进行升序排序。

1.2.3 Bucket 聚合限定范围

默认情况下,Bucket 聚合会对索引库的所有文档进行聚合。可以通过查询条件对聚合范围进行限定,以减少对内存的消耗。

示例:搜索价格 小于等于 200 的酒店,并按品牌分类。


1.3 DSL 实现 Metrics 聚合

度量聚合是对分组后的每组文档数据进行计算。例如,查询每个品牌用户评分的最小值、最大值、平均值等。

1.3.1 度量聚合语法

GET /索引库名/_search {    "size": 0,    "aggs": {        "自定义聚合名": {            "terms": {                "field": "字段名",                "size": 20            },            "aggs": {                "自定义度量聚合": {                    "stats": {                        "field": "score"                    }                }            }        }    }}

步骤

  • 定义聚合名称。
  • terms 聚合按字段值分组。
  • 在分组基础上定义度量聚合(如 stats 聚合)。
  • 示例:按品牌分组,计算每组用户评分的平均值。


    1.4 基于 JavaRestClient 实现聚合

    1.4.1 组装请求

    示例代码

    @Testpublic void testAggregation() 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);    handlerResponse(response);}

    1.4.2 解析响应

    // 解析聚合结果Aggregations aggregations = response.getAggregations();Terms terms = aggregations.get("brandAgg");List
    buckets = terms.getBuckets();for (Bucket bucket : buckets) { String key = bucket.getKeyAsString(); System.out.println(key);}

    1.5 黑马旅游案例

    1.5.1 需求

    通过聚合索引库中的酒店数据,动态生成搜索页面的品牌、城市、星级信息。


    1.5.2 对接前端接口

    请求参数

    {    "brand": ["王国酒店", "大酒店集团"],    "city": ["上海", "北京"],    "starName": [1, 2, 3]}

    响应格式

    {    "城市": ["上海", "北京"],    "品牌": ["王国酒店", "大酒店集团"],    "星级": ["一星级", "二星级", "三星级"]}

    1.5.3 编写 controller

    示例代码

    @RequestMapping("/filters")public Map
    > filters(@RequestBody RequestParams params) { return hotelService.filters(params);}

    1.5.4 添加 filters 接口

    public interface IHotelService extends IService
    { PageResult search(RequestParams params); Map
    > filters(RequestParams params);}

    1.5.5 实现接口

    @Overridepublic Map
    > filters(RequestParams params) { try { SearchRequest request = new SearchRequest("hotel"); handlerBoolQueryBuilder(request, params); request.source().size(0); buildAggregation(request); SearchResponse response = client.search(request, RequestOptions.DEFAULT); Map
    > result = new HashMap<>(); Aggregations aggregations = response.getAggregations(); List
    brandAgg = getAggListByName(aggregations, "brandAgg"); result.put("brand", brandAgg); List
    cityAgg = getAggListByName(aggregations, "cityAgg"); result.put("city", cityAgg); List
    starAgg = getAggListByName(aggregations, "starAgg"); result.put("starName", starAgg); return result; } catch (IOException e) { System.out.println("[HotelService] 酒店数据聚合失败!"); e.printStackTrace(); return null; }}private List
    getAggListByName(Aggregations aggregations, String aggName) { Terms terms = aggregations.get(aggName); List
    buckets = terms.getBuckets(); List
    result = new ArrayList<>(); for (Bucket bucket : buckets) { result.add(bucket.getKeyAsString()); } return result;}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));}

    通过以上方法,可以实现酒店数据的动态聚合,满足前端页面对品牌、城市、星级等字段的实时查询需求。

    上一篇:ElasticSearch - 基于 JavaRestClient 操作索引库和文档
    下一篇:ElasticSearch - 基于 docker 部署 es、kibana,配置中文分词器、扩展词词典、停用词词典

    发表评论

    最新留言

    很好
    [***.229.124.182]2025年04月28日 13时10分50秒