使用字段的值并将其添加到数组中的新字段 - Mongodb

发布于 2025-01-12 14:57:22 字数 1495 浏览 0 评论 0 原文

我正在尝试将其变成

{
   "fooList" : [
      {
         "bar" : {
            "baz" : 100
         }
      },
      {
         "bar" : {
            "baz" : 200
         }
      }
   ]
},
{
   "fooList" : [
      {
         "bar" : {
            "baz" : 300
         }
      },
      {
         "bar" : {
            "baz" : 400
         }
      }
   ]
}

这样:

{
   "fooList" : [
      {
         "baz" : 100,
         "bar" : {
            "baz" : 100
         }
      },
      {
         "baz" : 200,
         "bar" : {
            "baz" : 200
         }
      }
   ]
},
{
   "fooList" : [
      {
         "baz" : 300,
         "bar" : {
            "baz" : 300
         }
      },
      {
         "baz" : 400,
         "bar" : {
            "baz" : 400
         }
      }
   ]
}

如您所见,我实际上只是将 baz 及其值从 bar 中复制出来,但我的问题是发生在数组内。

db.getCollection(<collection_name>).updateMany(
    {}, 
    { $set: { 'fooList.$[element].baz' : '$fooList.$[element].bar.baz' } },
    { upsert: true ,
      arrayFilters: [{'element.bar' : { $exists : true }}]
    }
)

但这只会将字符串 $fooList.$[element].bar.baz 设置为 baz,通过结果可以看到这里

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "fooList": [
      {
        "bar": {
          "baz": 100
        },
        "baz": "$fooList.$[element].bar.baz"
      }
    ]
  }
]

有人可以告诉我我可能做错了什么,或者这是否可能?谢谢

I am attempting to turn this

{
   "fooList" : [
      {
         "bar" : {
            "baz" : 100
         }
      },
      {
         "bar" : {
            "baz" : 200
         }
      }
   ]
},
{
   "fooList" : [
      {
         "bar" : {
            "baz" : 300
         }
      },
      {
         "bar" : {
            "baz" : 400
         }
      }
   ]
}

into this:

{
   "fooList" : [
      {
         "baz" : 100,
         "bar" : {
            "baz" : 100
         }
      },
      {
         "baz" : 200,
         "bar" : {
            "baz" : 200
         }
      }
   ]
},
{
   "fooList" : [
      {
         "baz" : 300,
         "bar" : {
            "baz" : 300
         }
      },
      {
         "baz" : 400,
         "bar" : {
            "baz" : 400
         }
      }
   ]
}

As you can see, I really just copy baz and its value out of bar, but my issue is that is occurs within an array.

db.getCollection(<collection_name>).updateMany(
    {}, 
    { $set: { 'fooList.$[element].baz' : '$fooList.$[element].bar.baz' } },
    { upsert: true ,
      arrayFilters: [{'element.bar' : { $exists : true }}]
    }
)

But this will only set the string $fooList.$[element].bar.baz into baz, seen here through the results

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "fooList": [
      {
        "bar": {
          "baz": 100
        },
        "baz": "$fooList.$[element].bar.baz"
      }
    ]
  }
]

Can anyone tell me what I might be doing wrong, or if this is even possible? Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

醉殇 2025-01-19 14:57:22

您可以在 mongoDB 4.2+ 的更新中使用聚合管道来完成此操作,如下所示:

db.collection.update({},
[
{
$set: {
  fooList: {
    $map: {
      input: "$fooList",
      in: {
        $mergeObjects: [
          "$this",
          {
            $cond: [
              {
                $ne: [
                  "$this.bar.baz",
                  undefined
                ]
              },
              {
                baz: "$this.bar.baz"
              },
              {}
            ]
          }
        ]
       }
      }
     }
    }
   }
  ],
 {
    multi: true
 })

