我有一个Mongo文档,其中包含一个元素数组。
我想重置.profile = XX数组中所有对象的.handled属性。
文件格式如下:
{
"_id": ObjectId("4d2d8deff4e6c1d71fc29a07"),
"user_id": "714638ba-2e08-2168-2b99-00002f3d43c0",
"events": [{
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 20,
"data": "....."
}
...
]
}
所以,我尝试了以下方法:
.update({"events.profile":10},{$set:{"events.$.handled":0}},false,true)
但是,它只更新每个文档中第一个匹配的数组元素。(这是$ -位置操作符的定义行为。)
如何更新所有匹配的数组元素?
首先:您的代码无法工作,因为您使用了位置操作符$,该操作符仅标识数组中要更新的元素,但甚至没有显式地指定其在数组中的位置。
您需要的是筛选位置操作符$[<identifier>]。它将更新所有匹配数组筛选条件的元素。
解决方案:
db.collection.update({"events.profile":10}, { $set: { "events.$[elem].handled" : 0 } },
{
multi: true,
arrayFilters: [ { "elem.profile": 10 } ]
})
点击这里访问mongodb doc
代码的作用:
{"events.profile":10} filters your collection and return the documents matching the filter
The $set update operator: modifies matching fields of documents it acts on.
{multi:true} It makes .update() modifies all documents matching the filter hence behaving like updateMany()
{ "events.$[elem].handled" : 0 } and arrayFilters: [ { "elem.profile": 10 } ]
This technique involves the use of the filtered positional array with arrayFilters. the filtered positional array here $[elem] acts as a placeholder for all elements in the array fields that match the conditions specified in the array filter.
数组的过滤器
您可以更新MongoDB中的所有元素
db.collectioname.updateOne(
{ "key": /vikas/i },
{ $set: {
"arr.$[].status" : "completed"
} }
)
它将更新“arr”数组中所有的“status”值为“completed”
如果只有一份文件
db.collectioname.updateOne(
{ key:"someunique", "arr.key": "myuniq" },
{ $set: {
"arr.$.status" : "completed",
"arr.$.msgs": {
"result" : ""
}
} }
)
但如果不是一个,而且你也不希望数组中的所有文档都更新,那么你需要遍历元素和if块内部
db.collectioname.find({findCriteria })
.forEach(function (doc) {
doc.arr.forEach(function (singlearr) {
if (singlearr check) {
singlearr.handled =0
}
});
db.collection.save(doc);
});
这也可以用while循环来完成,该循环检查是否有任何文档仍然有未更新的子文档。这种方法保留了更新的原子性(这里的许多其他解决方案都没有做到这一点)。
var query = {
events: {
$elemMatch: {
profile: 10,
handled: { $ne: 0 }
}
}
};
while (db.yourCollection.find(query).count() > 0) {
db.yourCollection.update(
query,
{ $set: { "events.$.handled": 0 } },
{ multi: true }
);
}
循环执行的次数将等于profile为10且处理不等于0的子文档在集合中的任何文档中出现的最大次数。因此,如果您的集合中有100个文档,其中一个文档有三个匹配查询的子文档,而所有其他文档的匹配子文档更少,则循环将执行三次。
此方法避免了破坏其他数据的危险,这些数据可能在此脚本执行时由另一个进程更新。它还最大限度地减少了客户端和服务器之间传输的数据量。