133

Does Mongoose v3.6+ support batch inserts now? I've searched for a few minutes but anything matching this query is a couple of years old and the answer was an unequivocal no.

Edit:

For future reference, the answer is to use Model.create(). create() accepts an array as its first argument, so you can pass your documents to be inserted as an array.

See Model.create() documentation

hong4rc
  • 3,999
  • 4
  • 21
  • 40
Geuis
  • 41,122
  • 56
  • 157
  • 219
  • See [this answer](http://stackoverflow.com/a/14133893/1259510) to a previous question. – JohnnyHK May 24 '13 at 02:07
  • Thanks. That's what I ended up finding after posting. – Geuis May 24 '13 at 02:53
  • @Geuis please add your edit as an answer and accept it to resolve your question. – Filip Dupanović Jan 23 '14 at 11:55
  • https://groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds – arcseldon Jul 24 '14 at 19:19
  • Model.create() is slow and if you're considering inserting a huge number of documents, it's better to take [this approach](http://stackoverflow.com/a/24848148/778272) instead. – Lucio Paiva Jan 23 '15 at 02:06
  • Check this http://stackoverflow.com/questions/38509295/mongoose-silent-crash-in-node-js/38512822#38512822 – MarBVI Sep 21 '16 at 18:45
  • mongoose now supports [`Model.bulkWrite()`](https://mongoosejs.com/docs/api.html#model_Model.bulkWrite) and [`Model.insertMany()`](https://mongoosejs.com/docs/api.html#model_Model.insertMany). – Dan Dascalescu Apr 13 '20 at 05:48

8 Answers8

185

Model.create() vs Model.collection.insert(): a faster approach

Model.create() is a bad way to do inserts if you are dealing with a very large bulk. It will be very slow. In that case you should use Model.collection.insert, which performs much better. Depending on the size of the bulk, Model.create() will even crash! Tried with a million documents, no luck. Using Model.collection.insert it took just a few seconds.

Model.collection.insert(docs, options, callback)
  • docs is the array of documents to be inserted;
  • options is an optional configuration object - see the docs
  • callback(err, docs) will be called after all documents get saved or an error occurs. On success, docs is the array of persisted documents.

As Mongoose's author points out here, this method will bypass any validation procedures and access the Mongo driver directly. It's a trade-off you have to make since you're handling a large amount of data, otherwise you wouldn't be able to insert it to your database at all (remember we're talking hundreds of thousands of documents here).

A simple example

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);
    }
}

Update 2019-06-22: although insert() can still be used just fine, it's been deprecated in favor of insertMany(). The parameters are exactly the same, so you can just use it as a drop-in replacement and everything should work just fine (well, the return value is a bit different, but you're probably not using it anyway).

Reference

