使用Mongoose更新MongoDB中的许多记录的正确方法是什么

时间:2016-08-03 11:37:05

标签: node.js mongodb mongoose

我使用Mongoose从MongoDB中提取一些记录,将它们导入另一个系统,然后我想将所有这些文档的状态(文档属性)设置为processed

我可以找到这个解决方案:Update multiple documents by id set. Mongoose

我想知道这是否是正确的方法,建立一个由所有文档ID组成的标准,然后执行更新。还请考虑一个事实,即它会有很多文件。

(更新查询的限制是什么?无法在任何地方找到它。官方文档:http://mongoosejs.com/docs/2.7.x/docs/updating-documents.html

2 个答案:

答案 0 :(得分:20)

构建包含所有文档ID的标准然后执行更新的方法必然会引发潜在问题。当您迭代发送每个文档的更新操作的文档列表时,在Mongoose中,您冒着炸毁服务器的风险,特别是在处理大型数据集时,因为您没有等待异步调用完成,然后再转到下一个迭代。你将基本上建立一个"堆栈"未解决的操作直到导致问题 - Stackoverflow。

例如,假设您有一组文档ID,您希望在状态字段上更新匹配的文档:

var processedIds = [
    "57a0a96bd1c6ef24376477cd",
    "57a052242acf5a06d4996537",
    "57a052242acf5a06d4996538"
];

然后对于非常小的数据集,您可以使用数组上的 forEach() 方法迭代它并更新您的集合:

processedIds.forEach(function(id)){
    Model.update({"_id": id}, {"$set": {"status": "processed" }}, callback);
});

以上内容适用于小型数据集。但是,当您面对要更新的数千或数百万个文档时,这将成为一个问题,因为您将在循环内重复调用异步代码的服务器。

另一种方法是使用像async的 eachLimit 这样的东西,并迭代数组,为每个项目执行MongoDB更新操作,而从不执行超过x个并行更新同一时间。

最好的方法是使用批量API,这对于批量处理更新非常有效。性能与在多个文档中的每一个上调用更新操作的差异在于,不是每次迭代都向服务器发送更新请求,而是批量API在每1000个请求中发送一次请求(批处理)。

对于支持MongoDB Server >=4.3.0的Mongoose版本3.2.x,您可以使用 bulkWrite() 进行更新。以下示例说明了如何解决此问题:

var bulkUpdateCallback = function(err, r){
    console.log(r.matchedCount);
    console.log(r.modifiedCount);
}
// Initialise the bulk operations array
var bulkUpdateOps = [],
    counter = 0;

processedIds.forEach(function(id) {
    bulkUpdateOps.push({
        "updateOne": {
            "filter": { "_id": id },
            "update": { "$set": { "status": "processed" } }
        }
    });
    counter++;

    if (counter % 500 == 0) {
        // Get the underlying collection via the native node.js driver collection object
        Model.collection.bulkWrite(bulkOps, { "ordered": true, w: 1 }, bulkUpdateCallback);
        bulkUpdateOps = []; // re-initialize
    }
})

if (counter % 500 != 0) { Model.collection.bulkWrite(bulkOps, { "ordered": true, w: 1 }, bulkUpdateCallback); }

对于支持MongoDB服务器~3.8.8的Mongoose版本~3.8.224.x>=2.6.x,您可以按如下方式使用批量API

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

processedIds.forEach(function(id) {
    bulk.find({ "_id": id }).updateOne({ 
        "$set": { "status": "processed" }
    });

    counter++;
    if (counter % 500 == 0) {
        bulk.execute(function(err, r) {
           // do something with the result
           bulk = Model.collection.initializeOrderedBulkOp();
           counter = 0;
        });
    }
});

// Catch any docs in the queue under or over the 500's
if (counter > 0) {
    bulk.execute(function(err,result) {
       // do something with the result here
    });
}

答案 1 :(得分:6)

您可以在更新查询中使用 {multi: true} 选项进行批量更新。

示例

employees.update({ _id: { $gt: 3 } },{$inc: { sortOrder: -1 }},{'multi':true});

mongoose 中的上述代码等同于 mongodb 中的以下代码:

db.employees.updateMany({ _id: { $gt: 3 } },{$inc: { sortOrder: -1 }});