MongoDB findOneAndUpdate()

In MongoDB the db.collection.findOneAndUpdate() method updates a single document based on the filter and sort criteria.

The collection part is the name of the collection with which to perform the operation against.

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 can use the db.collection.findOneAndUpdate() method to update one of those documents.

db.pets.findOneAndUpdate(
    { "type": "Dog" },
    { $set: { "type": "Cow" } }
)

Result:

{ "_id" : 1, "name" : "Wag", "type" : "Dog" } 

By default, it returns the original document (not the modified version).

Note that only one dog was updated, even though there are two dogs in the collection.

Let’s check the collection.

db.pets.find()

Result:

{ "_id" : 1, "name" : "Wag", "type" : "Cow" }
{ "_id" : 2, "name" : "Bark", "type" : "Dog" }
{ "_id" : 3, "name" : "Meow", "type" : "Cat" }

We can see that the first document now contains a cow instead of a dog.

Return the Modified Document

By default, the original document is returned when you use db.collection.findOneAndUpdate().

If you’d prefer to have the modified document returned instead, use the returnNewDocument parameter.

Let’s make another modification, but this time we’ll use returnNewDocument: true.

db.pets.findOneAndUpdate(
    { "type": "Dog" },
    { $set: { "type": "Horse" } },
    { returnNewDocument: true }
)

Result:

{ "_id" : 2, "name" : "Bark", "type" : "Horse" }

This time the other dog was updated. In this case, we changed it to a horse, and we can see that the return document reflects this.

Upserts

An upsert is an option that you can use on update operations. If the specified document doesn’t exist, a new one is inserted. If it does exist, then the original document is updated (and no document is inserted).

You can perform upserts by specifying upsert: true.

Example using upsert: false

First, here’s an example of trying to update a non-existent document when upsert: false.

db.pets.findOneAndUpdate(
    { "_id": 4 },
    { $set: { "name": "Barry", "type": "Badger" } },
    { 
        returnNewDocument: true 
    }
)

Result:

null

The document didn’t exist in the collection and so findOneAndUpdate() returned null. Even though we didn’t specify upsert: false, we know it was false because that’s the default value (i.e. that’s the value that’s used when you don’t specify an upsert option).

If we take another look in the collection, we can see that the document wasn’t upserted.

db.pets.find()

Result:

{ "_id" : 1, "type" : "Cow" }
{ "_id" : 2, "name" : "Bark", "type" : "Horse" }
{ "_id" : 3, "name" : "Scratch", "type" : "Cat" }

Example using upsert: true

Now here it is again, but this time we specify upsert: true.

db.pets.findOneAndUpdate(
    { "_id": 4 },
    { $set: { "name": "Barry", "type": "Badger" } },
    { 
        upsert: true,
        returnNewDocument: true 
    }
)

Result:

{ "_id" : 4, "name" : "Barry", "type" : "Badger" }

This time a new document is upserted and we see the upserted document as the output (because we specified returnNewDocument: true).

Let’s check the collection again.

db.pets.find()

Result:

{ "_id" : 1, "name" : "Wag", "type" : "Cow" }
{ "_id" : 2, "name" : "Bark", "type" : "Horse" }
{ "_id" : 3, "name" : "Meow", "type" : "Cat" }
{ "_id" : 4, "name" : "Barry", "type" : "Badger" }

So we can see that the new document was in fact upserted.

The arrayFilters Parameter

When working with arrays, you can use the arrayFilters parameter along with the positional $ operator to determine which array elements to update. This enables you to update an array element based on its value, even if you don’t know its position.

For example, suppose we have a collection called players with the following documents:

{ "_id" : 1, "scores" : [ 1, 5, 3 ] }
{ "_id" : 2, "scores" : [ 8, 17, 18 ] }
{ "_id" : 3, "scores" : [ 15, 11, 8 ] }

We could run the following query to update only those array elements that have a value higher than a certain amount (in this case 10).

db.players.findOneAndUpdate(
   { scores: { $gte: 10 } },
   { $set: { "scores.$[e]" : 10 } },
   {
       arrayFilters: [ { "e": { $gte: 10 } } ]
    }
)

Result:

{ "_id" : 2, "scores" : [ 8, 17, 18 ] }

This shows the document before it was updated. As expected, this only updates one document, even though two documents match the criteria.

Here’s what the documents look like now.

db.players.find()

Result:

{ "_id" : 1, "scores" : [ 1, 5, 3 ] }
{ "_id" : 2, "scores" : [ 8, 10, 10 ] }
{ "_id" : 3, "scores" : [ 15, 11, 8 ] }

Document 2 had two array elements updated, because those elements matched the criteria.

More Information

The db.collection.findOneAndUpdate() method also accepts other parameters, such as projection (to specify a subset of fields to return), sort, maxTimeMS, and collation.

See the MongoDB documentation for db.collections.findOneAndUpdate() for more information.