【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`)在大规模数据下的性能瓶颈。同时,它还支持灵活的排序、多级分组和子聚合功能,能够满足复杂的业务需求。

相关推荐
郝开14 小时前
ElasticSearch 分词器介绍及测试:Standard(标准分词器)、English(英文分词器)、Chinese(中文分词器)、IK(IK 分词器)
elasticsearch·中文分词·ik·ik analyzer
kngines16 小时前
【实战ES】实战 Elasticsearch:快速上手与深度实践-3.2.3 案例:新闻搜索引擎的相关性优化
大数据·elasticsearch·搜索引擎
天草二十六_简村人1 天前
JPA编程,去重查询ES索引中的字段,对已有数据的去重过滤,而非全部字典数据
java·大数据·spring boot·elasticsearch·搜索引擎·微服务·架构
C182981825751 天前
ES Filter Query 区别
elasticsearch
Elastic 中国社区官方博客1 天前
Elasticsearch:使用 BigQuery 提取数据
大数据·数据库·elasticsearch·搜索引擎·全文检索
小诸葛IT课堂1 天前
MySQL数据实时同步至Elasticsearch的高效方案:Java实现+源码解析,一文搞定!
java·mysql·elasticsearch
山上春1 天前
常见的 Git 命令
大数据·git·elasticsearch
kngines2 天前
【实战ES】实战 Elasticsearch:快速上手与深度实践-3.1.3高亮与排序的性能陷阱
大数据·elasticsearch·搜索引擎
palomua2 天前
25.3.7#Git命令#merge和rebase的区别
大数据·git·elasticsearch
我的ID配享太庙呀2 天前
Centos的ElasticSearch安装教程
运维·计算机网络·elasticsearch·centos·智能路由器·jenkins·es