【Elasticsearch】 Composite Aggregation 详解

1.什么是 Composite Aggregation?

Composite Aggregation 是 Elasticsearch 中的一种特殊聚合方式,适用于需要分页展示的聚合结果。它与传统的聚合方式不同,采用了基于游标的分页模型。这种聚合方式可以高效地处理多级聚合中的所有桶,并支持分页功能。

2.基本结构

一个典型的 Composite Aggregation 查询如下:

```json

GET /your_index_name/_search

{

"size": 0,

"aggs": {

"my_composite_agg": {

"composite": {

"size": 10,

"sources": [

{

"field1": {

"terms": {

"field": "your_field_name1"

}

}

},

{

"field2": {

"terms": {

"field": "your_field_name2"

}

}

}

]

}

}

}

}

```

在上述查询中:

• `sources`定义了按哪些字段分组,字段顺序决定了分组键(bucket key)的生成顺序。

• `size`定义每页的桶数量。

• 响应结果中的`after_key`用于获取下一页数据。

3.分页机制

Composite Aggregation 的分页机制通过`after`参数实现。每次查询返回指定数量的桶,并通过`after_key`提供下一页的游标。这种方式可以确保分页查询中数据无重复、无遗漏。

例如:

```json

GET /your_index_name/_search

{

"size": 0,

"aggs": {

"my_composite_agg": {

"composite": {

"size": 10,

"sources": [

{

"field1": {

"terms": {

"field": "your_field_name1"

}

}

}

],

"after": {

"field1": "last_value_of_field1"

}

}

}

}

}

``

4.排序和方向

Composite Aggregation 支持对每个值源进行排序,可以通过设置`order`参数为`asc`(升序)或`desc`(降序)。

```json

GET /your_index_name/_search

{

"size": 0,

"aggs": {

"my_composite_agg": {

"composite": {

"size": 10,

"sources": [

{

"field1": {

"terms": {

"field": "your_field_name1",

"order": "desc"

}

}

},

{

"field2": {

"terms": {

"field": "your_field_name2",

"order": "asc"

}

}

}

]

}

}

}

}

```

5.处理缺失值

默认情况下,缺少指定字段值的文档会被忽略。通过设置`missing_bucket`参数为`true`,可以将这些文档包含在响应中。

```json

GET /your_index_name/_search

{

"size": 0,

"aggs": {

"my_composite_agg": {

"composite": {

"size": 10,

"sources": [

{

"field1": {

"terms": {

"field": "your_field_name1",

"missing_bucket": true

}

}

}

]

}

}

}

}

```

6.性能优化

Composite Aggregation 的设计特别适合大规模数据的聚合和分页,是传统`from + size`分页方法的高效替代方案。为了进一步优化性能,建议在索引中设置索引排序,使其与复合聚合中的源顺序部分或完全匹配。

7.应用场景

Composite Aggregation 适用于以下场景:

• 需要分页展示聚合结果。

• 处理大规模数据时,需要高效分页和排序。

• 需要对多个字段进行分组和聚合。

通过上述特性,Composite Aggregation 提供了一种强大且灵活的方式来处理复杂的聚合需求,特别是在需要分页和排序的场景中表现出色。

好的,下面我将通过一个具体的例子来展示如何使用 Composite Aggregation 来实现分页聚合查询。假设我们有一个电商数据集,其中包含商品的销售记录,我们希望按日期和商品类别进行分组,并计算每个分组的销售总额。

数据示例

假设我们的索引名为`sales`,其中的文档如下:

```json

{

"timestamp": "2024-01-01T00:00:00Z",

"product": "T-shirt",

"category": "Clothing",

"price": 20

}

{

"timestamp": "2024-01-01T00:00:00Z",

"product": "Jeans",

"category": "Clothing",

"price": 40

}

{

"timestamp": "2024-01-02T00:00:00Z",

"product": "T-shirt",

"category": "Clothing",

"price": 20

}

{

"timestamp": "2024-01-02T00:00:00Z",

"product": "Laptop",

"category": "Electronics",

"price": 1000

}

```

查询目标

我们希望按日期和商品类别进行分组,并计算每个分组的销售总额。同时,我们希望分页显示结果,每页显示 2 个分组。

第一页查询

首先,我们查询第一页的结果:

```json

GET /sales/_search

{

"size": 0,

"aggs": {

"sales_by_date_and_category": {

"composite": {

"size": 2,

"sources": [

{

"date": {

"date_histogram": {

"field": "timestamp",

"calendar_interval": "1d"

}

}

},

{

"category": {

"terms": {

"field": "category"

}

}

}

]

},

"aggregations": {

"total_sales": {

"sum": {

"field": "price"

}

}

}

}

}

}

```

解释

• `size`:每页返回的分组数量。

• `sources`:定义了两个分组字段:

• `date`:按日期分组,每天一个桶。

• `category`:按商品类别分组。

• `aggregations`:在每个复合桶中计算销售总额。

查询结果

返回的结果如下:

```json

{

"aggregations": {

"sales_by_date_and_category": {

"after_key": {

"date": 1704115200000,

"category": "Clothing"

},

"buckets": [

{

"key": {

"date": 1704028800000,

"category": "Clothing"

},

"doc_count": 2,

"total_sales": {

"value": 60

}

},

{

"key": {

"date": 1704115200000,

"category": "Clothing"

},

"doc_count": 1,

"total_sales": {

"value": 20

}

}

]

}

}

}

