90

目前我使用 save 添加单个文档。假设我有一组文档,我希望将它们存储为单个对象。有没有办法通过一个函数调用将它们全部添加,然后在完成后获得一个回调?我可以单独添加所有文档,但管理回调以解决所有问题。

4

13 回答 13

101

Mongoose 现在支持将多个文档结构传递给Model.create。引用他们的 API 示例,它支持传递数组或可变参数对象列表,最后带有回调:

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 驱动程序来实现批量插入以提高性能。

于 2013-01-03T06:08:22.677 回答
79

Mongoose 4.4 添加了一个名为insertMany

验证文档数组并将它们插入 MongoDB(如果它们都有效)的快捷方式。此函数比 .create() 更快,因为它只向服务器发送一个操作,而不是为每个文档发送一个操作。

引用问题#723中的 vkarpov15 :

权衡是 insertMany() 不会触发预保存挂钩,但它应该具有更好的性能,因为它只对数据库进行 1 次往返,而不是每个文档 1 次。

该方法的签名与以下内容相同create

Model.insertMany([ ... ], (err, docs) => {
  ...
})

或者,通过承诺:

Model.insertMany([ ... ]).then((docs) => {
  ...
}).catch((err) => {
  ...
})
于 2016-03-18T17:05:12.213 回答
41

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,它很棒)。

于 2012-04-22T09:38:16.863 回答
26

除非您需要访问中间件,否则可以使用 .insert() 在 Mongoose 中进行批量插入。

Model.collection.insert(docs, options, callback)

https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L71-91

于 2013-05-31T21:45:01.633 回答
14

使用异步并行,您的代码将如下所示:

  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);
于 2013-04-17T19:02:35.730 回答
8

我知道这是一个老问题,但我担心这里没有正确的答案。大多数答案只是谈论遍历所有文档并单独保存每个文档,如果您有多个文档,这是一个坏主意,并且即使是许多请求中的一个,该过程也会重复。

MongoDB 专门batchInsert()调用了插入多个文档,这应该从本机 mongodb 驱动程序中使用。Mongoose 是基于这个驱动构建的,它不支持批量插入。这可能是有道理的,因为它应该是 MongoDB 的对象文档建模工具。

解决方案:Mongoose 自带原生 MongoDB 驱动程序。您可以通过要求使用该驱动程序require('mongoose/node_modules/mongodb')(对此不太确定,但如果它不起作用,您可以随时再次安装 mongodb npm,但我认为应该)然后做一个适当的batchInsert

于 2013-05-09T10:11:29.843 回答
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).');
}
于 2014-08-14T16:49:39.430 回答
5

这是另一种不使用其他库的方法(不包括错误检查)

function saveAll( callback ){
  var count = 0;
  docs.forEach(function(doc){
      doc.save(function(err){
          count++;
          if( count == docs.length ){
             callback();
          }
      });
  });
}
于 2012-04-22T09:44:47.560 回答
5

使用insertMany函数插入许多文档。这只会向服务器发送一个操作,并Mongoose在访问 mongo 服务器之前验证所有文档。默认情况下Mongoose,按照它们在数组中存在的顺序插入项目。如果您可以不维护任何订单,请设置ordered:false

重要 - 错误处理:

ordered:true验证和错误处理发生在一个组中时,意味着如果一个失败,一切都会失败。

ordered:false验证和错误处理单独发生时,操作将继续。错误将在一系列错误中报告。

于 2018-01-28T07:57:49.480 回答
3

您可以使用 mongoose 返回的 promise savePromise在 mongoose 中并不具备所有功能,但您可以使用此模块添加功能。

创建一个模块来增强 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);
  })
于 2015-06-16T19:31:35.823 回答
0

添加一个名为 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();});

您可以更改错误处理以满足您的需要,在回调中传回错误等。

于 2013-03-01T22:43:05.037 回答
0

这是一个老问题,但是当我搜索“猫鼬插入文档数组”时,它首先出现在谷歌结果中。

您可以使用两个选项 model.create() [mongoose] 和 model.collection.insert() [mongodb]。在此处查看有关每个选项的优缺点的更全面讨论:

猫鼬(mongodb)批量插入?

于 2015-07-26T15:23:45.547 回答
0

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
});
于 2015-11-04T17:04:14.687 回答