如何最有效地更新MongoDB中的大量文档?

时间:2015-11-08 13:18:10

标签: javascript node.js mongodb

我希望最有效地更新大量(> 100,000)的文档。

我的第一个天真的方法是在JS级别上编写脚本 首先获取_ids,然后遍历_ids并通过_id调用更新(完整 docs或$ set patches)。

我遇到了内存问题,还将数据分成最大块。 500 文档(打开和关闭连接)似乎不能正常工作。

那么如何在MongoDB级别解决这个问题呢? 最佳做法?

我有3个常见用例,通常是维护工作流程:

1。更改属性值的类型,而不更改值。

// before
{
  timestamp : '1446987395'
}

// after
{
  timestamp : 1446987395
}

2。根据现有属性的值添加新属性。

// before
{
  firstname : 'John',
  lastname  : 'Doe'
}

// after
{
  firstname : 'John',
  lastname  : 'Doe',
  name      : 'John Doe'
}

第3。只需添加从文档中删除属性。

// before
{
  street    : 'Whatever Ave',
  street_no : '1025'
}

// after
{
  street    : 'Whatever Ave',
  no        : '1025'
}

感谢您提供帮助。

1 个答案:

答案 0 :(得分:7)

如果您的MongoDB服务器是2.6或更高版本,最好利用允许执行批量 {{3}的写入命令 Bulk API 操作只是服务器顶部的抽象操作,可以轻松构建批量操作。这些批量操作主要有两种形式:

  • 订购批量操作。这些操作按顺序执行所有操作,并在第一次写入错误时执行错误。
  • 无序批量操作。这些操作并行执行所有操作并聚合所有错误。无序批量操作不保证执行顺序。

注意,对于比2.6更旧的服务器,API将下转换操作。但是,不可能将100%下变频,因此可能存在一些无法正确报告正确数字的边缘情况。

对于您的三个常见用例,您可以像这样实现批量API:

案例1。更改属性值的类型,而不更改值:

var MongoClient = require('mongodb').MongoClient;

MongoClient.connect("mongodb://localhost:27017/test", function(err, db) {
    // Handle error
    if(err) throw err;

    // Get the collection and bulk api artefacts
    var col = db.collection('users'),           
        bulk = col.initializeOrderedBulkOp(), // Initialize the Ordered Batch
        counter = 0;        

    // Case 1. Change type of value of property, without changing the value.        
    col.find({"timestamp": {"$exists": true, "$type": 2} }).each(function (err, doc) {

        var newTimestamp = parseInt(doc.timestamp);
        bulk.find({ "_id": doc._id }).updateOne({
            "$set": { "timestamp": newTimestamp }
        });

        counter++;

        if (counter % 1000 == 0 ) {
            bulk.execute(function(err, result) {  
                // re-initialise batch operation           
                bulk = col.initializeOrderedBulkOp();
            });
        }
    });

    if (counter % 1000 != 0 ){
        bulk.execute(function(err, result) {
            // do something with result
            db.close();
        }); 
    } 
});

案例2。根据现有属性的值添加新属性:

MongoClient.connect("mongodb://localhost:27017/test", function(err, db) {
    // Handle error
    if(err) throw err;

    // Get the collection and bulk api artefacts
    var col = db.collection('users'),           
        bulk = col.initializeOrderedBulkOp(), // Initialize the Ordered Batch
        counter = 0;        

    // Case 2. Add new property based on value of existing property.        
    col.find({"name": {"$exists": false } }).each(function (err, doc) {

        var fullName = doc.firstname + " " doc.lastname;
        bulk.find({ "_id": doc._id }).updateOne({
            "$set": { "name": fullName }
        });

        counter++;

        if (counter % 1000 == 0 ) {
            bulk.execute(function(err, result) {  
                // re-initialise batch operation           
                bulk = col.initializeOrderedBulkOp();
            });
        }
    });

    if (counter % 1000 != 0 ){
        bulk.execute(function(err, result) {
            // do something with result
            db.close();
        }); 
    } 
});

案例3。只需在文档中添加删除属性。

MongoClient.connect("mongodb://localhost:27017/test", function(err, db) {
    // Handle error
    if(err) throw err;

    // Get the collection and bulk api artefacts
    var col = db.collection('users'),           
        bulk = col.initializeOrderedBulkOp(), // Initialize the Ordered Batch
        counter = 0;        

    // Case 3. Simply adding removing properties from documents.    
    col.find({"street_no": {"$exists": true } }).each(function (err, doc) {

        bulk.find({ "_id": doc._id }).updateOne({
            "$set": { "no": doc.street_no },
            "$unset": { "street_no": "" }
        });

        counter++;

        if (counter % 1000 == 0 ) {
            bulk.execute(function(err, result) {  
                // re-initialise batch operation           
                bulk = col.initializeOrderedBulkOp();
            });
        }
    });

    if (counter % 1000 != 0 ){
        bulk.execute(function(err, result) {
            // do something with result
            db.close();
        }); 
    } 
});