Many update operations in MongoDB have the potential to be upserts. An upsert is a combination of an insert and an update.
It works like this: You perform an update operation based on filter criteria, and if there are any matches, only the matched documents are updated, but if there are no matches, then a new document is inserted.
Example
Suppose we have a collection called pets
that contains the following documents:
{ "_id" : 1, "name" : "Wag", "type" : "Dog" } { "_id" : 2, "name" : "Bark", "type" : "Dog" } { "_id" : 3, "name" : "Meow", "type" : "Cat" }
We could perform the following update operation that sets the upsert
parameter to true
:
db.pets.updateOne(
{ name: "Wag" },
{ $set: { type: "Cow" } },
{ upsert: true }
)
Result:
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
In this case, there was a matching document (i.e. there’s a document with name: "Wag"
) and therefore the matching document was updated. Nothing was inserted.
We can verify this as follows:
db.pets.find()
Result:
{ "_id" : 1, "name" : "Wag", "type" : "Cow" } { "_id" : 2, "name" : "Bark", "type" : "Dog" } { "_id" : 3, "name" : "Meow", "type" : "Cat" }
The first document now has a type
of Cow
.
Lets’ run another update operation, again using upsert: true
. But this time, there will be no matching document to update.
db.pets.updateOne(
{ name: "Bubbles" },
{ $set: { type: "Fish" } },
{ upsert: true }
)
Result:
{ "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0, "upsertedId" : ObjectId("5fe1b4c8d9914101694100b7") }
In this example, we try to find a document that has name: "Bubbles"
but there’s none to be found.
This time we can see that the matchedCount
is 0
, and the modifiedCount
is also 0
. This means that none of the existing documents were updated.
We can also see that an upsertedId
was returned, which means that a document was upserted.
Let’s take another look at the collection of documents:
db.pets.find()
Result:
{ "_id" : 1, "name" : "Wag", "type" : "Cow" } { "_id" : 2, "name" : "Bark", "type" : "Dog" } { "_id" : 3, "name" : "Meow", "type" : "Cat" } { "_id" : ObjectId("5fe1b4c8d9914101694100b7"), "name" : "Bubbles", "type" : "Fish" }
We can see that a new document was inserted/upserted and it has the same ID as indicated above.
The upsert occurred because this time there were no matching documents to update (and so a new one was inserted/upserted instead).
If we hadn’t set upsert: true
, that document wouldn’t have been inserted.
Upsert on Bulk Updates
When performing a bulk update, if you want to specify upsert: true
, you need to use it with Bulk.find.upsert()
.
This can be used with the following write operations:
Bulk.find.replaceOne()
Bulk.find.updateOne()
Bulk.find.update()
The syntax goes like this:
Bulk.find(<query>).upsert().update(<update>);
Bulk.find(<query>).upsert().updateOne(<update>);
Bulk.find(<query>).upsert().replaceOne(<replacement>);
Example:
var bulk = db.pets.initializeUnorderedBulkOp();
bulk.find( { name: "Bruce" } ).upsert().replaceOne(
{
name: "Bruce",
type: "Bat",
}
);
bulk.execute();
Result:
BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 0, "nUpserted" : 1, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ { "index" : 0, "_id" : ObjectId("5fe1c179d9914101694100dd") } ] })
We can see that one document was upserted. We can also see the _id
that was generated for that document.
Now when we view the documents in our collection, we can see the new document that was upserted:
db.pets.find()
Result:
{ "_id" : 1, "name" : "Wag", "type" : "Cow" } { "_id" : 2, "name" : "Bark", "type" : "Dog" } { "_id" : 3, "name" : "Meow", "type" : "Cat" } { "_id" : ObjectId("5fe1b4c8d9914101694100b7"), "name" : "Bubbles", "type" : "Fish" } { "_id" : ObjectId("5fe1c179d9914101694100dd"), "name" : "Bruce", "type" : "Bat" }