News Archive
PhpRiot Newsletter
Your Email Address:

More information

Auto Increment with MongoDB

Note: This article was originally published at Planet PHP on 30 July 2010.
Planet PHP

We are currently working on an app that uses a number of technologies, including PHP, Python, and MongoDB. Recently, a need arose to use sequential identifiers for users, similar to an auto_increment column in MySQL.

If you've used MongoDB, you might be familiar with the default behavior of using a UUID as the primary key. This is convenient, especially if you partition your database across servers, because you don't have to coordinate the primary key in any way. If you use sequential identifiers (as I demonstrate in this post), you can use multiple servers and interleave identifiers by advancing each server's sequence by the total number of servers. (For example, with two servers, advance each sequence by two, so one server generates even identifiers, and the other generates odd.)

I'd rather not discuss the advantages and disadvantages of either approach, because it's exactly this debate that makes it very difficult to find any useful information on using sequential identifiers with MongoDB. Instead, I'm just going to explain how I did it, and hope this is helpful to someone. :-)

First, create a sequence collection that you can use to determine the next identifier in the sequence. The following creates a collection called seq that has a single sequence in it (for users), but you can add as many as you need:

db.seq.insert({"_id":"users", "seq":new NumberLong(1)});

If you assign seq to 1 instead of new NumberLong(1), it will be interpreted as a float due to a JavaScript quirk.

Before adding a new user, you need to increment the sequence by one and fetch the next identifier. Fortunately, the findandmodify command provides an atomic way to do this. Using the MongoDB shell, the command would look something like this:

db.seq.findAndModify({ query: {"_id":"users"}, update: {$inc: {"seq":1}}, new: true });

Because I'm using Lithium, I added a method for fetching the next identifier to my User model:

A namespace app\models; A class User extends \lithium\data\Model { A static public function seq() { $seq = static::_connection()-connection-command(array('findandmodify' = 'seq', 'query' = array('_id' = 'users'), 'update' = array('$inc' = array('seq' = 1)), 'new' = TRUE)); A return $seq

Truncated by Planet PHP, read more at the original (another 2978 bytes)