findAndModify Command

Find and Modify (or Remove)

v1.3.0 and higher

MongoDB 1.3+ supports a "find, modify, and return" command.  This command can be used to atomically modify a document (at most one) and return it. Note that, by default, the document returned will not include the modifications made on the update.

If you don't need to return the document, you can use Update (which can affect multiple documents, as well).

The general form is

db.runCommand( { findAndModify : <collection>,
                 <options> } )

The MongoDB shell includes a helper method, findAndModify(), for executing the command. Some drivers provide helpers also.

At least one of the update or remove parameters is required; the other arguments are optional.

Argument Description Default
query a filter for the query {}
sort if multiple docs match, choose the first one in the specified sort order as the object to manipulate {}
remove set to a true to remove the object before returning
N/A
update a modifier object N/A
new set to true if you want to return the modified object rather than the original. Ignored for remove. false
fields see Retrieving a Subset of Fields (1.5.0+) All fields
upsert create object if it doesn't exist; a query must be supplied! examples (1.5.4+) false

The sort option is useful when storing queue-like data. Let's take the example of fetching the highest priority job that hasn't been grabbed yet and atomically marking it as grabbed:

> db.jobs.save( {
     name: "Next promo",
     inprogress: false, priority:0,
     tasks : [ "select product", "add inventory", "do placement"]
} );

> db.jobs.save( {
     name: "Biz report",
     inprogress: false, priority:1,
     tasks : [ "run sales report", "email report" ]
} );

> db.jobs.save( {
     name: "Biz report",
     inprogress: false, priority:2,
     tasks : [ "run marketing report", "email report" ]
} );
> job = db.jobs.findAndModify({
     query: {inprogress: false, name: "Biz report"},
     sort : {priority:-1},
     update: {$set: {inprogress: true, started: new Date()}},
     new: true
});

{
	"_id" : ...,
	"inprogress" : true,
	"name" : "Biz report",
	"priority" : 2,
	"started" : "Mon Oct 25 2010 11:15:07 GMT-0700 (PDT)",
	"tasks" : [
		"run marketing report",
		"email report"
	]
}

You can pop an element from an array for processing and update in a single atomic operation:

> task = db.jobs.findAndModify({
     query: {inprogress: false, name: "Next promo"},
     update : {$pop : { tasks:-1}}, fields: {tasks:1},
     new: false } )
{
	"_id" : ...,
	"tasks" : [
		"select product",
		"add inventory",
		"do placement"
	]
}

> db.jobs.find( { name : "Next promo"} )
{
        "_id" : ...,
        "inprogress" : false,
        "name" : "Next promo",
        "priority" : 0,
        "tasks" : [ "add inventory", "do placement" ]
}

You can also simply remove the object to be returned.

> job = db.jobs.findAndModify( {sort:{priority:-1}, remove:true} );
{
	"_id" : ...,
	"inprogress" : true,
	"name" : "Biz report",
	"priority" : 2,
	"started" : "Mon Oct 25 2010 10:44:15 GMT-0700 (PDT)",
	"tasks" : [
		"run marketing report",
		"email report"
	]
}

> db.jobs.find()
{
       "_id" : ...,
       "inprogress" : false,
       "name" : "Next promo",
       "priority" : 0,
       "tasks" : [ "add inventory", "do placement" ]
}
{
       "_id" : ...,
       "name" : "Biz report",
       "inprogress" : false,
       "priority" : 1,
       "tasks" : [ "run sales report", "email report" ]
}

If the client crashes before processing the job or task in the above examples, the data will be lost forever.

See the tests for more examples.

If your driver doesn't provide a helper function for this command, run the command directly with something like this:

job = db.runCommand({ findAndModify : "jobs",
                      sort : { priority : -1 },
                      remove : true
                    }).value;

Sharding limitations

findandmodify will behave the same when called through a mongos as long as the collection it is modifying is unsharded. If the collection is sharded, then the query must contain the shard key.

See Also

Follow @mongodb

MongoDB Pittsburgh - May 15
MongoNYC - May 23
MongoDB Paris - Jun 14
MongoDB UK - Jun 20
MongoDC - June 26


Labels

queues queues Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

PLEASE POST QUESTIONS IN THE USER GROUPS FORUM. Post non-question comments and helpful hints here.

blog comments powered by Disqus