如何使用 $set 和点表示法使用相应的旧元素更新嵌入的数组元素?

How to use $set and dot notation to update embedded array elements using corresponding old element?

我在 MongoDb 中有以下文件:

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)

现在我想将 属性 sites.energy_consumption 复制到每个站点的新字段 sites.new_field

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

pipeline = [set_stage]
collection.aggregate(pipeline)

但是,不是复制每个站点的单个值,而是收集所有站点值并添加为数组。 Intead of 'new_field': [1000, 2000] 我想为第一个站点获取 'new_field': 1000

{
    "_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

或者这是一个错误?

编辑

我也尝试使用“$”positional operator,例如与

sites.$.new_field

$sites.$.energy_consumption

但是我得到了错误

FieldPath field names may not start with '$'

相关:

https://docs.mongodb.com/manual/reference/operator/aggregation/set/#std-label-set-add-field-to-embedded

In MongoDB how do you use $set to update a nested value/embedded document?

使用$addFields

db.collection.update({},
[
  {
    "$addFields": {
      "sites": {
        $map: {
          input: "$sites",
          as: "s",
          in: {
            name: "$$s.name",
            energy_consumption: "$$s.energy_consumption",
            new_field: {
              $map: {
                input: "$sites",
                as: "value",
                in: "$$value.energy_consumption"
              }
            }
          }
        }
      }
    }
  }
])

mongoplayground

我发现以下设置完整 sites 而不是仅使用点符号指定新字段的丑陋解决方法:

a) 基于javascript函数

set_stage = {
    "$set": {
        "sites": {
            "$function": {
                "body": "function(sites) {return sites.map(site => {site.new_field = site.energy_consumption_in_mwh; return site})}",
                "args": ["$sites"],
                "lang": "js"
            }
        }
    }
}

b) 基于地图和mergeObjects

set_stage = {
    "$set": {
        "sites": {
            "$map": {
                "input": "$sites",
                "in": {
                    "$mergeObjects": ["$$this", {
                        "new_field": "$$this.energy_consumption_in_mwh"
                    }]
                }
            }
        }
    }
}

如果点运算符表达式有某种 $$this 上下文,允许更优雅的解决方案,请告诉我。

如果通过选择该字段是数组的成员,您将选择所有这些字段。

{ar :[{"a" : 1}, {"a" : 2}]}

"$ar.a" = [1 ,2]

此外,您不能将更新运算符与聚合混合使用,您不能使用诸如 $sites.$.energy_consumption,如果要进行聚合,则必须使用聚合运算符,只有 $match 阶段可以使用查询运算符。

查询

  • 与您使用 $setField
  • 的解决方案略有不同
  • 我想它会更快,但可能差别不大
  • 不用javascript会比较慢
  • 这是>=MongoDB5的解,$setField是new运算符

Test code here

aggregate(
[{"$set":
  {"sites":
   {"$map":
    {"input":"$sites",
     "in":
     {"$setField":
      {"field":"new_field",
       "input":"$$this",
       "value":"$$this.energy_consumption"}}}}}}]
)