如何在Mongoose/Node.js中同时保存多个文档?

如何在Mongoose/Node.js中同时保存多个文档?,node.js,mongodb,mongoose,Node.js,Mongodb,Mongoose,目前,我使用save添加单个文档。假设我有一个文档数组,希望将其存储为单个对象。有没有一种方法可以通过一个函数调用将它们全部添加到一起,然后在完成后得到一个回调?我可以单独添加所有文档,但管理回调以确定何时完成所有操作会有问题。Mongoose尚未实现批量插入(请参阅) 由于您知道要保存的文档数量,因此可以编写如下内容: var total = docArray.length , result = [] ; function saveAll(){ var doc = docArray.

目前,我使用save添加单个文档。假设我有一个文档数组,希望将其存储为单个对象。有没有一种方法可以通过一个函数调用将它们全部添加到一起,然后在完成后得到一个回调?我可以单独添加所有文档,但管理回调以确定何时完成所有操作会有问题。

Mongoose尚未实现批量插入(请参阅)

由于您知道要保存的文档数量,因此可以编写如下内容:

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();
  async.parallel([obj1.save, obj2.save, obj3.save], callback);

当然,这是一种权宜之计,我建议使用某种流控制库(我使用,而且非常棒)。

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


Mongoose现在支持将多个文档结构传递给。引用他们的API示例,它支持传递数组或varargs对象列表,并在末尾带有回调:

Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
    if (err) // ...
});


Edit:正如许多人所指出的,这不会执行真正的批量插入-它只是隐藏了自己多次调用
save
的复杂性。下面有一些答案和注释,解释了如何使用实际的Mongo驱动程序实现大容量插入以提高性能。

添加一个名为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");
然后是保存通话的时间(在您连接并检索到集合后)

您可以根据需要更改错误处理,在回调中传回错误等。

使用,您的代码如下所示:

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();
  async.parallel([obj1.save, obj2.save, obj3.save], callback);
由于Mongoose中的约定与async(err,callback)中的约定相同,因此您不需要将它们包装在自己的回调中,只需将save调用添加到数组中,就可以在完成所有调用后获得回调

如果使用mapLimit,则可以控制要并行保存的文档数量。在本例中,我们并行保存10个文档,直到成功保存所有项目

async.mapLimit(myArray, 10, function(document, next){
  document.save(next);
}, done);

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

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


解决方案:Mongoose自带本机MongoDB驱动程序。您可以通过要求它
require('mongoose/node\u modules/mongodb')
来使用该驱动程序(对此不太确定,但如果mongodb npm不起作用,您可以再次安装,但我认为应该安装),然后使用.insert()在mongoose中执行适当的
batchInsert

批量插入除非您需要访问中间件

Model.collection.insert(文档、选项、回调)


较新版本的MongoDB支持批量操作:

var col=db.collection('people');
var batch=col.initializeUnderedBulkop();
插入({name:“John”});
插入({name:“Jane”});
插入({name:“Jason”});
插入({name:“Joanne”});
批处理执行(函数(错误、结果){
if(err)控制台错误(err);
console.log('Inserted'+result.n插入+'行);
}

您可以使用mongoose返回的承诺
保存
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);
  })
var Promise=require(“./Promise”)
...
var任务=[];
对于(变量i=0;i
这是一个老问题,但我在搜索“mongoose insert array of documents”(猫鼬插入文档数组)时,在谷歌搜索结果中首先想到了这个问题

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


这里是一个直接在Mongoose中使用MongoDB的
Model.collection.insert()
的示例。请注意,如果没有那么多文档,比如少于100个文档,则不需要使用MongoDB的批量操作()

MongoDB还支持通过传递 将文档添加到db.collection.insert()方法


Mongoose4.4添加了一个名为

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

引用vkarpov15的问题:

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

该方法的签名与
create
相同:

Model.insertMany([ ... ], (err, docs) => {
  ...
})
或者,承诺:

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

使用
insertMany
函数插入多个文档。这只会向服务器发送一个操作,并且
Mongoose
会在命中mongo服务器之前验证所有文档。默认情况下
Mongoose
会按项目在数组中的顺序插入项目。如果您不需要维护任何顺序,则设置
ordered:错误

重要信息-错误处理:

ordered:true
组中发生验证和错误处理意味着如果
Model.insertMany([ ... ], (err, docs) => {
  ...
})
Model.insertMany([ ... ]).then((docs) => {
  ...
}).catch((err) => {
  ...
})