我有一些文件,如
doc:{
"_id":6,
item1:"something"
item2:[
{
subitem1:value1,
subitem2:value2
},
{
subitem1:value3,
subitem2:value4
}
]
}
我想插入一个包含另外两个数据的字段,然后删除它们以获得此
doc:{
"_id":6,
item1:"something"
item2:[
{
subitem:{field:value1,field2:value2}
},
{
subitem:{field:value3,field2:value4}
}
]
}
我必须使用1个脚本更新集合的所有文档。 我已经尝试了几个东西,比如$ set,$ push但没有任何效果(执行时没有错误)
我的上一个剧本是
db.docs.find({}).update.forEach(
function(doc){
doc.item2.forEach(
function(item){
{ $set : {item.subitem = {field:item.subitem1,field2:item.subitem2}}}
}
)
db.docs.save(doc);
}
,false,true)
这不会产生错误但不会执行任何操作。 我甚至没有找到如何删除字段。 请帮帮我!
答案 0 :(得分:1)
您应该使用.bulkWrite()
进行循环以提交更新。这里要注意的主要是你实际上正在迭代的东西,它是集合项以及要转换的目标数组的成员。
要么用整个阵列替换它,要么替换它:
var ops = [];
db.docs.find({
"item2": {
"$elemMatch": {
"subitem1": { "$exists": true },
"subitem2": { "$exists": true }
}
}
}).forEach(function(doc) {
doc.item2 = doc.item2.map(function(el) {
return { "subitem": { "field1": el.subitem1, "field2": el.subitem2 } };
});
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "item2": doc.item2 } }
}
});
// Test outside array looping
if ( ops.length == 1000 ) {
db.docs.bulkWrite(ops);
ops = []
}
});
if ( ops.length > 0 )
db.docs.bulkWrite(ops);
或者最好使用positional matches作为更新:
var ops = [];
db.docs.find({
"item2": {
"$elemMatch": {
"subitem1": { "$exists": true },
"subitem2": { "$exists": true }
}
}
}).forEach(function(doc) {
doc.item2.forEach(function(item) {
var updoc = { "subitem": { "field1": item.subitem1, "field2": item.subitem2 } };
ops.push({
"updateOne": {
"filter": {
"_id": doc._id,
"item2": {
"$elemMatch": {
"subitem1": item.subitem1,
"subitem2": item.subitem2
}
}
},
"update": { "$set": { "item2.$": updoc } }
}
});
// Test inside the array looping
if ( ops.length == 1000 ) {
db.docs.bulkWrite(ops);
ops = []
}
});
});
if ( ops.length > 0 )
db.docs.bulkWrite(ops);
后一种情况更好的原因是每个元素的写入实际上是原子的,因此在高容量环境中,您不会从其他进程获得冲突的写入。
这是转换当前数组内容的快速而安全的方法。第一种方式会运行得更快,但我真的不会在实时系统上推荐它。第二个仍然会非常快,但由于它在操作中一次更新一个数组元素,因此还有更多工作要做。
在这两种情况下,与服务器的实际“有线通信”仅发生在千分之一的操作中,因此这消除了发送请求和等待每次更新响应的开销。