我有以下文件:
[{
"_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 查询同时更新两个文档。您始终必须在两个查询中执行此操作。您当然可以将字段的值设置为相同的值,或以相同的数字递增,但您不能使用相同的查询在 MongoDB 中执行两次不同的更新。
从 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}]
自 MongoDB 4.2 起
update
可以接受聚合管道作为第二个参数,允许根据数据修改多个文档。
文档摘录:
使用文档中其他字段的值修改字段
使用以下文档创建成员集合:
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 } )
假设更新您的位置后,您的数组将如下所示
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以获得更好的性能。
正如作者所提到的,MongoDB 现在支持更新操作中的聚合管道,例如 updateOne 和 updateMany。这使我们能够修改一组文档,每个文档具有不同的值。
需要在选项中设置multi: true。
@Shubham的答案是正确的并且适用于作者的情况,但它可能效率低下,因为它使用arrayElementAt和filter。这是因为,对于 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给出的答案指出不可能更新具有不同值的多个文档是不正确的,应该进行编辑,或者我们应该努力降低其相关性,因为它可能会导致误解。