51_ElasticSearch 基于doc value正排索引的聚合内部原理

本文探讨了ElasticSearch中聚合操作的内部实现机制,重点分析了倒排索引与正排索引结合的方式如何提高搜索及聚合操作的效率,并通过具体示例说明了这种结合方式的优势。

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

51_ElasticSearch 基于doc value正排索引的聚合内部原理

更多干货

一、概述

  • 聚合分析的内部原理是什么?
  • aggs,term,metric avg max,执行一个聚合操作的时候,内部原理是怎样的呢?
  • 用了什么样的数据结构去执行聚合?
  • 是不是用的倒排索引?

二、例子

搜索+聚合,写个示例。搜索 品牌为苹果手机,按照颜色聚合。(苹果手机,每种颜色的台数)

GET /test_index/test_type/_search 
{
	"query": {
		"match": {
			"search_field": "苹果"
		}
	},
	"aggs": {
		"group_by_agg_field": {
			"terms": {
				"field": "agg_field"
			}
		}
	}
}

1. 采用倒排索引来进行聚合操作

纯用倒排索引来实现的弊端,性能是很低下

  • es不是纯用倒排索引来实现搜索 + 聚合。
  • 性能是很低下的原因在与聚合,聚合的时候需要遍历下面例子中所有的颜色
  • 需要遍历所有的倒排索引才能实现聚合

1、实现搜索出苹果。在品牌的倒排索引中搜索。找到是苹果的倒排索引就停止。搜索不需要遍历整个倒排索引效率高

苹果   -    第一台 第二台
小米   -    第三台 第四台

2、 进行聚合

颜色的倒排索引中搜索。要获得所有颜色的话,就需要遍历所有的倒排索引

	 红色 - 第一台 
	 蓝色 - 第二台 
	 红色 - 第三台 
	 蓝色 - 第四台 

2. 倒排索引+正排索引来实现

实现搜索出苹果

苹果   -    第一台 第二台
小米   -    第三台 第四台

进行聚合,只要根据搜索结果依次在正排索引中搜索。并做聚合操作

第一台 	-  红色 
第二台 	-  蓝色 
第三台 	-  红色 
第四台 	-  蓝色 

苹果 --> 第一台,第二台 -> 红色、蓝色


相关文章


