[Elasticsearch] 聚合中的重要概念 - Buckets(桶)及Metrics(指标)

本文介绍了Elasticsearch中的聚合概念,包括桶(Buckets)和指标(Metrics)两大核心部分。文章详细解释了如何利用桶对文档进行有意义的划分,以及如何通过指标计算桶内文档的统计信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本章翻译自Elasticsearch官方指南的Aggregations-High-level Concepts一章。


高层概念(High-Level Concepts)

和查询DSL一样,聚合(Aggregations)也拥有一种可组合(Composable)的语法:独立的功能单元可以被混合在一起来满足你的需求。这意味着需要学习的基本概念虽然不多,但是它们的组合方式是几近无穷的。

为了掌握聚合,你只需要了解两个主要概念:

Buckets(桶):

满足某个条件的文档集合。

Metrics(指标):

为某个桶中的文档计算得到的统计信息。

就是这样!每个聚合只是简单地由一个或者多个桶,零个或者多个指标组合而成。可以将它粗略地转换为SQL:

SELECT COUNT(color) 
FROM table
GROUP BY color

以上的COUNT(color)就相当于一个指标。GROUP BY color则相当于一个桶。

桶和SQL中的组(Grouping)拥有相似的概念,而指标则与COUNT(),SUM(),MAX()等相似。

让我们仔细看看这些概念。


桶(Buckets)

一个桶就是满足特定条件的一个文档集合:

  • 一名员工要么属于男性桶,或者女性桶。
  • 城市Albany属于New York州这个桶。
  • 日期2014-10-28属于十月份这个桶。

随着聚合被执行,每份文档中的值会被计算来决定它们是否匹配了桶的条件。如果匹配成功,那么该文档会被置入该桶中,同时聚合会继续执行。

桶也能够嵌套在其它桶中,能让你完成层次或者条件划分这些需求。比如,Cincinnati可以被放置在Ohio州这个桶中,而整个Ohio州则能够被放置在美国这个桶中。

ES中有很多类型的桶,让你可以将文档通过多种方式进行划分(按小时,按最流行的词条,按年龄区间,按地理位置,以及更多)。但是从根本上,它们都根据相同的原理运作:按照条件对文档进行划分。


指标(Metrics)

桶能够让我们对文档进行有意义的划分,但是最终我们还是需要对每个桶中的文档进行某种指标计算。分桶是达到最终目的的手段:提供了对文档进行划分的方法,从而让你能够计算需要的指标。

多数指标仅仅是简单的数学运算(比如,min,mean,max以及sum),它们使用文档中的值进行计算。在实际应用中,指标能够让你计算例如平均薪资,最高出售价格,或者百分之95的查询延迟。


将两者结合起来

一个聚合就是一些桶和指标的组合。一个聚合可以只有一个桶,或者一个指标,或者每样一个。在桶中甚至可以有多个嵌套的桶。比如,我们可以将文档按照其所属国家进行分桶,然后对每个桶计算其平均薪资(一个指标)。

因为桶是可以嵌套的,我们能够实现一个更加复杂的聚合操作:

  1. 将文档按照国家进行分桶。(桶)
  2. 然后将每个国家的桶再按照性别分桶。(桶)
  3. 然后将每个性别的桶按照年龄区间进行分桶。(桶)
  4. 最后,为每个年龄区间计算平均薪资。(指标)

此时,就能够得到每个<国家,性别,年龄>组合的平均薪资信息了。它可以通过一个请求,一次数据遍历来完成!



