目前,我使用保存添加单个文档。假设我有一系列文档希望存储为单个对象。有没有一种方法可以通过单个函数调用将它们全部添加,然后在完成后获得单个回调?我可以单独添加所有文档,但是管理回调以解决所有问题。
目前,我使用保存添加单个文档。假设我有一系列文档希望存储为单个对象。有没有一种方法可以通过单个函数调用将它们全部添加,然后在完成后获得单个回调?我可以单独添加所有文档,但是管理回调以解决所有问题。
Answers:
猫鼬尚未实现批量插入(请参阅问题#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很棒)。
save
,等待所有人调用其回调并返回结果数组。您可以为此使用async或某些promise接口。
if (--total)
为假?
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驱动程序来实现批量插入。
猫鼬4.4添加了一种称为 insertMany
验证文档数组并将其全部插入MongoDB的快捷方式。此函数比.create()更快,因为它仅向服务器发送一个操作,而不是为每个文档发送一个操作。
从问题#723引用vkarpov15 :
折衷方案是insertMany()不会触发预保存的钩子,但是它应该具有更好的性能,因为它仅使数据库往返1次,而不是每个文档往返1次。
该方法的签名与create
:
Model.insertMany([ ... ], (err, docs) => {
...
})
或者,承诺:
Model.insertMany([ ... ]).then((docs) => {
...
}).catch((err) => {
...
})
除非需要访问中间件,否则可以使用.insert()进行Mongoose中的批量插入。
Model.collection.insert(docs, options, callback)
https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L71-91
Model.collection.insert
在猫鼬中使用?请提供一个例子。
使用异步并行,您的代码将如下所示:
async.parallel([obj1.save, obj2.save, obj3.save], callback);
由于Mongoose中的约定与异步(err,回调)中的约定相同,因此您无需将它们包装在自己的回调中,只需将您的save调用添加到数组中,当所有操作完成后即可获得回调。
如果使用mapLimit,则可以控制要并行保存的文档数量。在此示例中,我们并行保存10个文档,直到成功保存所有项目。
async.mapLimit(myArray, 10, function(document, next){
document.save(next);
}, done);
myArray
?而myArray有1000万个项目。
我知道这是一个老问题,但令我担心的是,这里没有正确正确的答案。大多数答案只是谈论遍历所有文档并分别保存每个文档,如果您有多个文档,这是一个糟糕的主意,并且对于许多请求中的一个甚至重复该过程。
MongoDB专门有一个batchInsert()
用于插入多个文档的调用,应该在本机mongodb驱动程序中使用它。猫鼬基于此驱动程序构建,并且不支持批量插入。这可能很有意义,因为它应该是MongoDB的对象文档建模工具。
解决方案:Mongoose随附了本机MongoDB驱动程序。您可以通过要求使用该驱动程序require('mongoose/node_modules/mongodb')
(对此不太确定,但是如果无法正常运行,可以随时再次安装mongodb npm,但我认为应该这样做),然后执行适当的操作batchInsert
mongodb
javascript驱动程序解决的。
较新版本的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).');
}
您可以使用mongoose返回的promise 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;
然后与猫鼬一起使用:
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();});
您可以更改错误处理以适合您的需求,将错误传回回调等。
这是一个老问题,但是当搜索“猫鼬插入文档数组”时,它首先出现在Google搜索结果中。
您可以使用两个选项model.create()[mongoose]和model.collection.insert()[mongodb]。在这里查看有关每个选项的优缺点的更详尽的讨论:
这是Model.collection.insert()
直接在Mongoose中使用MongoDB的示例。请注意,如果您没有那么多文档(例如少于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
});