用不同值更新多个文档的方法

问题描述 投票:0回答:6

我有以下文件:

[{
  "_id":1,
  "name":"john",
  "position":1
},
 {"_id":2,
  "name":"bob",
  "position":2
},
 {"_id":3,
  "name":"tom",
  "position":3
}]

在 UI 中,用户可以更改项目的位置(例如,将 Bob 移动到第一个位置,john 移动到位置 2,tom 移动到位置 3)。 有没有办法一次性更新所有文档中的所有位置?

mongodb pymongo nosql
6个回答
34
投票

您无法使用 MongoDB 查询同时更新两个文档。您始终必须在两个查询中执行此操作。您当然可以将字段的值设置为相同的值,或以相同的数字递增,但您不能使用相同的查询在 MongoDB 中执行两次不同的更新。


8
投票

您可以使用

db.collection.bulkWrite()
批量执行多个操作。自
3.2
以来一直可用。

可以无序执行操作以提高性能。


8
投票

从 mongodb 4.2 开始,您可以使用 $set 运算符在更新中使用管道

由于聚合管道中有许多运算符,现在有很多可能的方法,尽管我提供了其中一种

    exports.updateDisplayOrder = async keyValPairArr => {
    try {
        let data = await ContestModel.collection.update(
            { _id: { $in: keyValPairArr.map(o => o.id) } },
            [{
                $set: {
                    displayOrder: {
                        $let: {
                            vars: { obj: { $arrayElemAt: [{ $filter: { input: keyValPairArr, as: "kvpa", cond: { $eq: ["$$kvpa.id", "$_id"] } } }, 0] } },
                            in:"$$obj.displayOrder"
                        
                        }
                    }
                }
            }],
            { runValidators: true, multi: true }
        )

        return data;
    } catch (error) {
        throw error;
    }
   }

示例键值对是:

[{"id":"5e7643d436963c21f14582ee","displayOrder":9}, {"id":"5e7643e736963c21f14582ef","displayOrder":4}]


1
投票

自 MongoDB 4.2 起

update
可以接受聚合管道作为第二个参数,允许根据数据修改多个文档。

参见 https://docs.mongodb.com/manual/reference/method/db.collection.update/#modify-a-field-using-the-values-of-the-other-fields-in-the-文件

文档摘录:

使用文档中其他字段的值修改字段

使用以下文档创建成员集合:

db.members.insertMany([
  { "_id" : 1, "member" : "abc123", "status" : "A", "points" : 2, "misc1" : "note to self: confirm status", "misc2" : "Need to activate", "lastUpdate" : ISODate("2019-01-01T00:00:00Z") },
  { "_id" : 2, "member" : "xyz123", "status" : "A", "points" : 60, "misc1" : "reminder: ping me at 100pts", "misc2" : "Some random comment", "lastUpdate" : ISODate("2019-01-01T00:00:00Z") }
])

假设您希望将这些字段收集到一个新的注释字段中,而不是单独的 Misc1 和 Misc2 字段。以下更新操作使用聚合管道来:

  • 添加新的comments字段并设置lastUpdate字段。
  • 删除集合中所有文档的misc1和misc2字段。
db.members.update(
  { },
  [
     { $set: { status: "Modified", comments: [ "$misc1", "$misc2" ], lastUpdate: "$$NOW" } },
     { $unset: [ "misc1", "misc2" ] }
  ],
  { multi: true }
)

1
投票

假设更新您的位置后,您的数组将如下所示

const objectToUpdate = [{
    "_id":1,
    "name":"john",
    "position":2
  },
  {
    "_id":2,
    "name":"bob",
    "position":1
  },
  {
    "_id":3,
    "name":"tom",
    "position":3
  }].map( eachObj => {
    return {
        updateOne: {
            filter: { _id: eachObj._id },
            update: { name: eachObj.name, position: eachObj.position }
        }
    }
})
YourModelName.bulkWrite(objectToUpdate,
    { ordered: false }
    ).then((result) => {
         console.log(result);
    }).catch(err=>{
         console.log(err.result.result.writeErrors[0].err.op.q);
    })

它将用不同的值更新所有位置。

注意:我在这里使用了ordered:false以获得更好的性能。


0
投票

正如作者所提到的,MongoDB 现在支持更新操作中的聚合管道,例如 updateOne 和 updateMany。这使我们能够修改一组文档,每个文档具有不同的值。

更新中的聚合管道

需要在选项中设置multi: true。

@Shubham的答案是正确的并且适用于作者的情况,但它可能效率低下,因为它使用arrayElementAtfilter。这是因为,对于 MongoDB 迭代的每个文档,它将过滤相同的列表以匹配值。

解决方案是使用哈希图,因此我们可以利用getField并使聚合更简单、更高效,并解决作者的常见问题。

HashMap<ID, newPosition>
let user_position_hash = {
  2: 1,
  1: 2,
}

聚合

let agg = {
    "$set": {
        "position": {
            "$let": {
                "vars": {
                    "new_position": {
                        "$getField": {
                            "field": {
                                "$toString": "$_id"
                            },
                            "input": user_position_hash
                        }
                    }
                },
                "in": "$$new_position"
            }
        }
    }
};

注意@Derick给出的答案指出不可能更新具有不同值的多个文档是不正确的,应该进行编辑,或者我们应该努力降低其相关性,因为它可能会导致误解。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.