在嵌套数组中添加新项

时间:2017-01-05 06:51:17

标签: mongodb mongodb-query

我需要通过Mongo控制台更新嵌入的Fields数组。我需要在所有Fields子文档中添加一个新项目“Test13”,默认值为“1”。

以下是名为testmarket的我的收藏品样本:

{ 
    "_id" : ObjectId("573c9801056848fef667bfde"), 
    "MST" : "RR", 
    "GST" : null, 
    "Fields" : [{
        "Test1" : "boolean", 
        "Test2" : "TestLot", 
        "Test3" : "TestLot", 
        "Test4" : null, 
        "Test5" : true, 
        "Test6" : true, 
        "Test7" : NumberInt(1), 
        "Test8" : false, 
        "Test9" : false, 
        "Test10" : false, 
        "Test11" : false, 
        "Test12" : null
     }, {
        "Test1" : "String", 
        "Test2" : "TestSerial", 
        "Test3" : "TestSerial", 
        "Test4" : null, 
        "Test5" : true, 
        "Test6" : true, 
        "Test7" : NumberInt(1), 
        "Test8" : false, 
        "Test9" : false, 
        "Test10" : false, 
        "Test11" : false, 
        "Test12" : null
    }]
}

集合非常大,我需要在Fields集合中添加一个新项目。

我尝试过运行查询

db.testmarket.Fields.update(
    {}, 
    {$set : {"Test13":"1"}}, 
    {upsert:true, multi:true}
) 

4 个答案:

答案 0 :(得分:1)

以下是答案:

{
    "_id": "586ded3fdfc5f92724491f82",
    "doctor_name": "asd asd",
    "doctor_dept": "Cardiologist",
    "prescription": [
        {
            "_id": "586dfdbe98c23d1a200cfb3f",
            "name": "ALPHACAINE N, solution injectable à usage dentaire",
            "count": "1",
            "type": "0",
            "consume": "0",
            "comment": "test"
        },
        {
            "_id": "586dfda498c23d1a200cfb3b",
            "name": "ALPHACAINE N, solution injectable à usage dentaire",
            "count": "1",
            "type": "0",
            "consume": "0",
            "comment": "test"
        }
    ]
}

由于

答案 1 :(得分:1)

您基本上需要获取集合中所有Fields数组的集合,迭代集合,并为每个文档迭代Fields子集。对于每个Fields元素,使用$ positional operator更新集合。

例如,在内部循环中(因为你有2个嵌套循环),你对第一个Fields数组元素的更新将是

db.testmarket.updateOne(
    {
        "_id" : ObjectId("573c9801056848fef667bfde"),
        "Fields.Test1" : "boolean",
        "Fields.Test2" : "TestLot",
        "Fields.Test3" : "TestLot",
        "Fields.Test4" : null,
        "Fields.Test5" : true,
        "Fields.Test6" : true,
        "Fields.Test7" : 1,
        "Fields.Test8" : false,
        "Fields.Test9" : false,
        "Fields.Test10" : false,
        "Fields.Test11" : false,
        "Fields.Test12" : null
    },
    { "$set": { "Fields.$.Test13": "1" } }
);

并且迭代中的下一次更新将是:

db.testmarket.updateOne(
    {
        "_id" : ObjectId("573c9801056848fef667bfde"),
        "Fields.Test1" : "String",
        "Fields.Test2" : "TestSerial",
        "Fields.Test3" : "TestSerial",
        "Fields.Test4" : null,
        "Fields.Test5" : true,
        "Fields.Test6" : true,
        "Fields.Test7" : 1,
        "Fields.Test8" : false,
        "Fields.Test9" : false,
        "Fields.Test10" : false,
        "Fields.Test11" : false,
        "Fields.Test12" : null
    },
    { "$set": { "Fields.$.Test13": "1" } }
);

等等。

该算法可以实现如下:

db.testmarket.find().snapshot().forEach(function(doc) {
    doc.Fields.forEach(function(field) {
        var query = {},
            obj = { Fields: field };

        /* function to convert an object into dot notation */
        (function recurse(obj, current) {
            for(var key in obj) {
                var value = obj[key];
                var newKey = (current ? current + "." + key : key);  /* joined key with dot */
                if(value && typeof value === "object") {
                    recurse(value, newKey);  // it's a nested object, so do it again
                } else {
                    query[newKey] = value;  // it's not an object, so set the property
                }
            }
        })(obj);

        query["_id"] = doc._id;

        /* do the update */
        db.testmarket.updateOne(
            query,
            { "$set": { "Fields.$.Test13": "1" } }
        );
    });
});

从上面可以看出,由于你有双嵌套for循环,性能肯定会受到影响。第一次迭代的外部循环,即迭代整个集合,执行n次,其中n是集合中文档的总数。

