首页 > 解决方案 > 在同一文档中按 ID 以原子方式将对象从一个数组移动到另一个数组

问题描述

我的数据如下所示:

{
  "_id": ObjectId("4d525ab2924f0000000022ad"),
  "arrayField": [
    { id: 1, other: 23 },
    { id: 2, other: 21 },
    { id: 0, other: 235 },
    { id: 3, other: 765 }
  ],
  "someOtherArrayField": []
}

给定一个嵌套对象的 ID ( 0),我想$pull将元素从一个数组 ( arrayField) 中取出,并将它放到同一个文档中的$push另一个数组 ( ) 中。someOtherArrayField结果应如下所示:

{
  "_id": ObjectId("id"), 
  "arrayField": [
    { id: 1, other: 23 },
    { id: 2, other: 21 },
    { id: 3, other: 765 }
  ],
  "someOtherArrayField": [
    { id: 0, other: 235 }
  ]
}

我意识到我可以通过查找然后更新来完成此操作,即

db.foo.findOne({"_id": param._id})
.then((doc)=>{
  db.foo.update(
    {
      "_id": param._id
    },
    {
      "$pull": {"arrayField": {id: 0}},
      "$push": {"someOtherArrayField": {doc.array[2]} }
    }
  )
})

但我正在寻找一个原子操作,例如,在伪代码中,这个:

db.foo.update({"_id": param._id}, {"$move": [{"arrayField": {id: 0}}, {"someOtherArrayField": 1}]}

有没有一种原子方法可以做到这一点,也许使用 MongoDB 4.2 的能力来指定一个更新命令的管道?那看起来怎么样?

我发现这篇文章慷慨地提供了我使用的数据,但提供的解决方案不是原子操作。MongoDB 4.2 是否可以实现原子解决方案?

标签: mongodb

解决方案


这是一个例子:

> db.baz.find()
> db.baz.insert({
...   "_id": ObjectId("4d525ab2924f0000000022ad"),
...   "arrayField": [
...     { id: 1, other: 23 },
...     { id: 2, other: 21 },
...     { id: 0, other: 235 },
...     { id: 3, other: 765 }
...   ],
...   "someOtherArrayField": []
... })
WriteResult({ "nInserted" : 1 })

function extractIdZero(arrayFieldName) {
    return {$arrayElemAt: [
        {$filter: {input: arrayFieldName, cond: {$eq: ["$$this.id", 0]}}}, 
        0
    ]};
}

extractIdZero("$arrayField")
{
    "$arrayElemAt" : [
        {
            "$filter" : {
                "input" : "$arrayField",
                "cond" : {
                    "$eq" : [
                        "$$this.id",
                        0
                    ]
                }
            }
        },
        0
    ]
}

db.baz.updateOne(
    {_id: ObjectId("4d525ab2924f0000000022ad")},
    [{$set: {
         arrayField: {$filter: {
             input: "$arrayField",
             cond: {$ne: ["$$this.id", 0]}
         }},
         someOtherArrayField: {$concatArrays: [
             "$someOtherArrayField",
             [extractIdZero("$arrayField")]
         ]}
     }}
    ])
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.baz.findOne()
{
    "_id" : ObjectId("4d525ab2924f0000000022ad"),
    "arrayField" : [
        {
            "id" : 1,
            "other" : 23
        },
        {
            "id" : 2,
            "other" : 21
        },
        {
            "id" : 3,
            "other" : 765
        }
    ],
    "someOtherArrayField" : [
        {
            "id" : 0,
            "other" : 235
        }
    ]
}

推荐阅读