【问题标题】:Mongoose (mongodb) batch insert?猫鼬(mongodb)批量插入?
【发布时间】:2013-05-19 13:04:51
【问题描述】:

Mongoose v3.6+ 现在支持批量插入吗?我已经搜索了几分钟,但与此查询匹配的任何内容都是几年前的,答案是明确的不。

编辑:

为了将来参考,答案是使用Model.create()create() 接受一个数组作为其第一个参数,因此您可以将要插入的文档作为数组传递。

Model.create() documentation

【问题讨论】:

标签: node.js mongodb mongoose


【解决方案1】:

Model.create() 与 Model.collection.insert():更快的方法

Model.create() 如果您要处理非常大的批量,那么插入是一种不好的方法。它会非常慢。在这种情况下,您应该使用Model.collection.insert,它的性能要好得多。根据散装的大小,Model.create() 甚至会崩溃!尝试了一百万个文件,没有运气。使用Model.collection.insert 只需几秒钟。

Model.collection.insert(docs, options, callback)
  • docs 是要插入的文档数组;
  • options 是一个可选配置对象 - 请参阅 the docs
  • callback(err, docs) 将在所有文档保存或发生错误后调用。成功时,docs 是持久化文档的数组。

正如 Mongoose 的作者所指出的here,此方法将绕过任何验证程序并直接访问 Mongo 驱动程序。这是您必须做出的权衡,因为您要处理大量数据,否则您根本无法将其插入数据库(请记住,我们在这里讨论的是数十万个文档)。

一个简单的例子

var Potato = mongoose.model('Potato', PotatoSchema);

var potatoBag = [/* a humongous amount of potato objects */];

Potato.collection.insert(potatoBag, onInsert);

function onInsert(err, docs) {
    if (err) {
        // TODO: handle error
    } else {
        console.info('%d potatoes were successfully stored.', docs.length);
    }
}

2019-06-22 更新:虽然insert() 仍然可以正常使用,但它已被弃用,取而代之的是insertMany()。参数完全相同,因此您可以将其用作直接替换,并且一切都应该正常工作(嗯,返回值有点不同,但您可能无论如何都不会使用它)。

参考

【讨论】:

  • 请以猫鼬为例。
  • 由于Model.collection 直接通过 Mongo 驱动程序,您将丢失所有整洁的 mongoose 内容,包括验证和钩子。只是要记住的事情。 Model.create 失去了钩子,但仍然通过验证。如果你想要这一切,你必须迭代和new MyModel()
  • @Pier-LucGendreau 您说的完全正确,但是一旦您开始处理大量数据,就必须做出权衡。
  • 请注意新读者:“2.6 版更改:insert() 返回一个包含操作状态的对象”。没有更多文档了。
【解决方案2】:

这里有两种使用 insertMany 和 save 保存数据的方法

1) Mongoose 批量保存带有insertMany 的文档数组

/* write mongoose schema model and export this */
var Potato = mongoose.model('Potato', PotatoSchema);

/* write this api in routes directory  */
router.post('/addDocuments', function (req, res) {
    const data = [/* array of object which data need to save in db */];

    Potato.insertMany(data)  
    .then((result) => {
            console.log("result ", result);
            res.status(200).json({'success': 'new documents added!', 'data': result});
    })
    .catch(err => {
            console.error("error ", err);
            res.status(400).json({err});
    });
})

2) Mongoose 使用.save() 保存文档数组

这些文档将并行保存。

/* write mongoose schema model and export this */
var Potato = mongoose.model('Potato', PotatoSchema);

/* write this api in routes directory  */
router.post('/addDocuments', function (req, res) {
    const saveData = []
    const data = [/* array of object which data need to save in db */];
    data.map((i) => {
        console.log(i)
        var potato = new Potato(data[i])
        potato.save()
        .then((result) => {
            console.log(result)
            saveData.push(result)
            if (saveData.length === data.length) {
                res.status(200).json({'success': 'new documents added!', 'data': saveData});
            }
        })
        .catch((err) => {
            console.error(err)
            res.status(500).json({err});
        })
    })
})

