[MongoDB]: Save vs Insert & Save vs Update

For save, if you provide _id, it will update. If you don’t, it will insert.

To elaborate, if a document does not exist with the specified _id value, the save() method performs an insert with the specified fields in the document.
If a document exists with the specified _id value, the save() method performs an update, replacing all field in the existing record with the fields from the document.

From this, we can say that save is a wrapper for update and insert.

Functionally, save and insert are very similar, especially if no _id value is passed.
However, if an _id key is passed, save() will update the document, while insert() will throw a duplicate key error.

Please see below illustration to make you understand this better.

[root@dbversity]# mongo dbversity –quiet
>
> db.mycol.find()
>
> // When we don’t pass _id value
>
> db.mycol.insert({name:’insert’})
WriteResult({ “nInserted” : 1 })
>
> db.mycol.save({name:’save’})
WriteResult({ “nInserted” : 1 })
>
> db.mycol.find()
{ “_id” : ObjectId(“5c05688042e9152c0cd3848f”), “name” : “insert” }
{ “_id” : ObjectId(“5c05688d42e9152c0cd38490”), “name” : “save” }
>
> // when we pass _id value
>
> db.mycol.insert({_id:1, name:’newrecord’})
WriteResult({ “nInserted” : 1 })
>
> db.mycol.insert({_id:1, name:’newrecord’})
WriteResult({
“nInserted” : 0,
“writeError” : {
“code” : 11000,
“errmsg” : “E11000 duplicate key error collection: dbversity.mycol index: _id_ dup key: { : 1.0 }”
}
})
>
> db.mycol.save({_id:1, name:’newrecord’})
WriteResult({ “nMatched” : 1, “nUpserted” : 0, “nModified” : 0 })
>
>
> db.mycol.save({_id:2, name:’newrecord’})
WriteResult({ “nMatched” : 0, “nUpserted” : 1, “nModified” : 0, “_id” : 2 })
>
> db.mycol.save({_id:2, name:’newrecord’})
WriteResult({ “nMatched” : 1, “nUpserted” : 0, “nModified” : 0 })
>
> db.mycol.save({_id:2, name:’newrecord’})
WriteResult({ “nMatched” : 1, “nUpserted” : 0, “nModified” : 0 })
>
> db.mycol.save({_id:2, name:’updated record‘})
WriteResult({ “nMatched” : 1, “nUpserted” : 0, “nModified” : 1 })
>
>
> db.mycol.insert({_id:2, name:’updated record’})
WriteResult({
“nInserted” : 0,
“writeError” : {
“code” : 11000,
“errmsg” : “E11000 duplicate key error collection: dbversity.mycol index: _id_ dup key: { : 2.0 }”
}
})
>
>

Let us consider the two cases here for save in a pictorial representation :-

1) Having _id in doc.

2) Not having _id in doc.

                        Save ()
                        /     \
                       /       \

                 Having _id     Not Having _id 

  ->In this case save will do    ->  It will do normal insertion 
    upsert to insert.Now             in this case as insert() do.
    what that means, it means 
    take the document and replace 
    the complete document having same
    _id.

Let us consider the two cases here for insert:-

1) Having _id of doc in collection.

2) Not having _id of doc in collection.

                        Insert()
                       /        \
                      /          \

   Doc Having _id in collection    Doc Not Having _id 
  ->  E11000 duplicate key     ->Insert a new doc inside the collection.
      error index:       

 

 

Save vs Update :

update modifies an existing document matched with your query params. If there is no such matching document, that’s when upsert comes in picture.

upsert : false : Nothing happens when no such document exist
upsert : true : New doc gets created with contents equal to query params and update params

save : Doesn’t allow any query-params. if _id exists and there is a matching doc with the same _id, it replaces it. When no _id specified/no matching document, it inserts the document as a new one.

 

Save function :

> t.save

function (obj, opts) {

    if (obj == null)

        throw Error(“can’t save a null”);

    if (typeof(obj) == “number” || typeof(obj) == “string”)

        throw Error(“can’t save a number or string”);

    if (typeof(obj._id) == “undefined”) {

        obj._id = new ObjectId();

        return this.insert(obj, opts);

    } else {

        return this.update({_id: obj._id}, obj, Object.merge({upsert: true}, opts));

    }

}

>

 

Insert Function :

