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

如何用$set和点表示法将MongoDB嵌入式数组旧值更新到对应元素新字段

问题描述

我在MongoDB中有以下文档,使用PyMongo初始化的代码如下:

from pymongo import MongoClient
client = MongoClient(host='my_host', port=27017)
database = client.forecast
collection = database.regions
collection.delete_many({})
regions = [
    {
        'id': 'DE',
        'sites': [
            {
                'name': 'paper_factory',
                'energy_consumption': 1000
            },
            {
                'name': 'chair_factory',
                'energy_consumption': 2000
            },
        ]
    },
    {
        'id': 'FR',
        'sites': [
            {
                'name': 'pizza_factory',
                'energy_consumption': 3000
            },
            {
                'name': 'foo_factory',
                'energy_consumption': 4000
            },
        ]
    }
]
collection.insert_many(regions)

现在我需要为每个site元素,将sites.energy_consumption属性值复制到新字段sites.new_field中,我编写的聚合操作代码如下:

set_stage = {
    "$set": {
        "sites.new_field": "$sites.energy_consumption"
    }
}

pipeline = [set_stage]
collection.aggregate(pipeline)

但执行后并未实现预期效果:没有为每个site复制对应的单个energy_consumption值,而是将整个sites数组的所有energy_consumption值收集为数组,赋值给了每个site的new_field。例如DE地区的第一个site预期得到'new_field': 1000,实际得到的是'new_field': [1000, 2000],错误的返回结果示例如下:

{
    "_id": ObjectId("61600c11732a5d6b103ba6be"),
    "id": "DE",
    "sites": [
      {
        "name": "paper_factory",
        "energy_consumption": 1000,
        "new_field": [
          1000,
          2000
        ]
      },
      {
        "name": "chair_factory",
        "energy_consumption": 2000,
        "new_field": [
          1000,
          2000
        ]
      }
    ]
  },
  {
    "_id": ObjectId("61600c11732a5d6b103ba6bf"),
    "id": "FR",
    "sites": [
      {
        "name": "pizza_factory",
        "energy_consumption": 3000,
        "new_field": [
          3000,
          4000
        ]
      },
      {
        "name": "foo_factory",
        "energy_consumption": 4000,
        "new_field": [
          3000,
          4000
        ]
      }
    ]
  }

我需要使用什么表达式才能仅获取数组中对应位置的元素值?
是否存在类似当前索引运算符的语法:

$sites[<current_index>].energy_consumption

或者替代点运算符(类似矩阵运算中普通乘法*与逐元素乘法.*的区别),例如:

$sites:energy_consumption

还是说该问题属于MongoDB的bug?

补充说明

我也尝试过使用"$" 位置运算符,例如使用sites.$.new_field或者$sites.$.energy_consumption的写法,但执行后返回报错:

FieldPath field names may not start with '$'
解决方案

这不是MongoDB的bug,属于对数组字段聚合操作的用法理解偏差:直接使用$sites.energy_consumption时,MongoDB会提取整个sites数组中所有元素的energy_consumption值组成新数组,因此会出现每个site的new_field都被赋值为全量数组的问题。

正确方案是使用$map运算符遍历sites数组,对每个元素单独处理:

set_stage = {
    "$set": {
        "sites": {
            "$map": {
                "input": "$sites",
                "as": "site",
                "in": {
                    "$mergeObjects": [
                        "$$site",
                        {"new_field": "$$site.energy_consumption"}
                    ]
                }
            }
        }
    }
}

pipeline = [set_stage]
collection.aggregate(pipeline)

$map会逐个遍历sites数组的元素,将当前遍历的元素赋值给临时变量site,再通过$mergeObjects将原site字段内容和新增的new_field字段合并,最后将处理后的新数组覆盖原sites字段,即可实现每个site的new_field为自身energy_consumption值的效果。

你之前使用位置运算符报错的原因是:普通更新操作的位置运算符$不适用于聚合管道的$set阶段,因此会触发字段名不允许以$开头的语法错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 14:57:04