В настоящий момент я использую save для добавления одного документа. Предположим, у меня есть массив документов, которые я хочу сохранить как отдельные объекты. Есть ли способ добавить их все с помощью одного вызова функции, а затем получить один обратный вызов, когда это будет сделано? Я мог бы добавить все документы отдельно, но управление обратными вызовами для разработки, когда все будет сделано, будет проблематичным.
Как сохранить несколько документов одновременно в Mongoose/Node.js?
Ответ 1
В Mongoose еще не реализованы объемные вставки (см. issue # 723).
Поскольку вы знаете количество документов, которые вы сохраняете, вы можете написать что-то вроде этого:
var total = docArray.length
, result = []
;
function saveAll(){
var doc = docArray.pop();
doc.save(function(err, saved){
if (err) throw err;//handle error
result.push(saved[0]);
if (--total) saveAll();
else // all saved here
})
}
saveAll();
Это, конечно, решение стоп-зазора, и я бы рекомендовал использовать какую-то библиотеку управления потоком (я использую q и это потрясающе).
Ответ 2
Mongoose теперь поддерживает передачу нескольких структур документов в Model.create. Чтобы процитировать их пример API, он поддерживает передачу либо массива, либо varargs-списка объектов с обратным вызовом в конце:
Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
if (err) // ...
});
или
var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
Candy.create(array, function (err, jellybean, snickers) {
if (err) // ...
});
Изменить: Как многие отметили, это не выполняет настоящую массовую вставку - она просто скрывает сложность вызова save
несколько раз самостоятельно. Ниже приведены ответы и комментарии, поясняющие, как использовать фактический драйвер Mongo для достижения объемной вставки в интересах производительности.
Ответ 3
Массовые вставки в Mongoose могут быть выполнены с помощью .insert(), если вам не требуется доступ к промежуточному программному обеспечению.
Model.collection.insert(docs, options, callback)
https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L71-91
Ответ 4
Mongoose 4.4 добавил метод insertMany
Ярлык для проверки массива документов и вставки их в MongoDB, если все они действительны. Эта функция быстрее, чем .create() потому что он отправляет только одну операцию на сервер, а не по одному для каждого документ.
Цитата vkarpov15 из выпуска # 723:
Компромиссы заключаются в том, что insertMany() не запускает предварительные блокировки, но он должен иметь лучшую производительность, поскольку он делает только 1 обратную связь в базу данных, а не 1 для каждого документа.
Подпись метода идентична create
:
Model.insertMany([ ... ], (err, docs) => {
...
})
Или, с promises:
Model.insertMany([ ... ]).then((docs) => {
...
}).catch((err) => {
...
})
Ответ 5
Используйте асинхронную параллель, и ваш код будет выглядеть следующим образом:
async.parallel([obj1.save, obj2.save, obj3.save], callback);
Так как соглашение в Mongoose одинаково, как в async (err, callback), вам не нужно обертывать их в свои собственные обратные вызовы, просто добавляйте свои вызовы сохранения в массив, и вы получите обратный вызов, когда все будет завершено.
Если вы используете mapLimit, вы можете контролировать, сколько документов вы хотите сохранить параллельно. В этом примере мы сохраняем 10 документов в параллельном порядке, пока все элементы не будут успешно сохранены.
async.mapLimit(myArray, 10, function(document, next){
document.save(next);
}, done);
Ответ 6
Я знаю, что это старый вопрос, но меня беспокоит, что здесь нет правильных правильных ответов. Большинство ответов просто говорят об итерации по всем документам и сохраняют каждый из них по отдельности, что представляет собой идею BAD, если у вас есть несколько документов, и процесс повторяется даже для одного запроса.
MongoDB специально имеет вызов batchInsert()
для вставки нескольких документов, и это должно использоваться из собственного драйвера mongodb. Mongoose построен на этом драйвере, и он не поддерживает пакетные вставки. Это, вероятно, имеет смысл, поскольку он должен быть инструментом моделирования документа Object для MongoDB.
Решение: Mongoose поставляется с родным драйвером MongoDB. Вы можете использовать этот драйвер, требуя его require('mongoose/node_modules/mongodb')
(не слишком уверен в этом, но вы всегда можете установить mpmodb npm снова, если он не работает, но я думаю, что он должен), а затем сделать правильный batchInsert
Ответ 7
Вот еще один способ без использования дополнительных библиотек (без проверки ошибок)
function saveAll( callback ){
var count = 0;
docs.forEach(function(doc){
doc.save(function(err){
count++;
if( count == docs.length ){
callback();
}
});
});
}
Ответ 8
Новые версии MongoDB поддерживают массовые операции:
var col = db.collection('people');
var batch = col.initializeUnorderedBulkOp();
batch.insert({name: "John"});
batch.insert({name: "Jane"});
batch.insert({name: "Jason"});
batch.insert({name: "Joanne"});
batch.execute(function(err, result) {
if (err) console.error(err);
console.log('Inserted ' + result.nInserted + ' row(s).');
}
Ответ 9
Вы можете использовать обещание, возвращенное mongoose save
, Promise
, в mongoose не все, но вы можете добавить функцию с этим модулем.
Создайте модуль, который улучшит обещание мангуста со всеми.
var Promise = require("mongoose").Promise;
Promise.all = function(promises) {
var mainPromise = new Promise();
if (promises.length == 0) {
mainPromise.resolve(null, promises);
}
var pending = 0;
promises.forEach(function(p, i) {
pending++;
p.then(function(val) {
promises[i] = val;
if (--pending === 0) {
mainPromise.resolve(null, promises);
}
}, function(err) {
mainPromise.reject(err);
});
});
return mainPromise;
}
module.exports = Promise;
Затем используйте его с mongoose:
var Promise = require('./promise')
...
var tasks = [];
for (var i=0; i < docs.length; i++) {
tasks.push(docs[i].save());
}
Promise.all(tasks)
.then(function(results) {
console.log(results);
}, function (err) {
console.log(err);
})
Ответ 10
Добавьте файл mongoHelper.js
var MongoClient = require('mongodb').MongoClient;
MongoClient.saveAny = function(data, collection, callback)
{
if(data instanceof Array)
{
saveRecords(data,collection, callback);
}
else
{
saveRecord(data,collection, callback);
}
}
function saveRecord(data, collection, callback)
{
collection.save
(
data,
{w:1},
function(err, result)
{
if(err)
throw new Error(err);
callback(result);
}
);
}
function saveRecords(data, collection, callback)
{
save
(
data,
collection,
callback
);
}
function save(data, collection, callback)
{
collection.save
(
data.pop(),
{w:1},
function(err, result)
{
if(err)
{
throw new Error(err);
}
if(data.length > 0)
save(data, collection, callback);
else
callback(result);
}
);
}
module.exports = MongoClient;
Затем при изменении кода вам нужно
var MongoClient = require("./mongoHelper.js");
Затем, когда пришло время для сохранения вызова (после подключения и получения коллекции)
MongoClient.saveAny(data, collection, function(){db.close();});
Вы можете изменить обработку ошибок в соответствии с вашими потребностями, передать ошибку в обратном вызове и т.д.
Ответ 11
Это старый вопрос, но он впервые появился для меня в результатах поиска при поиске в массиве документов "mongoose insert".
Есть два варианта model.create() [mongoose] и model.collection.insert() [mongodb], которые вы можете использовать. Просмотрите более подробное обсуждение преимуществ/недостатков каждого варианта:
Ответ 12
Вот пример использования MongoDB Model.collection.insert()
непосредственно в Mongoose. Обратите внимание, что если у вас не так много документов, скажем менее 100 документов, вам не нужно использовать массовую операцию MongoDB (см. Это).
MongoDB также поддерживает массовую вставку, передавая массив документов в метод db.collection.insert().
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
email : { type: String, index: { unique: true } },
name : String
});
var User = mongoose.model('User', userSchema);
function saveUsers(users) {
User.collection.insert(users, function callback(error, insertedDocs) {
// Here I use KrisKowal Q (https://github.com/kriskowal/q) to return a promise,
// so that the caller of this function can act upon its success or failure
if (!error)
return Q.resolve(insertedDocs);
else
return Q.reject({ error: error });
});
}
var users = [{email: '[email protected]', name: 'foo'}, {email: '[email protected]', name: 'baz'}];
saveUsers(users).then(function() {
// handle success case here
})
.fail(function(error) {
// handle error case here
});