You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ElasticSearch中聚合两个数组关联项的平均值

解决ElasticSearch跨文档数组配对元素的平均值聚合问题

核心思路

你的问题本质是要解决两个数组元素一一对应关联的聚合问题——ES默认会扁平化数组,直接聚合会导致runners和runners_times的元素错位。下面提供两种可行方案,优先推荐修改数据结构(方案一),这是最稳定高效的方式。


方案一:修改数据结构为嵌套对象数组(最优解)

把原有的两个独立数组字段,合并成一个嵌套的对象数组字段(比如runner_times),每个对象包含name和time两个属性,从根本上保证关联关系不丢失。

1. 创建新的索引映射

PUT /runner_stats
{
  "mappings": {
    "properties": {
      "runner_times": {
        "type": "nested",
        "properties": {
          "name": {"type": "keyword"},
          "time": {"type": "integer"}
        }
      }
    }
  }
}

2. 重新导入数据

将原有文档转换为嵌套结构,比如原文档1变为:

{
  "runner_times": [
    {"name": "person_a", "time": 100},
    {"name": "person_b", "time": 120}
  ]
}

3. 执行聚合查询

使用nested聚合+terms聚合+avg聚合,直接得到每个跑步者的平均用时:

GET /runner_stats/_search
{
  "size": 0,
  "aggs": {
    "nested_runner_times": {
      "nested": {
        "path": "runner_times"
      },
      "aggs": {
        "group_by_runner": {
          "terms": {
            "field": "runner_times.name"
          },
          "aggs": {
            "avg_run_time": {
              "avg": {
                "field": "runner_times.time"
              }
            }
          }
        }
      }
    }
  }
}

查询结果会直接返回你需要的数值:person_a:95、person_b:110、person_c:120。


方案二:使用脚本字段动态配对(无需修改数据)

如果无法修改现有数据结构,可以通过Painless脚本在查询时动态将runners和runners_times的对应元素组合成键值对,再进行聚合。

聚合查询DSL

GET /your_index/_search
{
  "size": 0,
  "runtime_mappings": {
    "runner_time_pair": {
      "type": "keyword",
      "script": """
        def runners = doc['runners.keyword'];
        def times = doc['runners_times'];
        if (runners.size() != times.size()) {
          return;
        }
        for (int i = 0; i < runners.size(); i++) {
          emit(runners[i] + '|' + times[i]);
        }
      """
    }
  },
  "aggs": {
    "group_by_runner": {
      "terms": {
        "script": {
          "source": "doc['runner_time_pair'].value.split('\\|')[0]"
        }
      },
      "aggs": {
        "avg_time": {
          "avg": {
            "script": {
              "source": "Integer.parseInt(doc['runner_time_pair'].value.split('\\|')[1])"
            }
          }
        }
      }
    }
  }
}

这个脚本先将每个跑步者和对应时间拼接成person_a|100格式的字符串,再通过聚合拆分计算平均值。注意这种方式性能低于方案一,数据量大时不推荐。


构建柱状图仪表盘(以Kibana为例)

拿到聚合结果后,在Kibana中构建柱状图的步骤:

  • 进入Visualize Library,创建新的Vertical Bar可视化
  • 选择对应的索引模式
  • 在Buckets的X轴选择Terms聚合,字段选跑步者名称(方案一选runner_times.name,方案二选脚本生成的runner字段)
  • 在Metrics处选择Average,字段选对应的时间字段(方案一选runner_times.time,方案二选脚本提取的时间值)
  • 调整图表样式后,保存到仪表盘即可

内容的提问来源于stack exchange,提问作者IgorStan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:16:14