对于外部循环的每次迭代,内部循环执行i次,其中iFields数组的长度,因此整体复杂度可以按如下方式计算:一个用于第一次迭代加上两个用于第二次迭代加上三次用于第三次迭代,依此类推,加上n用于第n次迭代。

1+2+3+4+5+...+n = (n*(n-1))/2 --> O(n^2)

对于非常大的集合,在O(n^2)复杂度的嵌套循环中执行更新效率不高。在这方面,您可以利用允许您发送的Bulk API来优化您的代码 作为简化批处理更新,即不是每次迭代都将每个更新请求发送到服务器,您可以将更新操作批量处理为更快,更高效的单个请求。以下显示了如何使用 bulkWrite() 方法来利用更新。

对于 MongoDB版本3.0 及以下:

var bulk = db.testmarket.initializeUnOrderedBulkOp(),
    counter = 0;

db.testmarket.find().snapshot().forEach(function(doc) {
    doc.Fields.forEach(function(field) {
        var query = {},
            obj = { Fields: field };

        /* function to convert an object into dot notation */
        (function recurse(obj, current) {
            for(var key in obj) {
                var value = obj[key];
                var newKey = (current ? current + "." + key : key);  /* joined key with dot */
                if(value && typeof value === "object") {
                    recurse(value, newKey);  // it's a nested object, so do it again
                } else {
                    query[newKey] = value;  // it's not an object, so set the property
                }
            }
        })(obj);

        query["_id"] = doc._id;

        /* load up update operations */
        bulk.find(query).updateOne({ "$set": { "Fields.$.Test13": "1" } });

        counter++;

        /* execute the update operations at once in bulk */
        if (counter % 500 === 0 ) {
            bulk.execute();
            bulk = db.testmarket.initializeUnOrderedBulkOp();
        }
    });
});

/* clean up the remaining update operations left in the queue */
if (counter % 500 !== 0 )
    bulk.execute();

MongoDB 3.2或更新

var ops = [];

 db.testmarket.find().snapshot().forEach(function(doc) {
    doc.Fields.forEach(function(field) {
        var query = {},
            obj = { Fields: field };

        /* function to convert an object into dot notation */
        (function recurse(obj, current) {
            for(var key in obj) {
                var value = obj[key];
                var newKey = (current ? current + "." + key : key);  /* joined key with dot */
                if(value && typeof value === "object") {
                    recurse(value, newKey);  // it's a nested object, so do it again
                } else {
                    query[newKey] = value;  // it's not an object, so set the property
                }
            }
        })(obj);

        query["_id"] = doc._id;

        /* load up update operations */
        ops.push({
            "updateOne": {
                "filter": query,
                "update": { "$set": { "Fields.$.Test13": "1" } }
            }
        });
        counter++;      
    });

    if (counter % 500 === 0) {
        db.testmarket.bulkWrite(ops);
        ops = [];
    }
});


if (counter % 500 !== 0) 
    db.testmarket.bulkWrite(ops);

如果您的收藏很大,上面的计数器变量可以有效地管理您的批量更新。它允许您批量更新操作,并以500的批量将写入发送到服务器,这样可以提供更好的性能,因为您不会向服务器发送每个请求,每500个请求中只发送一次。

对于批量操作,MongoDB对每个批处理施加了1000个操作的默认内部限制,因此在对批量大小有一定控制而不是让MongoDB强加默认值的情况下,选择500个文档是好的,即对于大型操作在>的大小> 1000份文件。

答案 2 :(得分:0)

db.getCollection(' testmarket&#39)更新({" _id":的ObjectId(573c9801056848fef667bfde)},{' $ addToSet' {&#39 ;字段' {" Test13":1}}}); 但我认为你应该给你的jsons里面的一些名字作为上面的代码,只需在所有的jsons之外插入一个json {Test13:1}。如果您要改变您的结构,请输入以下字段:[{" firstTest":{" Test1":" boolean",......},{& #34; secondTest":{" Test1":" boolean",......}}}]然后上面的代码可以重写为 - >

db.getCollection(' testmarket&#39)更新({" _id":的ObjectId(573c9801056848fef667bfde)},{' $ addToSet' {&#39 ; Fields.firstTest' {" Test13":1}}});

答案 3 :(得分:0)

db.testmarket.find({}).forEach(function(x){for(var i in x.Fields){x.Fields[i]['Test13']=1} db.testmarket.save(x);});

我认为你无法通过更新完成,更新调用最多会更新文档中的任何单个项目,尽管它会对所有文档执行多个:true但不是所有文档的所有项目都在文件。

如果您的集合太大,您可以使用limit和skip分批调用上面的方法。