Atomic counters are a necessity for a large number of use cases. Mongo doesn't have a built in feature for atomic counters; nevertheless, it can be easily implemented using some of its cool offerings. In fact, with the help of previously described findAndModify()
command, implementing is quite simple. Refer to the previous recipe Atomic find and modify operations to know what atomic find and modify operations are in Mongo.
Look at the recipe Installing single node MongoDB in Chapter 1, Installing and Starting the Server and start a single instance of Mongo. That is the only prerequisite for this recipe. Start a mongo shell and connect to the started server.
> function getNextSequence(counterId) { return db.counters.findAndModify( { query: {_id : counterId}, update: {$inc : {count : 1}}, upsert: true, fields:{count:1, _id:0}, new: true } ).count }
> getNextSequence('Posts Counter') > getNextSequence('Posts Counter') > getNextSequence('Profile Counter')
The function is as simple as a findAndModify
operation on a collection used to store all the counters. The counter identifier is the _id
field of the document stored and the value of the counter is stored in the field count
. The document passed to the findAndModify
operations accepts the query, which uniquely identifies the document storing the current count—a query using the _id
field. The update operation is an $inc
operation that will increment the value of the count
field by 1. But what if the document doesn't exist? This will happen on the first invocation of the counter. To take care of this scenario, we will set the upsert
flag to true
. The value of count
will always start with 1 and there is no way it would accept any user-defined start number for the sequence or a custom increment step. To address such requirements, we will have to specifically add a document with the initialized values to the counters collection. Finally, we are interested in the state of the counter after the value is incremented; hence, we set the value of the field new
as true
.
On invoking this method thrice (as we did), we should see the following in the collection counters. Simply execute the following query:
>db.counters.find() { "_id" : "Posts Counter", "count" : 2 } { "_id" : "Profile Counter", "count" : 1 }
Using this small function, we now have implemented atomic counters in Mongo.
We can store such common code on a Mongo server that would be available for execution in other functions. Look at the recipe Implementing server-side scripts to see how we can store JavaScript functions on the Mongo server. This allows us even to invoke this function from other programming language clients.