MongoDB findOneAndReplace()

In MongoDB the db.collection.findOneAndReplace() method replaces a single document based on the specified filter.

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.findOneAndReplace() method to replace one of those documents.

db.pets.findOneAndReplace(
   { "type": "Dog" },
   { "name": "Bruno", "type" : "Horse" }
)

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" : "Bruno", "type" : "Horse" }
{ "_id" : 2, "name" : "Bark", "type" : "Dog" }
{ "_id" : 3, "name" : "Meow", "type" : "Cat" }

We can see that the first document has been replaced with the new document.

Note that the replacement document cannot specify an _id value that differs from the _id value in the replaced document.

Return the Modified Document

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

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.findOneAndReplace(
   { "type": "Dog" },
   { "handle": "Harry", "DOB" : "2020-05-12", "points": 10 },
   { returnNewDocument: true }
)

Result:

{ "_id" : 2, "handle" : "Harry", "DOB" : "2020-05-12", "points" : 10 }

This time the other dog was updated. In this case, we made a more significant change, and we can see that the return document reflects this.

We can check the collection again to see the replaced document.

db.pets.find()

Result:

{ "_id" : 1, "name" : "Bruno", "type" : "Horse" }
{ "_id" : 2, "handle" : "Harry", "DOB" : "2020-05-12", "points" : 10 }
{ "_id" : 3, "name" : "Meow", "type" : "Cat" }

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.findOneAndReplace(
   { "_id": 4 },
   { "name": "Fluffy", "type": "Pooch", "DOB" : "2019-12-03", "points": 20 },
   {
       returnNewDocument: true
    }
)

Result:

null

The document didn’t exist in the collection and so findOneAndReplace() returned null. Even though we didn’t explicitly 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, "name" : "Bruno", "type" : "Horse" }
{ "_id" : 2, "handle" : "Harry", "DOB" : "2020-05-12", "points" : 10 }
{ "_id" : 3, "name" : "Meow", "type" : "Cat" }

Example using upsert: true

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

db.pets.findOneAndReplace(
   { "_id": 4 },
   { "name": "Fluffy", "type": "Pooch", "DOB" : "2019-12-03", "points": 20 },
   {
       upsert: true,
       returnNewDocument: true
    }
)

Result:

{
	"_id" : 4,
	"name" : "Fluffy",
	"type" : "Pooch",
	"DOB" : "2019-12-03",
	"points" : 20
}

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" : "Bruno", "type" : "Horse" }
{ "_id" : 2, "handle" : "Harry", "DOB" : "2020-05-12", "points" : 10 }
{ "_id" : 3, "name" : "Meow", "type" : "Cat" }
{ "_id" : 4, "name" : "Fluffy", "type" : "Pooch", "DOB" : "2019-12-03", "points" : 20 }

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

The sort Parameter

You can use the sort parameter to specify a sorting order for the documents matched by the filter.

When using the sort parameter, a value of 1 sorts the documents into ascending order, and a value of -1 sorts them into descending order.

The argument needs to be provided as a document. For example, { sort: { "salary": 1 } } sorts by the salary field in ascending order.

For example, suppose we create a collection called employees with the following documents:

db.employees.insertMany([
    { _id: 1, name: "Sandy", salary: 55000 },
    { _id: 2, name: "Sarah", salary: 128000 },
    { _id: 3, name: "Fritz", salary: 25000 },
    { _id: 4, name: "Chris", salary: 45000 },
    { _id: 5, name: "Beck", salary: 82000 }
    ])

We could run the following code to find documents with a salary of less than 60000, then replace the one that’s the lowest out of those documents.

db.employees.findOneAndReplace(
   { "salary": { $lt: 60000 } },
   { "name": "Fluffy", "salary": 250000 },
   {
       sort: { "salary": 1 }
    }
)

Result:

{ "_id" : 3, "name" : "Fritz", "salary" : 25000 }

This shows the document before it was updated. As expected, the employee with the lowest salary was replaced with a new salary (and a new name).

Here’s what the documents look like now.

db.employees.find()

Result:

{ "_id" : 1, "name" : "Sandy", "salary" : 55000 }
{ "_id" : 2, "name" : "Sarah", "salary" : 128000 }
{ "_id" : 3, "name" : "Fluffy", "salary" : 250000 }
{ "_id" : 4, "name" : "Chris", "salary" : 45000 }
{ "_id" : 5, "name" : "Beck", "salary" : 82000 }

So we can see that Fritz has been replaced by Fluffy who has a much better salary.

Let’s revert it back to the original document.

db.employees.remove({})
db.employees.insertMany([
    { _id: 1, name: "Sandy", salary: 55000 },
    { _id: 2, name: "Sarah", salary: 128000 },
    { _id: 3, name: "Fritz", salary: 25000 },
    { _id: 4, name: "Chris", salary: 45000 },
    { _id: 5, name: "Beck", salary: 82000 }
    ])

Now let’s run the same findOneAndReplace() code again, but this time we’ll sort it in descending order.

db.employees.findOneAndReplace(
   { "salary": { $lt: 60000 } },
   { "name": "Fluffy", "salary": 250000 },
   {
       sort: { "salary": -1 }
    }
)

Result:

{ "_id" : 1, "name" : "Sandy", "salary" : 55000 }

This time Sandy was replaced.

Let’s check the collection again.

db.employees.find()

Result:

{ "_id" : 1, "name" : "Fluffy", "salary" : 250000 }
{ "_id" : 2, "name" : "Sarah", "salary" : 128000 }
{ "_id" : 3, "name" : "Fritz", "salary" : 25000 }
{ "_id" : 4, "name" : "Chris", "salary" : 45000 }
{ "_id" : 5, "name" : "Beck", "salary" : 82000 }

As expected.

More Information

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

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