Lucio Paiva
  • 19,015
  • 11
  • 82
  • 104
  • 1
    https://groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds Says it all really. – arcseldon Jul 24 '14 at 19:20
  • 1
    Please give example with Mongoose. – Stephan Kristyn Sep 17 '14 at 15:42
  • 18
    Since `Model.collection` goes directly through the Mongo driver, you lose all the neat mongoose stuff including validation and hooks. Just something to keep in mind. `Model.create` loses the hooks, but still goes through validation. If you want it all, you must iterate and `new MyModel()` – Pier-Luc Gendreau Dec 11 '14 at 20:32
  • 1
    @Pier-LucGendreau You are absolutely right, but it's a tradeoff you have to make once you start dealing with a humongous amount of data. – Lucio Paiva Jan 23 '15 at 02:00
  • @SirBenBenji Example given :-) – Lucio Paiva Jan 23 '15 at 02:41
  • Thanks @arcseldon. Added your link as a reference. – Lucio Paiva Jan 23 '15 at 02:52
  • 1
    Be careful to new readers: "Changed in version 2.6: The insert() returns an object that contains the status of the operation". No more docs. – Mark Ni Jan 29 '16 at 13:49
  • By addressing .collection property you are bypassing Mongoose (validation, 'pre' methods ...). See the other answer for using (new native Mongoose method)[http://stackoverflow.com/a/35174701/1154241]. – Derek Mar 04 '16 at 11:34
  • Probably the best way to add tons of data :D – John Dec 12 '16 at 13:21
  • `insert` is now deprecated, use `insertMany` with the same function signature as `insert` – Eat at Joes Jun 21 '19 at 14:08
  • @EatatJoes can you point us to the documentation where it says so? I just checked 4.0 docs and `insert` is *not* marked as deprecated. Also checked the upcoming 4.2 and nothing there either. `insertMany` was added in 3.2, so I think I will keep using `insert` here since people may still be using older versions. If you find the reference where it says it's been deprecated, please send it to me and I'll add it to the answer. – Lucio Paiva Jun 21 '19 at 16:56
  • @LucioPaiva I had a look at History.md and it looks like Mongoose started using mongodb-native 3.2.0 since version 5.5.0, here is the link, http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#insert Since .collection.insert{Many} is just an interface to the core mongo driver you will see the deprecation warning. – Eat at Joes Jun 21 '19 at 21:03
  • Thanks for the link @EatatJoes - added it to the answer. – Lucio Paiva Jun 22 '19 at 15:30
  • As per mongoose 5.7.3 `insertMany` is 3-4 slower than `collection.insert`, probably due to the fact that it returns all the document back to the client. `collection.insert` is as fast as `pymongo insert_many` - https://stackoverflow.com/questions/58226391/mongoose-vs-pymongo-drivers-write-insertmany-test. – Yuki Oct 03 '19 at 22:56
127

Mongoose 4.4.0 now supports bulk insert

Mongoose 4.4.0 introduces --true-- bulk insert with the model method .insertMany(). It is way faster than looping on .create() or providing it with an array.

Usage:

var rawDocuments = [/* ... */];

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

Or

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

You can track it on:

Mc 100's
  • 480
  • 1
  • 4
  • 19
Derek
  • 3,295
  • 3
  • 24
  • 31
  • 2
    At this time, this method does not support options. – Amri May 23 '16 at 10:48
  • Thank you for the answer. Any idea what parsing of the rawDocuments should be in place? I've tried it with an array of Json objects and all it has inserted was just their IDs. :( – Ondrej Tokar Aug 01 '16 at 13:26
  • 4
    How is this different to `bulkWrite`? See here: http://stackoverflow.com/questions/38742475/what-is-the-right-approach-to-update-many-records-in-mongodb-using-mongoose/38743353#38743353 – Ondrej Tokar Aug 04 '16 at 08:07
  • insertMany doesn't work for me. I got a `fatal error allocation failed`. But if I use collection.insert It works perfectly. – John Dec 12 '16 at 13:03
  • Would this work with the extra stuff that mongoose schema provides? ex will this add the data if no date exists `dateCreated : { type: Date, default: Date.now },` – jack blank Feb 09 '17 at 05:45
  • This is the right answer. I had the same problem and this solved it and its FAST – kjohnsonthecoder Mar 17 '19 at 02:57
  • As per mongoose 5.7.3 `insertMany` is 3-4 slower than `collection.insert`, probably due to the fact that it returns all the document back to the client. `collection.insert` is as fast as `pymongo insert_many` - https://stackoverflow.com/questions/58226391/mongoose-vs-pymongo-drivers-write-insertmany-test. – Yuki Oct 03 '19 at 22:59
23

Indeed, you can use the "create" method of Mongoose, it can contain an array of documents, see this example:

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

The callback function contains the inserted documents. You do not always know how many items has to be inserted (fixed argument length like above) so you can loop through them:

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

Update: A better solution

A better solution would to use Candy.collection.insert() instead of Candy.create() - used in the example above - because it's faster (create() is calling Model.save() on each item so it's slower).

See the Mongo documentation for more information: http://docs.mongodb.org/manual/reference/method/db.collection.insert/

(thanks to arcseldon for pointing this out)

benske
  • 3,922
  • 4
  • 24
  • 22
  • https://groups.google.com/forum/#!topic/mongoose-orm/IkPmvcd0kds - Depending on what you want, the link has a better option. – arcseldon Jul 24 '14 at 19:19
  • Don't you mean `{type:'jellybean'}` instead of `{type:'jelly bean'}`? Btw. what strange types are those? Are they part of Mongoose API? – Stephan Kristyn Sep 17 '14 at 14:56
  • 2
    Well that's a bad naming choice then, since `type` is usually reserved in Mongoose for denominating the ADT of a database object. – Stephan Kristyn Sep 18 '14 at 17:11
  • 2
    @sirbenbenji I changed it, but it was an example also present in the official documentation. It was not necessary to downvote for this I think. – benske Sep 19 '14 at 09:31
  • 1
    By addressing .collection property you are bypassing Mongoose (validation, 'pre' methods ...) – Derek Mar 04 '16 at 11:34
6

Here are both way of saving data with insertMany and save

1) Mongoose save array of documents with insertMany in bulk

/* 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 array of documents with .save()

These documents will save parallel.

/* 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});
        })
    })
})
Arpit
  • 1,423
  • 1
  • 17
  • 20
5

It seems that using mongoose there is a limit of more than 1000 documents, when using

Potato.collection.insert(potatoBag, onInsert);

You can use:

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)
      });
});

But this is almost twice as fast when testing with 10000 documents:

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);
    });
}
ddennis
  • 211
  • 2
  • 7
  • 1
    By addressing .collection property you are bypassing Mongoose (validation, 'pre' methods ...) – Derek Mar 04 '16 at 11:33
4

You can perform bulk insert using mongoDB shell using inserting the values in an array.

db.collection.insert([{values},{values},{values},{values}]);
SUNDARRAJAN K
  • 2,237
  • 2
  • 22
  • 38
4

You can perform bulk insert using mongoose, as the highest score answer. But the example cannot work, it should be:

/* 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);
    }
}

Don't use a schema instance for the bulk insert, you should use a plain map object.

user2582680
  • 49
  • 1
  • 1
0

Sharing working and relevant code from our project:

//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);
    });
Zameer Ansari
  • 28,977
  • 24
  • 140
  • 219
  • The `.insertMany` solution was already given (and explained) in this [2016 answer](https://stackoverflow.com/questions/16726330/mongoose-mongodb-batch-insert/35174701#35174701). – Dan Dascalescu Apr 13 '20 at 05:30