在多个文档中更新MongoDB / Mongoose中的数组中的值

时间:2016-10-11 09:22:31

标签: node.js mongodb mongoose

我有一个包含嵌套数组中对象的MongoDB集合。我希望能够循环遍历每个文档并使用特定引用更新阵列中的任何对象。例如:

{
    level: 3
    framework: 'England',
    units: [{
        number: 3.01,
        name: 'Introduction',
        fileName: '3.01.pdf' 
    }]
},

{
    level: 3,
    framework: 'Scotland',
    units: [
    {
        number: 3.01,
        name: 'Introduction',
        fileName: '3.01.pdf' 
    },
    {
        number: 3.03,
        name: 'Intermediate',
        fileName: '3.03.pdf'
    }]
},
{
    level: 4
    framework: 'England',
    units: [{
        number: 4.01,
        name: 'Higher Introduction',
        fileName: '4.01.pdf' 
    }]
}

如何使用units number更新任何3.01数组中的任何项目的fileName。到目前为止,我设法找到的所有内容都只是关于如何更新单个文档,而不是所有文档中的所有匹配条目。

谢谢!

1 个答案:

答案 0 :(得分:0)

更新数组元素字段的关键是创建一个包含匹配文档的单独数据集,循环显示它们并使用 Bulk API 更新您的集合,这样可以发送许多更新单个请求中的操作(作为批处理)。

创建单独数据集的最佳选择是使用聚合框架,您可以聚合集合中的所有文档以返回上述数据集进行处理。

让我们看一下这支钢笔的实例:

a)对于MongoDB服务器版本3.2及以上

var operations = [];
db.collection.aggregate([
    { "$match": { "units.number": 3.01 } },
    { 
        "$project": {
            "units": {
                "$filter": {
                    "input": "$units",
                    "as": "unit",
                    "cond": { "$eq": [ "$$unit.number", 3.01 ] }
                }
            }
        }
    }
]).forEach(function(doc){
    doc.units.forEach(function(unit){
        operations.push({
            "updateOne": {
                "filter": { 
                    "_id": doc._id,
                    "units.fileName": unit.fileName
                },
                "update": {
                    "$set": { "units.$.fileName": "updated_3.01.pdf" }
                }
            }
        });

        // Send once in 500 requests only
        if (operations.length % 1000 === 0 ) {
            db.collection.bulkWrite(operations);
            operations = [];
        }
    });
})

// Clear remaining queue
if (operations.length > 0 )
    db.collection.bulkWrite(operations);

在上文中,您初始化了您的操作数组,该数组将由Bulk API的 bulkWrite() 函数使用并保存更新操作。前面的 aggregate() 操作将充当需要更新的文档的过滤器,并允许您为要更新的数组元素创建唯一标识符,这样可以更轻松地使用<更新中的strong> $ positional operator

然后迭代聚合操作的结果以使用更新对象创建操作数组。这些行动仅限于 批量为500.选择低于默认批次限制1000的值的原因通常是受控制的选择。正如那里的文档所述,默认情况下MongoDB将发送到s erver in batches of 1000 operations at a time at maximum并且无法保证确保这些默认的1000个操作请求实际适合16MB BSON limit。因此,您仍然需要处于“安全”状态并强制实施较低的批量大小,只能有效管理,以便在发送到服务器时总数小于数据限制。

a)如果使用MongoDB v3.0或更低版本:

var bulk = db.collection.initializeOrderedBulkOp(),
    counter = 0;

db.collection.aggregate([
    { "$match": { "units.number": 3.01 } },
    { 
        "$project": {
            "units": {
                "$setDifference": [
                   { 
                       "$map": {
                           "input": "$units",
                           "as": "unit",
                           "in": {
                               "$cond": [
                                   { "$eq": [ "$$unit.number", 3.01 ] },
                                   "$$unit",
                                   false
                               ]
                           }
                       }
                    },
                    [false]
                ]
            }
        }
    }
]).forEach(function(doc) {
    doc.units.forEach(function(unit) {
        bulk.find({ 
            "_id": doc._id, 
            "units.fileName": unit.fileName  
        }).updateOne({
            "$set": { "units.$.fileName": "updated_3.01.pdf" }
        });
        counter++;

        if (counter % 500 === 0) {
            bulk.execute();
            bulk = db.collection.initializeOrderedBulkOp();
        }
    });
});

if (counter % 500 !== 0)
    bulk.execute();