<think>我们正在处理一个关于Elasticsearchcomposite聚合中使用buckets_path时出现的错误:'got:[ArrayMap]ataggregation[_key]'。这个错误通常发生在尝试引用一个多值聚合结果(如terms聚合)而不是单值指标时。在composite聚合中,由于每个是由多个字段组合而成的,因此引用方式需要特别注意。根据用户的问题,他是在composite聚合中遇到了这个问题。composite聚合是一种分页聚合,它允许我们基于多个字段进行分组,每个由这些字段值的组合构成。在composite聚合中,每个的键是一个复合键(由多个字段组成),因此当我们尝试在子聚合中引用这个键时,可能会遇到ArrayMap错误。错误原因:在composite聚合中,每个的键(_key)实际上是一个对象(包含多个字段),而不是一个单一的值。因此,当我们试图在buckets_path中直接引用这个键(例如,在bucket_script聚合中)时,它会返回一个对象(ArrayMap),而bucket_script要求每个路径必须指向一个单值指标。解决方案:我们需要在buckets_path中明确指定复合键中的具体字段,而不是整个_key。或者,我们可以使用脚本来提取复合键中的某个字段。以下是一个示例场景和解决方案:假设我们有一个索引,包含两个字段:department(部门)和region(地区)。我们使用composite聚合按这两个字段分组,并计算每个组合的文档数量。然后,我们想使用bucket_script聚合来计算每个组合的文档数量占总数量的比例。但是,在引用composite聚合键时,我们可能会遇到上述错误。错误示例:```json{"size":0,"aggs":{"composite_agg":{"composite":{"sources":[{"department":{"terms":{"field":"department.keyword"}}},{"region":{"terms":{"field":"region.keyword"}}}]},"aggs":{"doc_count":{"value_count":{"field":"_id"}},"total_docs":{"global":{},"aggs":{"total":{"value_count":{"field":"_id"}}}},"percentage":{"bucket_script":{"buckets_path":{"count":"doc_count","total":"total_docs>total"},"script":"params.count/params.total"}}}}}}```上面的例子中,我们并没有直接引用_key,所以不会出现ArrayMap错误。但是,如果我们试图在bucket_script中使用composite聚合键(例如,我们想要使用department的值),那么可能会尝试这样引用:错误引用:```json"buckets_path":{"dept":"_key.department"//错误!因为_key是一个复合键,在管道聚合中不能这样引用}```然而,在composite聚合中,_key是一个对象,包含多个字段。在管道聚合中,我们不能直接引用对象中的字段,而只能引用单值指标。如果我们确实需要在bucket_script中使用复合键中的某个字段,我们可以通过以下两种方式之一:1.在composite聚合的子聚合中,使用脚本提取出复合键中的特定字段,并存储为一个单值指标聚合,然后在bucket_script中引用这个指标。例如,我们添加一个聚合来获取部门字段的值:```json{"size":0,"aggs":{"composite_agg":{"composite":{"sources":[{"department":{"terms":{"field":"department.keyword"}}},{"region":{"terms":{"field":"region.keyword"}}}]},"aggs":{"dept_value":{"max":{//使用一个指标聚合来获取该中部门的值,因为每个的部门值是唯一的,所以可以用max或min"script":{"source":"params['_source']['department']",//或者使用doc['department.keyword'],但注意脚本的性能"lang":"painless"}}},//...其他聚合}}}}```然后,在bucket_script中就可以引用`dept_value`这个单值聚合了。但是,这种方法有性能开销,因为它会对每个文档执行脚本。2.另一种方法是在composite聚合中,我们不需要直接使用_key中的字段,而是通过其他方式传递(比如在脚本中直接访问键,但Elasticsearch的管道聚合不支持直接访问键的字段)。因此,我们可能需要重新设计聚合逻辑,避免在管道聚合中直接使用复合键的字段。实际上,在bucket_script中,我们通常引用的是指标聚合(如count,sum等),而不是的键。所以,如果用户的问题是在bucket_script中引用了_key,那么应该避免这样做,而是引用一个单值指标。但是,如果用户确实需要在脚本中使用中的某个字段,那么只能通过上面的方法先提取出来。针对用户的问题,我们假设他是在composite聚合的bucket_script中引用了整个_key,导致了ArrayMap错误。那么解决方案就是不要直接引用_key,而是引用一个单值指标。如果确实需要_key中的某个字段,就按照方法1先提取。示例修正:假设我们有一个需求:计算每个部门-地区组合的文档数量,并且我们想要在bucket_script中使用部门字段的值。那么我们可以这样:```json{"size":0,"aggs":{"composite_agg":{"composite":{"sources":[{"department":{"terms":{"field":"department.keyword"}}},{"region":{"terms":{"field":"region.keyword"}}}]},"aggs":{"doc_count":{"value_count":{"field":"_id"}},"dept_name":{//提取部门名称"max":{"script":{"source":"params['_source'].department",//注意:如果字段是keyword,可能需要用doc['department.keyword'].value"lang":"painless"}}},"some_calculation":{"bucket_script":{"buckets_path":{"count":"doc_count","dept":"dept_name"//这里引用的是单值指标},"script":"params.dept+'has'+params.count+'docs'"}}}}}}```注意:上面的脚本中,我们使用了字符串连接,这只是一个示例。实际中,我们可能进行数值计算。但是,这种方法会为每个执行一个脚本(用于提取部门名称),如果数据量很大,可能会影响性能。因此,我们需要权衡。另外,在composite聚合中,我们其实已经知道每个的键值(在返回的buckets中会有_key对象,里面包含了department和region的值)。所以,如果只是为了在返回结果中使用,那么不需要在聚合脚本中获取,而是可以在应用层处理。因此,我们建议:避免在Elasticsearch聚合脚本中使用键,除非必要。如果必要,则使用上述方法提取。最后,用户遇到的错误信息'got:[ArrayMap]ataggregation[_key]',正是因为他试图在buckets_path中引用_key,而_key是一个对象(ArrayMap),不是单值。所以,解决方案就是不要引用_key,而是引用一个单值指标聚合。如果用户并没有直接引用_key,而是引用了其他路径,那么请检查该路径是否指向了多值聚合(如terms聚合)。在composite聚合中,子聚合不能是生成多个聚合(如terms),但可以是metrics聚合(单值或多值,但多值需要指定具体的指标名)。所以,确保buckets_path指向的是metrics聚合(单值)或者多值聚合中的具体指标(如avg、sum等)。例如,如果我们在composite聚合里面嵌套了一个terms聚合,那么我们不能直接引用这个terms聚合,而应该引用它的某个子聚合(单值指标)。否则,会得到ArrayMap错误。总结步骤:1.检查buckets_path中引用的路径,确保最终指向的是一个单值指标聚合。2.如果确实需要引用中的某个字段,则先通过一个脚本指标聚合(如max,min)将其提取为单值,然后引用这个聚合。3.避免在buckets_path中引用生成多个聚合(如terms,composite等)本身,而是引用它们的子聚合(单值指标)。希望这些解释和解决方案对您有帮助。相关问题:1.在Elasticsearchcomposite聚合中,如何引用键(bucketkey)的具体字段?2.如何优化在Elasticsearch聚合中使用脚本的性能?3.除了bucket_script,还有哪些管道聚合可能遇到ArrayMap错误?</think>### Elasticsearch Composite 聚合中 `buckets_path` 报 "ArrayMap at [_key]" 错误解决方案 在 Composite 聚合中使用 `buckets_path` 时遇到 `got: [ArrayMap] at aggregation [_key]` 错误,这是因为 Composite 聚合键(_key)本质上是**多字段值的集合**(ArrayMap 类型),而管道聚合要求 `buckets_path` 必须指向**单值指标**。以下是具体分析和解决方案: --- ### 错误原因深度解析 1. **Composite 聚合的特殊性** Composite 聚合的 `_key` 是一个包含所有源字段值的对象(ArrayMap 类型),例如: ```json "_key": { "department": "IT", "region": "North" // 多字段组合 } ``` 直接引用 `_key` 会返回整个对象而非单值[^1]。 2. **管道聚合的限制** `bucket_script` 等管道聚合要求 `buckets_path` 必须指向单值指标(如 `sum`, `avg`),无法直接处理对象结构[^2]。 --- ### 解决方案(三种方法) #### ✅ 方法1:提取特定字段值 在子聚合中使用 `scripted_metric` 提取目标字段: ```json { "size": 0, "aggs": { "composite_agg": { "composite": { "sources": [ { "dept": {"terms": {"field": "department.keyword"}}}, { "region": {"terms": {"field": "region.keyword"}}} ] }, "aggs": { "dept_value": { // 提取部门字段值 "scripted_metric": { "init_script": "state.value = ''", "map_script": "state.value = doc['department.keyword'].value", "combine_script": "return state.value", "reduce_script": "return states[0]" } }, "calc_ratio": { "bucket_script": { "buckets_path": { "dept": "dept_value", // ✅ 引用单值 "total": "global_agg>total_count" }, "script": "params.dept == 'IT' ? params.total * 0.1 : params.total * 0.05" } } } }, "global_agg": { "global": {}, "aggs": {"total_count": {"value_count": {"field": "_id"}}} } } } ``` #### ✅ 方法2:使用 Painless 脚本直接访问 在 `bucket_script` 中通过 `_key` 对象访问字段: ```json "calc_ratio": { "bucket_script": { "buckets_path": { "total": "global_agg>total_count" }, "script": """ def dept = _key.dept.value; // 直接访问 _key 的字段 dept == 'IT' ? params.total * 0.1 : params.total * 0.05 """ } } ``` #### ✅ 方法3:添加中间指标聚合 为需要字段创建独立指标: ```json "composite_agg": { "composite": {...}, "aggs": { "dept_field": { "max": {"field": "department.keyword"} // 单值指标 }, "calc_ratio": { "bucket_script": { "buckets_path": { "dept": "dept_field", // ✅ 安全引用 "total": "global_agg>total_count" }, "script": "params.dept == 'IT' ? ..." } } } } ``` --- ### 关键注意事项 1. **路径验证原则** 确保 `buckets_path` 末端满足: - ❌ 错误路径:`"composite_agg>_key"` - ✅ 正确路径:`"composite_agg>dept_value"`(单值指标) 2. **脚本优化技巧** - 使用 `doc['field'].value` 代替 `_source.field` 提升性能 - 添加空值处理:`def dept = _key?.dept?.value ?: ''` 3. **Composite 分页影响** 使用 `after_key` 分页时需保持聚合结构一致,避免路径变化[^3]。 --- ### 调试建议 1. **先验证 Composite 聚合结构**: ```json GET /your_index/_search { "size": 0, "aggs": { "composite_agg": { "composite": {...} } } } ``` 检查返回的中 `_key` 结构。 2. **逐步添加子聚合** 先添加指标聚合(如 `dept_value`),确认可单独运行后再添加 `bucket_script`。 --- ### 常见错误场景修复表 | 错误场景 | 错误原因 | 解决方案 | |----------------------------------|-----------------------------|----------------------------| | 直接引用 `_key` | 返回 ArrayMap 对象 | 使用方法1或2提取具体字段 | | 路径包含多值层 | 未穿透到单值 | 添加中间指标聚合(方法3) | | 脚本未处理字段缺失 | 访问不存在的字段 | 添加空值保护 `_key?.field` | --- ### 相关问题 1. 如何在 Composite 聚合结果中访问多个字段值进行计算? 2. Elasticsearch 管道聚合支持哪些脚本语言特性? 3. 使用 Composite 聚合分页时如何保持聚合结构一致? > 解决方案基于 Elasticsearch 对管道聚合的约束:**`buckets_path` 必须解析为单值指标**。Composite 聚合的 `_key` 作为特殊的多字段对象,需通过脚本或中间聚合转换才能满足要求[^1][^2]。实践中建议优先使用方法2(直接脚本访问),在需要复用字段值时采用方法1或3[^3]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值