说我有这个型号
{
_id : 1,
ref: '1',
children: [
{
ref:'1.1',
grandchildren: [
{
ref:'1.1.1',
visible: true;
}
]
}
]
}
我知道嵌套数组的位置运算符尚不可用。
https://jira.mongodb.org/browse/SERVER-831
但是想知道是否有可能以原子方式更新嵌套数组中的文档?
在我的示例中,我想将参考1.1.1的文档的visible
标志更新为false。
我让孩子们记录参考==' 1.1'和孙子参考==' 1.1.1'
感谢
答案 0 :(得分:3)
是的,只有当您知道具有要事先更新的孙子对象的子数组的索引并且更新查询将使用位置运算符时,才可能这样做:
db.collection.update(
{
"children.ref": "1.1",
"children.grandchildren.ref": "1.1.1"
},
{
"$set": {
"children.0.grandchildren.$.visible": false
}
}
)
但是,如果您事先不知道数组索引位置,则应考虑使用 MapReduce 动态创建 $set
条件STRONG>。 MapReduce的基本思想是它使用JavaScript作为查询语言,但这往往比聚合框架慢得多,不建议用于实时数据分析。
在MapReduce操作中,您需要定义几个步骤,即映射步骤(将操作映射到集合中的每个文档,操作可以不执行任何操作或使用键和投影值发出一些对象)和减少步骤(采用发射值列表并将其减少为单个元素)。
对于地图步骤,理想情况下,您希望获取集合中的每个文档,每个children
数组字段的索引以及包含$set
键的另一个键。
您的简化步骤将是一个函数(无效),简单定义为var reduce = function() {};
然后,MapReduce操作的最后一步将创建一个单独的集合操作,其中包含已发出的操作数组对象以及具有$set
条件的字段。在原始集合上运行MapReduce操作时,可以定期更新此集合。
总而言之,这个MapReduce方法看起来像:
var map = function(){
for(var i = 0; i < this.children.length; i++){
emit(
{
"_id": this._id,
"index": i
},
{
"index": i,
"children": this.children[i],
"update": {
"ref": "children." + i.toString() + ".grandchildren.$.ref",
"visible": "children." + i.toString() + ".grandchildren.$.visible"
}
}
);
}
};
var reduce = function(){};
db.collection.mapReduce(
map,
reduce,
{
"out": {
"replace": "update_collection"
}
}
);
然后,您可以使用db.update_collection.find()
方法中的光标迭代并相应地更新您的集合:
var cur = db.update_collection.find(
{
"value.children.ref": "1.1",
"value.children.grandchildren.ref": "1.1.1"
}
);
// Iterate through results and update using the update query object set dynamically by using the array-index syntax.
while (cur.hasNext()) {
var doc = cur.next();
var update = { "$set": {} };
// set the update query object
update["$set"][doc.value.update.visible] = false;
db.collection.update(
{
"children.ref": "1.1",
"children.grandchildren.ref": "1.1.1"
},
update
);
};