<think>我们正在处理Elasticsearch7.x中一个特定的场景:用户希望在一个`bucket_script`管道聚合中,使用父聚合(可能是多层聚合)中`composite`聚合的属性值(即桶的键)进行计算。注意,`composite`聚合是一种多桶聚合,用于高效地分页获取多个字段的聚合结果。 根据引用[2]中提到的composite聚合,我们知道它通常用于多字段聚合,并且可以分页。但是,在管道聚合(如`bucket_script`)中,我们通常只能访问到兄弟聚合的度量值(如sum,avg等)或父聚合的桶的度量值,而无法直接访问到桶的键(即属性值)。然而,在Elasticsearch中,管道聚合的`buckets_path`确实可以引用兄弟聚合的度量值,但桶的键(key)通常不是作为度量值存在的,因此不能直接通过`buckets_path`引用。 但是,我们可以通过一些技巧来实现类似的需求:###思路1.**在`composite`聚合的每个桶中,使用一个`bucket_script`聚合,并利用脚本访问当前桶的键(key)**。在脚本中,我们可以通过`params`访问当前桶的键,因为`bucket_script`聚合是在每个桶的上下文中执行的。2.**注意:**`bucket_script`聚合通常用于引用其他聚合的数值结果,但这里我们实际上需要的是当前桶的键(即属性值)。我们可以通过脚本的`params`对象中的`_bucket`变量来访问当前桶的键值。这是Elasticsearch的一个隐藏特性,在文档中可能没有明确说明,但在一些社区讨论和实践中被使用。 ###具体步骤1. **定义composite聚合**:按照常规方式定义composite聚合,指定多个字段作为聚合的源。2.**在composite聚合下添加一个度量聚合(如stats或value_count)**:这个度量聚合可以是任意的,因为我们实际上不会使用它的值,但它是必须的,因为`bucket_script`需要引用一个聚合路径(尽管我们可以不使用它)。或者,我们可以使用一个简单的`min`或`max`聚合,但也可以考虑使用一个`scripted_metric`聚合来避免不必要的计算。3.**添加`bucket_script`聚合**:在composite聚合内部,我们添加一个`bucket_script`聚合。在脚本中,我们可以通过`params._bucket`来访问当前桶的键值。这个`_bucket`对象包含了当前桶的所有键值对。###示例假设我们有一个索引,其中包含两个字段:`group`(分组)和`value`(数值)。我们想要按`group`和`category`(另一个字段)进行composite聚合,然后在每个桶中计算一个脚本值,该脚本值使用桶的键(即`group`和`category`)进行一些操作(例如,将`group`和`category`拼接成一个字符串,或者进行数值转换等)。```jsonGET/my_index/_search{"size":0,"aggs": {"my_composite":{"composite":{"sources":[{ "group":{ "terms":{ "field":"group.keyword" }} },{"category": {"terms": {"field": "category.keyword"} }}]},"aggs":{//这里可以添加一个度量聚合,比如计算value的平均值"avg_value": {"avg":{ "field":"value" }},//然后添加bucket_script聚合,用于访问桶的键"bucket_keys": {"bucket_script":{"script": {//在脚本中,我们可以通过params._bucket来访问当前桶的键//注意:这里使用Painless脚本语言"source": """//获取当前桶的键,例如group和categorydef groupKey= params._bucket.group;defcategoryKey= params._bucket.category;//这里可以做一些计算,例如将两个键拼接起来,然后返回字符串的长度(仅作为示例)//注意:bucket_script要求返回数值,所以这里返回一个double类型//如果你需要返回字符串,则不能使用bucket_script(因为它设计用于数值),但我们可以返回一个数值表示return (double)(groupKey.length()+ categoryKey.length());"""}}}}}} }``` ###解释-在上面的示例中,`my_composite`聚合生成了多个桶,每个桶由`group`和`category`的组合键构成。-在每个桶内,我们计算了`avg_value`(这只是一个示例,你可以用任何其他聚合,或者甚至不用,但至少需要一个聚合来让`bucket_script`存在)。- `bucket_keys`是一个`bucket_script`聚合,它不引用任何`buckets_path`(因为我们没有在脚本中使用其他聚合的结果,而是直接使用桶的键)。但是,在脚本中,我们可以通过`params._bucket`对象来访问当前桶的键。这个对象包含了我们在composite聚合中定义的所有源字段的键,即`group`和`category`。 -注意:`bucket_script`聚合通常用于数值计算,因此脚本必须返回一个数值(double类型)。如果你需要返回字符串,那么这种方法不适用,因为`bucket_script`的输出必须是数值。在这种情况下,你可能需要重新考虑你的需求,或者使用其他方法(例如在应用层处理)。 ###注意事项-**性能影响**:使用脚本可能会影响性能,特别是在大数据集上。确保脚本是高效的。- **脚本类型**:我们使用的是Painless脚本,这是Elasticsearch推荐的安全脚本语言。 -**版本兼容性**:这种方法在Elasticsearch7.x中有效,但请注意,不同的小版本之间可能有细微差异。- **桶键的可用性**:`params._bucket`对象包含的是当前桶的键,而不是原始文档的字段。因此,它只包含在composite聚合中定义的字段。 ###替代方案如果上述方法不能满足需求(例如需要返回字符串),则可以考虑:1.在应用层处理:先获取composite聚合的结果(包括桶的键和需要的度量值),然后在应用层进行所需的计算。 2.使用`scripted_metric`聚合:但这更复杂,且性能开销大。###总结在Elasticsearch7.x中,要在`bucket_script`聚合中获取composite聚合的属性值(桶的键),可以通过脚本中的`params._bucket`对象来访问。这提供了一种在桶内脚本中访问当前桶键的方法,但请注意`bucket_script`只能输出数值。§§相关问题§§1.如何在Elasticsearch的composite聚合中实现分页?2.Elasticsearch的bucket_script聚合还能引用哪些上下文变量?3.在Elasticsearch中,除了bucket_script,还有哪些管道聚合可以操作桶的键?</think>在 Elasticsearch 7 中,当需要在 `bucket_script` 聚合中访问父级 `composite` 聚合的属性值时,可以通过以下方法实现: ### 解决方案 1. **理解 `composite` 聚合的输出结构** `composite` 聚合会生成包含每个组合键(桶)的嵌套结构,其输出格式为: ```json "buckets": [{ "key": {"field1": value1, "field2": value2}, // 组合键对象 "doc_count": X, "sub_aggs": { ... } // 子聚合结果 }] ``` 2. **在 `bucket_script` 中访问组合键** 通过 `params._bucket.key` 对象访问当前桶的组合键属性: ```json "aggs": { "my_composite": { "composite": { "sources": [ { "group": {"terms": {"field": "group_field"}} }, { "category": {"terms": {"field": "category_field"}} } ] }, "aggs": { "my_script": { "bucket_script": { "script": { "source": """ // 访问组合键属性 def group = params._bucket.key.group; def category = params._bucket.key.category; // 示例计算:返回组合键的哈希值 return (group.hashCode() + category.hashCode()) % 100; """ } } } } } } ``` 3. **访问嵌套的子聚合结果** 若需访问同级的其他聚合结果,使用 `buckets_path` 参数: ```json "aggs": { "my_composite": {...}, "avg_value": {"avg": {"field": "value"}}, "combined_calc": { "bucket_script": { "buckets_path": { "compositeKey": "my_composite._bucket.key.group", // 访问组合键 "avgVal": "avg_value" // 访问同级聚合 }, "script": "params.compositeKey * params.avgVal" } } } ``` ### 关键点说明 1. **`_bucket.key` 对象** - 包含 `composite` 聚合定义的所有组合键字段 - 通过 `params._bucket.key.field_name` 直接访问 - 支持动态字段名访问:`params._bucket.key['dynamic_field']` 2. **脚本中的注意事项** - 使用 **Painless 脚本**(ES 7.x 默认脚本语言) - 属性值为原始数据类型(字符串/数值) - 需处理空值:`def val = params._bucket.key?.field ?: 0` 3. **性能优化建议** - 对组合键字段启用 `doc_values`[^1] - 避免在脚本中执行复杂计算 - 使用 `size` 参数限制返回桶数: ```json "composite": { "sources": [...], "size": 100 // 控制分页大小 } ``` ### 完整示例 ```json GET /sales/_search { "size": 0, "aggs": { "sales_composite": { "composite": { "sources": [ { "region": {"terms": {"field": "region.keyword"}} }, { "product": {"terms": {"field": "product_type.keyword"}} } ] }, "aggs": { "total_sales": {"sum": {"field": "amount"}}, "custom_metric": { "bucket_script": { "script": { "source": """ // 获取组合键和聚合值 def region = params._bucket.key.region; def product = params._bucket.key.product; def sales = params.total_sales; // 自定义计算逻辑 if (region == 'Asia' && product == 'Electronics') { return sales * 1.2; } return sales * 0.9; """ } } } } } } } ``` ### 常见问题解决 1. **属性不存在错误** 检查字段名拼写和大小写,确保与 `composite` 定义一致: ```json // 正确访问 params._bucket.key.region // 错误:大小写不匹配 params._bucket.key.Region // 导致 null ``` 2. **组合键动态访问** 当字段名动态生成时,使用方括号语法: ```java def fieldName = 'dynamic_' + ctx.type; def value = params._bucket.key[fieldName]; ``` 3. **多层嵌套访问** 在子聚合中访问祖父级 `composite` 的属性: ```json "aggs": { "composite_agg": {...}, "nested_agg": { "nested": {"path": "items"}, "aggs": { "script_agg": { "bucket_script": { "script": "params._parent.bucket.key.composite_field" } } } } } ``` 此方法在 Elasticsearch 7.x 中验证有效,可直接在脚本中操作组合键属性值[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值