Introduction
I have covered the basic queries (select
, insert
and delete
) of MongoDB in my last post, now I will describe here how to update records in MongoDB. Update
records is one the four basic operations CRUD (create
, read
, update
and delete
) for any database we required. MondoDB also provided its update queries, with more sophisticated tasks.
Update
queries function in Mongo are not straight forward like SQL databases. It is a little more enhanced functional component than simple update
. You will find that it facilitates the general operations of routine development tasks, and we could have made our database operations with less complications in the data layer.
Background
Update
queries are capable of doing the following jobs:
- It could replace the entire document (recall, you could think of it as
DataRow
in .NET environment)
- Simply update the records like in simple SQL databases, the number of fields you specify will be updated with the provided condition.
- Provide special
$inc
modifier, if you need to increment or decrement a field's value.
$push
modifier adds a value to the array (recall, you could place entire arrays in your document's fields)
- An upsert updates the document if found or inserts it if not.
Description
In its simplest form, update
takes 2 arguments: the selector (where) to use and what field to update with. If Imran has to change the category he played, we could execute:
db.KarachiTigers.update({name: 'Imran'}, {category: 'Bowler'})
If this was real code, you'd probably update your records by _id
, but since I don't know what _id
MongoDB generated for you, we'll stick to names. Now, if we look at the updated record:
db.KarachiTigers.find({name: 'Imran'})
You should discover updates first surprise. No document is found because the second parameter we supply is used to replace the original. In other words, the update found a document by name and replaced the entire document with the new document (the 2nd parameter). This is different than how SQL's update
command works. In some situations, this is ideal and can be leveraged for some truly dynamic updates. However, when all you want to do is change the value of one, or a few fields, you are best to use MongoDB's $set
modifier:
db.KarachiTigers.update({name: 'Imran'}, {$set: {category: 'Bowler', dob: new Date
(1979, 7, 18, 18, 44), hobbies: ['cricket'], gender: 'm'}})
This will reset the lost fields. It won't overwrite the new category since we didn't specify it. Now if we execute:
db.KarachiTigers.find({name: 'Imran'})
We get the expected result. Therefore, the correct way to have updated the category in the first place is:
db.KarachiTigers.update({name: 'Imran'}, {$set: {category: 'Bowler'}})
In addition to $set
, we can leverage other modifiers to do some interesting things. All of these update modifiers work on fields - so your entire document won't be wiped out. For example, the $inc
modifier is used to increment a field by a certain positive or negative amount. For example, if Amir was incorrectly awarded a wicket, we could correct the mistake by executing:
db.KarachiTigers.update({name: 'Amir'}, {$inc: {wickets: -1}})
If Saeed Ajmal has to update his bowling actions, e.g., he could bowl off-spin and doosra, we could set the array items defined in actions field of the document:
db.KarachiTigers.update({name: 'Saeed Ajmal'}, {$set: {actions: ['off-spin', 'doosra']}})
Let's say, if Saeed Ajmal suddenly developed a teesra bowling action, we could add a value to his actions field via the $push
modifier:
db.KarachiTigers.update({name: 'Saeed Ajmal'}, {$push: {actions: 'teesra'}})
The Updating section of the MongoDB website has more information on the other available update
modifiers.
One of updates more pleasant surprises is that it fully supports upserts
. An upsert
updates the document if found or inserts
it if not. Upserts
are handy to have in certain situations and, when you run into one, you'll know it. To enable upserting, we set a third parameter to true
. An everyday example is a hit counter for a website. If we wanted to keep an aggregate count in real time, we'd have to see if the record already existed for the page, and based on that decide to run an update
or insert
. With the third parameter omitted (or set to false
), executing the following won't do anything:
db.hits.update({page: 'cricket updates'}, {$inc: {hits: 1}});
db.hits.find();
However, if we enable upsert
s, the results are quite different:
db.hits.update({page: 'cricket updates'}, {$inc: {hits: 1}}, true);
db.hits.find();
Since no documents exists with a field page equal to cricket updates, a new document is inserted. If we execute it a second time, the existing document is updated and hits is incremented to 2.
db.hits.update({page: 'cricket updates'}, {$inc: {hits: 1}}, true);
db.hits.find();
A non-upsert
update may or may not modify an existing object. An upsert
will either modify an existing object or insert
a new object. The client may determine if it's most recent message on a connection updated an existing object by subsequently issuing a getlasterror
command (db.runCommand( "getlasterror" )
).
- If the result of the
getlasterror
command contains an updatedExisting
field, the last message on the connection was an update
request.
- If the
updatedExisting
field's value is true
, that update
request caused an existing object to be updated
- If
updatedExisting
is false
, no existing object was updated.
- An "
upserted
" field will contain the new _id
value if an insert
is performed.
The final surprise update
has to offer is that, by default, it'll update
a single document. So far, for the examples we've looked at, this might seem logical. However, if you executed something like:
db.KarachiTigers.update({}, {$set: {qualified: true }});
db.KarachiTigers.find({qualified: true});
You'd likely expect to find all of your team members to be qualified. To get the behavior you desire, a fourth parameter must be set to true
:
db.KarachiTigers.update({}, {$set: {qualified: true }}, false, true);
db.KarachiTigers.find({qualified: true});
Points of Interest
We concluded our introduction to the basic CRUD operations available against a collection. We looked at update
in detail and observed three interesting behaviors. First, unlike an SQL update
, MongoDB's update
replaces the actual document. Because of this, the $set
modifier is quite useful. Secondly, update
supports an intuitive upsert
which is particularly useful when paired with the $inc
modifier. Finally, by default, update
only updates the first found document. Do remember that we are looking at MongoDB from the point of view of its shell. The driver and library you use could alter these default behaviors or expose a different API.
History
- 27th February, 2012: Initial version