Working with MongoIds and auto-incrementing ids in MongoDB

Working with MongoIds and auto-incrementing ids in MongoDB

In this blog post I will explain how to work with both MongoIds and auto-incrementing ids in MongoDb in your PHP application. MongoDB assigns a special MongoID hash to each inserted document.

Mongo makes sure that the hash is unique and its structure is 24 symbols that looks like this: “562625cd181f4621618b4570”. This is a very convenient way, but not very human readable.
In some cases we would like to relate our documents by a more human readable id, like in the SQL based databases – a simple integer, for example “432”.
This was the case when one of our clients requested to add a numeric, SQL style ids to some of the existing collections. The problem was that the product has been already in production. In order to not break a working system, we had to add support for both MongoIds and SQL style auto-incrementing ids. I will explain the changes we made in our code to support this new feature. The code is written in PHP.

The changes I had to add were in the “create” and “get” functions. But first of all, we had to implement a getNextSequence function to support the running ids feature. The function was implemented as advised by Mongo official blog.
Don’t worry about race conditions, as the implementation is safe, and the findAndModify operation is atomic.

For the ones who are not keen reading the official Mongo blog, here is a brief summary:

    1. Create new “ids” collection (or whatever collection name you like) that will store and manage running ids for all the collections that support the running id feature.
      Here is our live example for the customers collection:
      customer id example
    2. Add a document for each collection you want to support running ids.
    3. Set the ‘_id’ field of the documents to something you can identify for a future use. In our example it’s “customerId”.
    4. Set the “seq” field to 0. ‘seq’ field stores the last inserted document id.
    5. Now just increment the ‘seq’ field every time you add a new document into collection.

 

Full getNextSequence function implementation:

    public function getNextSequence($sequenceIdName)
    {
        $query = array('_id' => $sequenceIdName);
        $update = array(
            '$inc' => array(
                'seq' => 1
            )
        );
        $sequence = null;
        try
        {
            $retVal = $this->_mongo->ids->findAndModify(
                $query,
                $update,
                null,
                array('new' => true)
            );
        }
        catch(Exception $ex)
        {
            log('MongoModel: getNextSequence - Error getting next sequence for  ' . $sequenceIdName . '. ' . $ex->getCode() . ': ' . $ex->getMessage(), Pvp_Log::ERR);
            throw new Exception("Get next sequence failed", $ex->getCode());
        }
        if(isset($retVal['seq']) && $retVal['seq'])
        {
            $sequence =  $retVal['seq'];
        }
        return $sequence;
    }

Now you need to call the getNextSequence function on every object creation in order to get the next id:

$id = $this->getNextSequence('customerId');

 

Support both MongoId and running ids

If you need to support both MongoIds and the auto-incrementing (running) ids, you need to add new filters for your “get” and “list” functions. Here is my implementation:

   //  Filter for GET operations
   public function getIdFilter($id)
    {
        $filter = array();
        if(MongoId::isValid($id))
        {
            $filter['_id'] =  new MongoId($id);
        }
        else if($id && is_numeric($id))
        {
            $filter['id'] = (int)$id;
        }
        else
        {
            $filter['id'] = $id;
        }
        return $filter;
    }
   //  Filter for LIST operations
    public function getIdsFilter($ids)
    {
        $mongoIds = array();
        $runningIds = array();
        foreach($ids as $id)
        {
            if(MongoId::isValid($id))
            {
                $mongoIds[] = new MongoId($id);
            }
            else if(is_numeric($id))
            {
                $runningIds[] = (int)$id;
            }
            else
            {
                $runningIds[] = $id;
            }
        }
        $idsFilter =  array(
            '$or' => array(
                array(
                    '_id' => array(
                        '$in' => $mongoIds
                    )
                ),
                array(
                    'id' => array(
                        '$in' => $runningIds
                    )
                )
            )
        );
        return $idsFilter;
    }

That’s it. Now you can use both MongoIds and auto-incrementing (running) ids for your REST actions in your PHP application.

If you have found a better way of implementing this feature, have any questions, or just want to say hi, feel free to email to: info@panda-os.com. Or just leave a comment here 🙂
Take a look at the the demo which uses MongoDB with running-id implementation.
Follow us on Facebook.

2 thoughts on “Working with MongoIds and auto-incrementing ids in MongoDB

Leave a Reply

Your email address will not be published. Required fields are marked *