如何在Mongoose / Node.js中同时保存多个文档?
目前我使用保存添加单个文档。 假设我有一组我希望作为单个对象存储的文档。 有没有办法通过一个函数调用来添加它们,然后在完成时获得单个callback? 我可以单独添加所有文档,但是在完成所有工作时pipe理callback以解决问题。
mongoose没有实现批量插入(参见问题#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();
当然,这是一个解决scheme,我会推荐使用某种stream控制库(我使用q而且很棒)。
Mongoose现在支持将多个文档结构传递给Model.create 。 引用他们的API示例,它支持在末尾传递一个带有callback的对象的数组或对象的可变参数列表:
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驱动程序来实现一个批量插入性能的利益。
在Mongoose中批量插入可以使用.insert()来完成,除非您需要访问中间件。
Model.collection.insert(docs, options, callback)
https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L71-91
Mongoose 4.4增加了一个名为insertMany
的方法
validation文档数组并将其插入MongoDB的快捷方式,如果它们都是有效的。 该函数比.create()更快,因为它只向服务器发送一个操作,而不是每个文档都发送一个操作。
从问题#723引用vkarpov15:
权衡的是,insertMany()不会触发预保存钩子,但它应该有更好的性能,因为它只对数据库进行一次往返,而不是每个文档1次。
该方法的签名与create
相同:
Model.insertMany([ ... ], (err, docs) => { ... })
或者,承诺:
Model.insertMany([ ... ]).then((docs) => { ... }).catch((err) => { ... })
使用asynchronous并行 ,您的代码将如下所示:
async.parallel([obj1.save, obj2.save, obj3.save], callback);
由于Mongoose中的约定与async(err,callback)中的约定相同,因此不需要将它们包装在自己的callback函数中,只需将您的保存调用添加到数组中,并在完成后调用callback函数。
如果您使用mapLimit,则可以控制要并行保存多less个文档。 在这个例子中,我们保存10个文件,直到所有的项目都成功保存。
async.mapLimit(myArray, 10, function(document, next){ document.save(next); }, done);
我知道这是一个古老的问题,但是我担心这里没有正确的答案。 大多数答案只是讨论遍历所有文档并将它们分别保存,如果文档不止一个,这是一个糟糕的主意,并且这个过程在很多请求中都会重复。
MongoDB特别有一个用于插入多个文档的batchInsert()
调用,这应该从本地mongodb驱动程序中使用。 Mongoosebuild立在这个驱动程序之上,它不支持批量插入。 这可能是有道理的,因为它应该是一个MongoDB的对象文档build模工具。
解决scheme:Mongoose附带了本地MongoDB驱动程序。 你可以通过要求require('mongoose/node_modules/mongodb')
来使用这个驱动(不太清楚这个,但是如果它不工作,你可以再次安装mongodb npm,但是我认为应该)一个正确的batchInsert
这是另一种方式,不使用额外的库(不包括错误检查)
function saveAll( callback ){ var count = 0; docs.forEach(function(doc){ doc.save(function(err){ count++; if( count == docs.length ){ callback(); } }); }); }
较新版本的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).'); }
你可以使用mongoosesave
返回的Promise
,mongoose中的Promise
没有,但你可以添加该模块的function。
创build一个模块,以增强所有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); })
添加一个名为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();});
您可以更改error handling,以适应您的需要,回传错误在callback等
这是一个古老的问题,但在search“mongoose插入数组文件”时,它在谷歌search结果中首先出现。
有两个选项,你可以使用model.create()[mongoose]和model.collection.insert()[mongodb]。 在这里查看每个选项的优缺点更彻底的讨论:
mongoose(mongodb)批量插入?
下面是一个在Mongoose中直接使用MongoDB的Model.collection.insert()
的例子。 请注意,如果您没有太多的文档,说less于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's 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: 'foo@bar.com', name: 'foo'}, {email: 'baz@bar.com', name: 'baz'}]; saveUsers(users).then(function() { // handle success case here }) .fail(function(error) { // handle error case here });