> t.insert

function (obj, options) {

    if (!obj)

        throw Error(“no object passed to insert!”);

    var flags = 0;

    var wc = undefined;

    var allowDottedFields = false;

    if (options === undefined) {

        // do nothing

    } else if (typeof(options) == ‘object’) {

        if (options.ordered === undefined) {

            // do nothing, like above

        } else {

            flags = options.ordered ? 0 : 1;

        }

        if (options.writeConcern)

            wc = options.writeConcern;

        if (options.allowdotted)

            allowDottedFields = true;

    } else {

        flags = options;

    }

    // 1 = continueOnError, which is synonymous with unordered in the write commands/bulk-api

    var ordered = ((flags & 1) == 0);

    if (!wc)

        wc = this.getWriteConcern();

    var result = undefined;

    var startTime =

        (typeof(_verboseShell) === ‘undefined’ || !_verboseShell) ? 0 : new Date().getTime();

    if (this.getMongo().writeMode() != “legacy”) {

        // Bit 1 of option flag is continueOnError. Bit 0 (stop on error) is the default.

        var bulk = ordered ? this.initializeOrderedBulkOp() : this.initializeUnorderedBulkOp();

        var isMultiInsert = Array.isArray(obj);

        if (isMultiInsert) {

            obj.forEach(function(doc) {

                bulk.insert(doc);

            });

        } else {

            bulk.insert(obj);

        }

        try {

            result = bulk.execute(wc);

            if (!isMultiInsert)

                result = result.toSingleResult();

        } catch (ex) {

            if (ex instanceof BulkWriteError) {

                result = isMultiInsert ? ex.toResult() : ex.toSingleResult();

            } else if (ex instanceof WriteCommandError) {

                result = ex;

            } else {

                // Other exceptions rethrown as-is.

                throw ex;

            }

        }

    } else {

        if (typeof(obj._id) == “undefined” && !Array.isArray(obj)) {

            var tmp = obj;  // don’t want to modify input

            obj = {_id: new ObjectId()};

            for (var key in tmp) {

                obj[key] = tmp[key];

            }

        }

        this.getMongo().insert(this._fullName, obj, flags);

        // enforce write concern, if required

        if (wc)

            result = this.runCommand(“getLastError”, wc instanceof WriteConcern ? wc.toJSON() : wc);

    }

    this._lastID = obj._id;

    this._printExtraInfo(“Inserted”, startTime);

    return result;

}

>

Update Function :

 

 

> t.update

function (query, obj, upsert, multi) {

    var parsed = this._parseUpdate(query, obj, upsert, multi);

    var query = parsed.query;

    var obj = parsed.obj;

    var upsert = parsed.upsert;

    var multi = parsed.multi;

    var wc = parsed.wc;

    var collation = parsed.collation;

    var arrayFilters = parsed.arrayFilters;

    var result = undefined;

    var startTime =

        (typeof(_verboseShell) === 'undefined' || !_verboseShell) ? 0 : new Date().getTime();

    if (this.getMongo().writeMode() != "legacy") {

        var bulk = this.initializeOrderedBulkOp();

        var updateOp = bulk.find(query);

        if (upsert) {

            updateOp = updateOp.upsert();

        }

        if (collation) {

            updateOp.collation(collation);

        }

        if (arrayFilters) {

            updateOp.arrayFilters(arrayFilters);

        }

        if (multi) {

            updateOp.update(obj);

        } else {

            updateOp.updateOne(obj);

        }

        try {

            result = bulk.execute(wc).toSingleResult();

        } catch (ex) {

            if (ex instanceof BulkWriteError) {

                result = ex.toSingleResult();

            } else if (ex instanceof WriteCommandError) {

                result = ex;

            } else {

                // Other exceptions thrown

                throw ex;

            }

        }

    } else {

        if (collation) {

            throw new Error("collation requires use of write commands");

        }

        if (arrayFilters) {

            throw new Error("arrayFilters requires use of write commands");

        }

        this.getMongo().update(this._fullName, query, obj, upsert, multi);

        // Enforce write concern, if required

        if (wc) {

            result = this.runCommand("getLastError", wc instanceof WriteConcern ? wc.toJSON() : wc);

        }

    }

    this._printExtraInfo("Updated", startTime);

    return result;

}

> 

  • Ask Question