解释:

  1. 您 $set $map-ping 数组文档并根据嵌套元素“bar.baz”存在的条件进行合并,然后添加对象 baz等于从 bar.baz 获取的值。
  2. 为与更新查询过滤器匹配的所有文档添加 multi:true ,或者您可以使用 updateMany()

playground

不幸的是,当您将更新与聚合管道一起使用时,数组过滤器是不可能的......

对于早期的 mongodb 版本,它看起来像这样:

   db.collection.find({}).forEach(function(d){ var newfooList=[];d.fooList.forEach(function(s){ s["baz"]=s["bar"]["baz"]; printjson(s);newfooList.push(s);  }); d["fooList"]=newfooList ; db.collection.save(d) })

You can do it using aggregation pipeline in the update in mongoDB 4.2+ as follow:

db.collection.update({},
[
{
$set: {
  fooList: {
    $map: {
      input: "$fooList",
      in: {
        $mergeObjects: [
          "$this",
          {
            $cond: [
              {
                $ne: [
                  "$this.bar.baz",
                  undefined
                ]
              },
              {
                baz: "$this.bar.baz"
              },
              {}
            ]
          }
        ]
       }
      }
     }
    }
   }
  ],
 {
    multi: true
 })

Explained:

  1. You $set $map-ping the array documents and merge based on condition where the nested element "bar.baz" exist , then you add object baz equal to the value taken from bar.baz.
  2. Add multi:true to be done for all documents matching the update query filter or you can use updateMany()

playground

Unfortunatelly arrayFilters are not possible when you use update with aggregation pipeline ...

For earlier mongodb versions it will look something like this:

   db.collection.find({}).forEach(function(d){ var newfooList=[];d.fooList.forEach(function(s){ s["baz"]=s["bar"]["baz"]; printjson(s);newfooList.push(s);  }); d["fooList"]=newfooList ; db.collection.save(d) })
自由如风 2025-01-19 14:57:22

这是一个变体,演示了 v4.4“合并到自身”功能,该功能将聚合转换为更新。当您想要处理所有文档时,这是一种有用的方法,因为它消除了 {}) 和 {multi:true} 位>更新

db.foo.aggregate([
    // The $map/$mergeObjects is the SAME as the answer above, just with a little
    // more compact format.  It is import to $project here, NOT $addFields, because
    // we seek to limit the pipeline to just _id (comes along automatically) and 
    // the fooList:
    {$project: {
        fooList: {$map: {
            input: "$fooList",
                in: {$mergeObjects: [
                        "$this",
                        {$cond: [
                            {$ne: ["$this.bar.baz", undefined]},
                            {baz: "$this.bar.baz"},
                            {}  // else empty object                                          
                        ]}
                     ]}
        }}
    }}

    //  ...and now, using _id as the key (fast!), merge fooList back into doc:
    ,{$merge: {
        into: "foo",
        on: [ "_id" ],
        whenMatched: "merge",
        whenNotMatched: "fail"
    }}
]);

Here is a variation that demonstrates the v4.4 "merge onto self" capability that turns aggregate into an update. This is a useful approach when you want to process all the docs because it eliminates the "no filter" ({}) and {multi:true} bits required for update.

db.foo.aggregate([
    // The $map/$mergeObjects is the SAME as the answer above, just with a little
    // more compact format.  It is import to $project here, NOT $addFields, because
    // we seek to limit the pipeline to just _id (comes along automatically) and 
    // the fooList:
    {$project: {
        fooList: {$map: {
            input: "$fooList",
                in: {$mergeObjects: [
                        "$this",
                        {$cond: [
                            {$ne: ["$this.bar.baz", undefined]},
                            {baz: "$this.bar.baz"},
                            {}  // else empty object                                          
                        ]}
                     ]}
        }}
    }}

    //  ...and now, using _id as the key (fast!), merge fooList back into doc:
    ,{$merge: {
        into: "foo",
        on: [ "_id" ],
        whenMatched: "merge",
        whenNotMatched: "fail"
    }}
]);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文