In Chapter 2, Command-line Operations and Indexes, we had some recipes that explained various CRUD operations we perform in MongoDB. There was one concept that we didn't cover and it is atomically find and modify documents. Modification consists of both update and delete operations. In this recipe, we will go through the basics of MongoDB's findAndModify
operation. In the next recipe, we will use this method to implement a counter.
Look at the recipe Installing single node MongoDB in Chapter 1, Installing and Starting the Server and start a single instance of MongoDB. That is the only prerequisite for this recipe. Start a mongo shell and connect to the started server.
atomicOperationsTest
collection. Execute the following from the shell:> db.atomicOperationsTest.drop() > db.atomicOperationsTest.insert({i:1})
> db.atomicOperationsTest.findAndModify({ query: {i: 1}, update: {$set : {text : 'Test String'}}, new: false } )
> db.atomicOperationsTest.findAndModify({ query: {i: 1}, update: {$set : {text : 'Updated String'}}, fields: {i: 1, text :1, _id:0}, new: true } )
>db.atomicOperationsTest.findAndModify({ query: {i: 2}, update: {$set : {text : 'Test String'}}, fields: {i: 1, text :1, _id:0}, upsert: true, new: true } )
> db.atomicOperationsTest.find().pretty()
>db.atomicOperationsTest.findAndModify({ query: {i: 2}, remove: true, fields: {i: 1, text :1, _id:0}, new: false } )
If we perform find and update operations independently by first finding the document and then updating it in MongoDB, the results might not be as expected. There might be an interleaving update between the find and the update operations, which may have changed the document state. In some of the specific use cases, like implementing atomic counters, this is not acceptable and thus we need a way to atomically find, update, and return a document. The returned value is either the one before the update is applied or after the update is applied and is decided by the invoking client.
Now that we have executed the steps in the preceding section, let's see what we actually did and what all these fields in the JSON document passed as the parameter to the findAndModify
operation mean. Starting with step 3, we gave a document as a parameter to the findAndModify
function that contains the fields query
, update
, and new
.
The query
field specifies the search parameters that would be used to find the document and the update
field contains the modifications that need to be applied. The third field, new, if set to true
, tells MongoDB to return the updated document.
In step 4, we actually added a new field to the document passed as a parameter called fields that is used to select a limited set of fields from the result document returned. Also, the value of the field new is true
, which tells that we want the updated document that is, the one after the update operation is executed and not the one before.
In step 5 contains a new field called upsert
, which upserts (update + insert) the document. That is, if the document with the given query is found, it is updated else a new one is created and updated. If the document didn't exist and an upsert happened, having the value of the parameter new
as false
will return null
. This is because there was nothing present before the update operation was executed.
Finally, in step 7, instead of the update
field, we used the remove
field with the value true
indicating that the document is to be removed. Also, the value of the new field is false
, which means that we expect the document that got deleted.