Обновление нескольких документов с использованием массива объектов

Я использую сервер express.js. Я пытаюсь обновить существующие документы в коллекции mongodb используя mongoose с помощью массива объектов (каждый объект имеет свойство _id, и его значение соответствует каждому документу _id.)

Один из подходов - цикл через массив и выполнение findByIdAndUpdate()

for(var i=0; i < expenseListToEdit.length; i++) {    
    var expense = expenseListToEdit[i];

    Expense.findByIdAndUpdate(expense._id, expense, function(err, model) {
        if (err) {    
            console.log('Error occurred while editing expense');
        }

        console.log('model: ' + util.inspect(model));
    });
}

Но таким образом мне придется обрабатывать асинхронный сценарий и проверять, когда запрос БД для всей итерации завершен, а затем отправлять ответ обратно клиенту с сервера.

Есть ли какой-либо альтернативный подход в мангусте для редактирования/изменения массива объектов за один раз, а затем вызвать обратный вызов?

Примечание*- В каждом объекте массива присутствует _id, который соответствует значению документа _id.


person Abhijeet Srivastava    schedule 16.12.2016    source источник


Ответы (3)


Да, это вполне возможно. Вы можете воспользоваться преимуществами использования API массовой записи для обработки асинхронных операций и, следовательно, повышения производительности, особенно при работе с большими наборами данных. Для версий Mongoose >=4.3.0, которые поддерживают MongoDB Server 3.2.x, вы можете использовать bulkWrite() для обновлений. В следующем примере показано, как вы можете это сделать:

var bulkUpdateCallback = function(err, r){
    console.log(r.matchedCount);
    console.log(r.modifiedCount);
}
// Initialise the bulk operations array
var bulkOps = expenseListToEdit.map(function (expense) { 
    return { 
        "updateOne": { 
            "filter": { "_id": expense._id } ,              
            "update": { "$set": expense } 
        }         
    }    
});

// Get the underlying collection via the native node.js driver collection object
Expense.collection.bulkWrite(bulkOps, { "ordered": true, w: 1 }, bulkUpdateCallback);

Для версий Mongoose ~3.8.8, ~3.8.22, 4.x, которые поддерживают MongoDB Server >=2.6.x, вы можете использовать Bulk API следующим образом

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

expenseListToEdit.forEach(function(expense) {
    bulk.find({ "_id": expense._id })
        .updateOne({ "$set": expense });

    counter++;
    if (counter % 500 == 0) {
        bulk.execute(function(err, r) {
           // do something with the result
           bulk = Expense.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
    });
}
person chridam    schedule 16.12.2016

Решение состоит в том, чтобы изменить оператор for на async.each http://caolan.github.io/async/docs.html#each :

Предупреждение: каждый запускает все элементы параллельно.

async.each(expenseListToEdit, function (expense, done) {
    Expense.findByIdAndUpdate(expense._id, expense, function(err, model) {
        if (err) {    
           console.log('Error occurred while editing expense');
           done(err); // if an error occured, all each is stoped
           return;
        }

        console.log('model: ' + util.inspect(model));
        done();
    });
}, function (err) {
    // final callback launched after all findByIdAndUpdate calls.
});
person throrin19    schedule 16.12.2016

person    schedule
comment
пожалуйста, попробуйте добавить объяснение того, как работает этот код и почему, возможно, со ссылками на документацию. - person dheiberg; 25.07.2017