```

解释

• `buckets`:包含两个分组:

• 第一个分组:`2024-01-01`的`Clothing`类别,销售总额为 60。

• 第二个分组:`2024-01-02`的`Clothing`类别,销售总额为 20。

• `after_key`:提供了下一页的游标。

第二页查询

使用`after_key`查询下一页的结果:

```json

GET /sales/_search

{

"size": 0,

"aggs": {

"sales_by_date_and_category": {

"composite": {

"size": 2,

"sources": [

{

"date": {

"date_histogram": {

"field": "timestamp",

"calendar_interval": "1d"

}

}

},

{

"category": {

"terms": {

"field": "category"

}

}

}

],

"after": {

"date": 1704115200000,

"category": "Clothing"

}

},

"aggregations": {

"total_sales": {

"sum": {

"field": "price"

}

}

}

}

}

}

``

查询结果

返回的结果如下:

```json

{

"aggregations": {

"sales_by_date_and_category": {

"after_key": {

"date": 1704115200000,

"category": "Electronics"

},

"buckets": [

{

"key": {

"date": 1704115200000,

"category": "Electronics"

},

"doc_count": 1,

"total_sales": {

"value": 1000

}

}

]

}

}

}

``

解释

• `buckets`:包含一个分组:

• `2024-01-02`的`Electronics`类别,销售总额为 1000。

• `after_key`:提供了下一页的游标。

通过这种方式,我们可以高效地分页查询聚合结果,而不会遗漏或重复任何数据。

好的,接下来我们继续探讨如何处理更多分页结果,以及如何优化和扩展这个查询。

继续分页查询

假设我们继续查询下一页,使用上一页返回的`after_key`:

```json

GET /sales/_search

{

"size": 0,

"aggs": {

"sales_by_date_and_category": {

"composite": {

"size": 2,

"sources": [

{

"date": {

"date_histogram": {

"field": "timestamp",

"calendar_interval": "1d"

}

}

},

{

"category": {

"terms": {

"field": "category"

}

}

}

],

"after": {

"date": 1704115200000,

"category": "Electronics"

}

},

"aggregations": {

"total_sales": {

"sum": {

"field": "price"

}

}

}

}

}

}

```

查询结果

如果返回结果为空,说明已经到达最后一页:

```json

{

"aggregations": {

"sales_by_date_and_category": {

"buckets": []

}

}

}

``

这表明所有分组已经查询完毕。

性能优化

为了进一步优化性能,可以考虑以下几点:

  1. 设置合理的`size`参数:根据实际需求设置合适的分页大小,避免过大或过小。

  2. 索引排序:如果数据量很大,可以在索引创建时设置索引排序,使其与聚合的字段顺序一致。例如:

```json

PUT /sales

{

"settings": {

"index": {

"sort.field": ["timestamp", "category"],

"sort.order": ["asc", "asc"]

}

}

}

```

  1. 禁用`track_total_hits`:在分页查询中,通常不需要统计总命中数,可以通过设置`track_total_hits: false`来节省资源。

扩展应用

Composite Aggregation 不仅可以用于分页查询,还可以结合其他聚合功能,例如:

• 计算平均值:在每个分组中计算平均销售额。

```json

"aggregations": {

"average_sales": {

"avg": {

"field": "price"

}

}

}

```

• 多级分组:可以增加更多分组字段,例如按地区分组。

```json

"sources": [

{

"date": {

"date_histogram": {

"field": "timestamp",

"calendar_interval": "1d"

}

}

},

{

"category": {

"terms": {

"field": "category"

}

}

},

{

"region": {

"terms": {

"field": "region"

}

}

}

]

```

总结

通过 Composite Aggregation,我们可以高效地实现分页聚合查询,避免了传统分页方法(如`from + size`)在大规模数据下的性能瓶颈。同时,它还支持灵活的排序、多级分组和子聚合功能,能够满足复杂的业务需求。

相关推荐
A__tao几秒前
一键实现 SQL 转 Elasticsearch Mapping(支持字段注释 + meta 描述)
数据库·sql·elasticsearch
老纪的技术唠嗑局20 小时前
告别OpenClaw配置丢失——Mindkeeper内测版邀测
大数据·elasticsearch·搜索引擎
Elasticsearch20 小时前
使用 Elasticsearch + Jina embeddings 进行无监督文档聚类
elasticsearch
勇哥的编程江湖1 天前
flinkcdc streaming 同步数据到es记录过程
大数据·elasticsearch·flink·flinkcdc
曾阿伦1 天前
Elasticsearch 7.x 常用命令备忘录
大数据·elasticsearch·搜索引擎
斯特凡今天也很帅1 天前
Elasticsearch数据库专栏(二)DSL语句总结(更新中)
大数据·elasticsearch·搜索引擎
要记得喝水1 天前
适用于 Git Bash 的脚本,批量提交和推送多个仓库的修改
git·elasticsearch·bash
二十七剑1 天前
Elasticsearch的索引问题
大数据·elasticsearch·搜索引擎
A__tao1 天前
Elasticsearch Mapping 一键生成 Java 实体类(支持嵌套 + 自动过滤注释)
java·python·elasticsearch
A__tao1 天前
Elasticsearch Mapping 一键生成 Proto 文件(支持嵌套 + 注释过滤)
大数据·elasticsearch·jenkins