How to Create an Auto-Increment Sequence in MongoDB

This article explains how to create an auto-increment sequence in MongoDB.

MongoDB does not provide sequences by default, so you need to create them separately in code. This article introduces two simple approaches with examples.

Overview

MongoDB basically handles multiple documents, so arbitrary fields are not incremented automatically. By default, a unique ObjectId key is generated in _id, and that is usually used.

That said, some systems need a unique serial number. This article introduces two ways to issue a unique number.

For both methods, the examples issue a sequence to userid in a document as follows.

  • database: test
  • collection: users
  • document: {userid, name}

Using a counter collection

This method creates a counters collection in the {key: string, seq: number} format. When registering a document that needs a sequence, it reads and updates the maximum value from this counters document to create an auto-increment value.

Prerequisite Create the counters collection. The following example is executed from the mongo command.

> db.counters.createIndex({ key: 1 }, { unique: true })
> db.counters.insertOne({ key: "userid", seq: 0 })

The result is as follows.

> db.counters.find({}, { _id: 0 })
{ "key" : "userid", "seq" : 0 }

Source (index.js)

var MongoClient = require("mongodb").MongoClient;
 
const CONNECTION_STRING = "mongodb://localhost:27017/test";
 
// Issue a sequence by using the sequence collection.
var insertUser = function (userinfo, callback) {
  MongoClient.connect(CONNECTION_STRING).then((db) => {

    // Read the sequence from the counter collection.
    db.collection("counters").findOneAndUpdate(
      { key: "userid" },
      { $inc: { seq: 1 } },
      { upsert: true, returnOriginal: true },
      (err, doc) => {
        // Set the sequence number.
        userinfo.userid = doc.value.seq;
 
        // Register the document.
        db.collection("users").insertOne(userinfo, (err, res) => {
          callback && callback(err, res);
        });
      });
  }).catch((err) => {
    console.log(err.message);
  });
};
 
// Register data.
insertUser({
  name: "kimkc"
});
 
insertUser({
  name: "devkuma"
});

Run

> node ./index.js

Result

> db.users.find({},{_id:0})
{ "name" : "kimkc", "userid" : 0 }
{ "name" : "devkuma", "userid" : 1 }

Using loop processing

This method does not create a sequence collection. Instead, it retrieves the current maximum value from the registered data and uses the next number.

Source (index.js)

var MongoClient = require("mongodb").MongoClient;
 
const CONNECTION_STRING = "mongodb://localhost:27017/test";
 
// Issue a sequence by using loop processing.
var insertUser = function (userinfo, callback) {
  MongoClient.connect(CONNECTION_STRING).then((db) => {
 
    // Loop processing.
    var loop = function () {
      db.collection("users")
        .find({}, { _id: 0, userid: 1 })
        .sort({ userid: -1 })
        .limit(1)
        .toArray((err, docs) => {
          if (err) {
            callback && callback(err, null);
            return;
          }
 
          // Create the sequence.
          var seq = docs.length > 0 ? docs[0].userid + 1 : 0;
 
          // Set the sequence.
          userinfo.userid = seq;
 
          // Register the document.
          db.collection("users").insertOne(userinfo, (err, res) => {
            // If the sequence number is already used, run recursively.
            if (err && err.code === 11000) {
              loop();
 
              return;
            }
            callback && callback(err, res);
          });
        });
    };
 
    // First execution of loop processing.
    loop();
  });
};
 
// Register data.
insertUser({
  name: "kimkc"
});
 
insertUser({
  name: "devkuma"
});

Run:

> node ./index.js

Result:

> db.users.find({}, { _id: 0 })
{ "name" : "kimkc", "userid" : 0 }
{ "name" : "devkuma", "userid" : 1 }

References