Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB- Insert if it doesn't exist, else skip

Is it possible to insert in Mongo with condition;

//Pseudo code  Bulk Insert Item :  If Key exists     Skip, don't throw error If key does not exist     Add item 

If i do single inserts, it might return an error or insert in the collection, but is it possible in bulk ?

like image 619
Basit Anwer Avatar asked Sep 07 '15 02:09

Basit Anwer


People also ask

Does MongoDB update insert if not exists?

You can use upsert i.e. whenever you insert a value and it already exist then update would be performed. If the value does not already exist then it would get inserted.

Does MongoDB Create collection if not exists?

If a collection does not exist, MongoDB creates the collection when you first store data for that collection. You can also explicitly create a collection with various options, such as setting the maximum size or the documentation validation rules.

How do I Upsert in MongoDB?

Upsert is a combination of insert and update (inSERT + UPdate = upsert). We can use the upsert with different update methods, i.e., update, findAndModify, and replaceOne. Here in MongoDB, the upsert option is a Boolean value. Suppose the value is true and the documents match the specified query filter.


1 Answers

You have two real choices here depending on how you want to handle things:

  1. Use upsert functionality of MongoDB to essentially "lookup" if the key data exists. If not then you only pass in data to $setOnInsert and that will not touch anything else.

  2. Use "UnOrdered" operations in Bulk. The whole batch of updates will continue even if an error is returned, but the error report(s) are just that, and anything that is not an error will be comitted.

Whole example:

var async = require('async'),     mongoose = require('mongoose'),     Schema = mongoose.Schema;  var testSchema = new Schema({   "_id": Number,   "name": String },{ "_id": false });  var Test = mongoose.model('Test',testSchema,'test');  mongoose.connect('mongodb://localhost/test');  var data = [   { "_id": 1, "name": "One" },   { "_id": 1, "name": "Another" },   { "_id": 2, "name": "Two" } ];  async.series(   [     // Start fresh     function(callback) {       Test.remove({},callback);     },      // Ordered will fail on error. Upserts never fail!     function(callback) {       var bulk = Test.collection.initializeOrderedBulkOp();       data.forEach(function(item) {         bulk.find({ "_id": item._id }).upsert().updateOne({           "$setOnInsert": { "name": item.name }         });       });       bulk.execute(callback);     },      // All as expected     function(callback) {       Test.find().exec(function(err,docs) {         console.log(docs)         callback(err);       });     },       // Start again     function(callback) {       Test.remove({},callback);     },      // Unordered will just continue on error and record an error     function(callback) {       var bulk = Test.collection.initializeUnorderedBulkOp();       data.forEach(function(item) {         bulk.insert(item);       });       bulk.execute(function(err,result) {         callback(); // so what! Could not care about errors       });     },       // Still processed the whole batch     function(callback) {       Test.find().exec(function(err,docs) {         console.log(docs)         callback(err);       });     }   ],   function(err) {     if (err) throw err;     mongoose.disconnect();   } ); 

Note that the "changed action" in current drivers is that the result response on .execute() will return an error object to be thrown, where previous releases did not do so with "Un-ordered" operations.

This makes it imperative that your code never relies on the err returned alone, and you should be inpspeting the returned result instead for the full classification of errors.

Nonetheless, when unordered then the batch continues until the end, no matter how many errors occur. Things that are not an error will be committed as normal.

This really comes down to "is sequence important". If so, then you need "Ordered" operations and you can only avoid duplicate keys by using "upserts". Otherwise use "unordered", but be aware of the error returns and what they actually mean.

Also, when using .collection to get the underlying collection object from the base driver to enable "Bulk" operations, then always be sure that either "some" mongoose method has always been called first.

Without that, there is no guaranteed connection to the database with the native driver methods as it is handled for the mongoose methods, so the operation will fail due to no connection.

The alternate to "firing" a mongoose method first, is to wrap your app logic in an event listener for the connection:

mongoose.connection.on("open",function(err) {     // app logic in here }) 
like image 51
Blakes Seven Avatar answered Oct 10 '22 21:10

Blakes Seven