【讨论】:

    【解决方案3】:

    分享我们项目中的工作代码和相关代码:

    //documentsArray is the list of sampleCollection objects
    sampleCollection.insertMany(documentsArray)  
        .then((res) => {
            console.log("insert sampleCollection result ", res);
        })
        .catch(err => {
            console.log("bulk insert sampleCollection error ", err);
        });
    

    【讨论】:

    • .insertMany 解决方案已在此 2016 answer 中给出(并解释)。
    【解决方案4】:

    Mongoose 4.4.0 现在支持批量插入

    Mongoose 4.4.0 引入了 --true-- 批量插入,模型方法.insertMany()。这比在 .create() 上循环或为其提供数组要快得多。

    用法:

    var rawDocuments = [/* ... */];
    
    Book.insertMany(rawDocuments)
        .then(function(mongooseDocuments) {
             /* ... */
        })
        .catch(function(err) {
            /* Error handling */
        });
    

    或者

    Book.insertMany(rawDocuments, function (err, mongooseDocuments) { /* Your callback function... */ });
    

    你可以追踪它:

    【讨论】:

    • 目前此方法不支持选项。
    • 感谢您的回答。知道应该对 rawDocuments 进行什么解析吗?我用一组 Json 对象尝试过它,它插入的只是它们的 ID。 :(
    • 这与bulkWrite 有何不同?见这里:stackoverflow.com/questions/38742475/…
    • insertMany 对我不起作用。我有一个fatal error allocation failed。但是如果我使用 collection.insert 它就完美了。
    • 这是否适用于猫鼬模式提供的额外内容?如果不存在日期,ex 将添加数据dateCreated : { type: Date, default: Date.now },
    【解决方案5】:

    似乎使用猫鼬有超过1000个文档的限制,使用时

    Potato.collection.insert(potatoBag, onInsert);
    

    你可以使用:

    var bulk = Model.collection.initializeOrderedBulkOp();
    
    async.each(users, function (user, callback) {
        bulk.insert(hash);
    }, function (err) {
        var bulkStart = Date.now();
        bulk.execute(function(err, res){
            if (err) console.log (" gameResult.js > err " , err);
            console.log (" gameResult.js > BULK TIME  " , Date.now() - bulkStart );
            console.log (" gameResult.js > BULK INSERT " , res.nInserted)
          });
    });
    

    但这几乎是使用 10000 个文档进行测试时的两倍:

    function fastInsert(arrOfResults) {
    var startTime = Date.now();
        var count = 0;
        var c = Math.round( arrOfResults.length / 990);
    
        var fakeArr = [];
        fakeArr.length = c;
        var docsSaved = 0
    
        async.each(fakeArr, function (item, callback) {
    
                var sliced = arrOfResults.slice(count, count+999);
                sliced.length)
                count = count +999;
                if(sliced.length != 0 ){
                        GameResultModel.collection.insert(sliced, function (err, docs) {
                                docsSaved += docs.ops.length
                                callback();
                        });
                }else {
                        callback()
                }
        }, function (err) {
                console.log (" gameResult.js > BULK INSERT AMOUNT: ", arrOfResults.length, "docsSaved  " , docsSaved, " DIFF TIME:",Date.now() - startTime);
        });
    }
    

    【讨论】:

    • 通过寻址 .collection 属性,您正在绕过 Mongoose(验证,'pre' 方法......)
    【解决方案6】:

    您可以使用 mongoose 执行批量插入,作为最高分答案。 但是例子行不通,应该是:

    /* a humongous amount of potatos */
    var potatoBag = [{name:'potato1'}, {name:'potato2'}];
    
    var Potato = mongoose.model('Potato', PotatoSchema);
    Potato.collection.insert(potatoBag, onInsert);
    
    function onInsert(err, docs) {
        if (err) {
            // TODO: handle error
        } else {
            console.info('%d potatoes were successfully stored.', docs.length);
        }
    }
    

    不要为批量插入使用架构实例,您应该使用普通的地图对象。

    【讨论】:

    • 第一个答案没有错,只是有验证
    • 通过寻址 .collection 属性,您正在绕过 Mongoose(验证,'pre' 方法......)
    【解决方案7】:

    确实,你可以使用Mongoose的“create”方法,它可以包含一个文档数组,看这个例子:

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

    回调函数包含插入的文档。 您并不总是知道必须插入多少项(如上固定参数长度),因此您可以循环遍历它们:

    var insertedDocs = [];
    for (var i=1; i<arguments.length; ++i) {
        insertedDocs.push(arguments[i]);
    }
    

    更新:更好的解决方案

    更好的解决方案是使用Candy.collection.insert() 而不是Candy.create() - 在上面的示例中使用 - 因为它更快(create() 在每个项目上调用Model.save(),所以它更慢)。

    有关更多信息,请参阅 Mongo 文档: http://docs.mongodb.org/manual/reference/method/db.collection.insert/

    (感谢 arcseldon 指出这一点)

    【讨论】:

    • groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds - 根据您的需要,链接有更好的选择。
    • 你的意思是{type:'jellybean'}而不是{type:'jelly bean'}吗?顺便提一句。那些是什么奇怪的类型?它们是 Mongoose API 的一部分吗?
    • 那么这是一个糟糕的命名选择,因为 type 通常在 Mongoose 中保留用于命名数据​​库对象的 ADT。
    • @sirbenbenji 我更改了它,但它也是官方文档中的一个示例。我认为没有必要为此投反对票。
    • 通过寻址 .collection 属性,您正在绕过 Mongoose(验证,'pre' 方法......)
    【解决方案8】:

    您可以使用 mongoDB shell 通过在数组中插入值来执行批量插入。

    db.collection.insert([{values},{values},{values},{values}]);
    

    【讨论】:

    • 猫鼬有办法批量插入吗?
    • YourModel.collection.insert()
    • 通过寻址 .collection 属性,您正在绕过 Mongoose(验证,'pre' 方法......)
    • 这不是猫鼬,原始的collection.insert answer 是在此答案前几周给出的,并进行了更详细的解释。
    猜你喜欢
    • 2017-04-09
    • 1970-01-01
    • 2013-11-07
    • 2014-07-15
    • 2017-10-08
    • 2013-06-29
    • 2015-03-03
    • 2013-07-12
    • 2017-10-05
    相关资源
    最近更新 更多