在 MongoDB 聚合期间,我需要更新嵌套字段的值,但该字段的名称是另一个字段的值。 示例文档:
{
"prop": {
"nestedField": "oldValue" // Property to be updated
},
"keyPath": "prop.nestedField", // Path to the target field using dot notation
"val": "newValue" // New value to set to target field
}
所以聚合后应该变成:
{
"prop": {
"nestedField": "newValue" // Value has been updated
},
...
}
该路径保证存在于文档中。
我尝试使用
$arrayToObject
,但对于嵌套的关键路径,它将值解释为文字,并创建具有关键路径的精确值(如 "prop.nestedField"
)而不是嵌套对象的字段。
当然,这可以在聚合后完成,但以下阶段需要使用更新的数据,所以我有兴趣在聚合内进行。
这几天一直在努力解决这个问题,非常感谢一些帮助
高度嵌套的文档和使用动态值作为字段名称被视为反模式,应避免。如果可能的话,请考虑重构您的架构。
但是对于您当前的场景,如果
keyPath
中的值仅嵌套 1 层,仍然可以通过使用 $arrayToObject
动态构造更新对象并使用 $mergeObjects
将其合并到 $$ROOT
对象来管理。
$split
将 $keyPath
分解为包含外层字段名称 prop
和内层字段名称 nestedField
$arrayToObject
两次,借助 $first
和 $last
构造更新对象,以访问外层和内层字段名称。更新对象应该如下所示:{
"prop": {
"nestedField": "$val"
}
}
$mergeObjects
将更新对象合并到 $$ROOT
$replaceWith
保存更新$unset
辅助字段tokens
db.collection.update({},
[
{
"$set": {
"tokens": {
"$split": [
"$keyPath",
"."
]
}
}
},
{
"$replaceWith": {
"$mergeObjects": [
"$$ROOT",
{
"$arrayToObject": [
[
{
k: {
"$first": "$tokens"
},
v: {
"$arrayToObject": [
[
{
k: {
"$last": "$tokens"
},
v: "$val"
}
]
]
}
}
]
]
}
]
}
},
{
"$unset": "tokens"
}
])
注意:如果您的
keyPath
嵌套多次,则此解决方案不太可能扩展以构造动态级别的更新对象,因此无法应用。即使可以这样做,也会引入很多代码味道并使查询难以维护。再次强调,如果可能的话,建议重构架构。