est
est

Reputation: 11875

Auto increment in MongoDB to store sequence of Unique User ID

I am making a analytics system, the API call would provide a Unique User ID, but it's not in sequence and too sparse.

I need to give each Unique User ID an auto increment id to mark a analytics datapoint in a bitarray/bitset. So the first user encounters would corresponding to the first bit of the bitarray, second user would be the second bit in the bitarray, etc.

So is there a solid and fast way to generate incremental Unique User IDs in MongoDB?

Upvotes: 55

Views: 203565

Answers (11)

JAIHIND KUSHWAHA
JAIHIND KUSHWAHA

Reputation: 1

EmployeeSchema.pre("save", function (next) {
  var docs = this;
  model("Employee", EmployeeSchema)
    .find()
    .sort({ $natural: -1 })
    .limit(1)
    .exec()
    .then((result) => {
      docs.uuid = result[0].uuid + 1;
      next();
    });
});

Upvotes: 0

Mohammad Rahimi
Mohammad Rahimi

Reputation: 21

MongoDB provides an _id field for every document which is automatically generated by default. This field is designed to be unique and may be able to be used instead of an auto incremental id field.

If you need an auto incremental id field, you can use a sequence collection to generate unique ids. The sequence collection contains a document that stores the current value of the sequence. Whenever a new document is inserted, the application fetches the next value from the sequence collection and assigns it as the _id of the new document.

To ensure that the sequence is incremented atomically, you can use the findAndModify command with the $inc operator to increment the sequence value and return the updated value in a single atomic operation. This will prevent race conditions that may occur when multiple threads try to increment the sequence at the same time.

Upvotes: 0

Amir Mehrnam
Amir Mehrnam

Reputation: 550

I used something like nested queries in MySQL to simulate auto increment, which worked for me. To get the latest id and increment one to it you can use:

lastContact = db.contacts.find().sort({$natural:-1}).limit(1)[0];
db.contacts.insert({
    "id":lastContact ?lastContact ["id"] + 1 : 1, 
    "name":"John Doe",
    "emails": ["[email protected]", "[email protected]"], 
    "phone":"555111322",
    "status":"Active"
})

It solves the removal issue of Alex's answer. So no duplicate id will appear if any record is removed.

More explanation: I just get the id of the latest inserted document, add one to it, and then set it as the id of the new record. And ternary is for cases that we don't have any records yet or all of the records are removed.

Upvotes: 2

Anand
Anand

Reputation: 1

// First check the table length

const data = await table.find()
if(data.length === 0){
   const id = 1
   // then post your query along with your id  
}
else{
   // find last item and then its id
   const length = data.length
   const lastItem = data[length-1]
   const lastItemId = lastItem.id // or { id } = lastItem
   const id = lastItemId + 1
   // now apply new id to your new item
   // even if you delete any item from middle also this work
}

Upvotes: 0

Alex Nicholas
Alex Nicholas

Reputation: 352

I know this is an old question, but I shall post my answer for posterity...

It depends on the system that you are building and the particular business rules in place.

I am building a moderate to large scale CRM in MongoDb, C# (Backend API), and Angular (Frontend web app) and found ObjectId utterly terrible for use in Angular Routing for selecting particular entities. Same with API Controller routing.

The suggestion above worked perfectly for my project.

db.contacts.insert({
 "id":db.contacts.find().Count()+1,
 "name":"John Doe",
 "emails":[
    "[email protected]",
    "[email protected]"
 ],
 "phone":"555111322",
 "status":"Active"
});

The reason it is perfect for my case, but not all cases is that as the above comment states, if you delete 3 records from the collection, you will get collisions.

My business rules state that due to our in house SLA's, we are not allowed to delete correspondence data or clients records for longer than the potential lifespan of the application I'm writing, and therefor, I simply mark records with an enum "Status" which is either "Active" or "Deleted". You can delete something from the UI, and it will say "Contact has been deleted" but all the application has done is change the status of the contact to "Deleted" and when the app calls the respository for a list of contacts, I filter out deleted records before pushing the data to the client app.

Therefore, db.collection.find().count() + 1 is a perfect solution for me...

It won't work for everyone, but if you will not be deleting data, it works fine.

Edit

latest versions of pymongo:

db.contacts.count() + 1

Upvotes: 22

Lucas Pedroso
Lucas Pedroso

Reputation: 21

// await collection.insertOne({ autoIncrementId: 1 });
const { value: { autoIncrementId } } = await collection.findOneAndUpdate(
  { autoIncrementId: { $exists: true } },
  {
    $inc: { autoIncrementId: 1 },
  },
);
return collection.insertOne({ id: autoIncrementId, ...data });

Upvotes: 1

Murat Akdeniz
Murat Akdeniz

Reputation: 434

this could be another approach

const mongoose = require("mongoose");

const contractSchema = mongoose.Schema(
  {
    account: {
      type: mongoose.Schema.Types.ObjectId,
      required: true,
    },
    idContract: {
      type: Number,
      default: 0,
    },
  },
  { timestamps: true }
);

contractSchema.pre("save", function (next) {
  var docs = this;
  mongoose
    .model("contract", contractSchema)
    .countDocuments({ account: docs.account }, function (error, counter) {
      if (error) return next(error);
      docs.idContract = counter + 1;
      next();
    });
});

module.exports = mongoose.model("contract", contractSchema);

Upvotes: 0

Marian
Marian

Reputation: 2988

I had a similar issue, namely I was interested in generating unique numbers, which can be used as identifiers, but doesn't have to. I came up with the following solution. First to initialize the collection:

fun create(mongo: MongoTemplate) {
        mongo.db.getCollection("sequence")
                .insertOne(Document(mapOf("_id" to "globalCounter", "sequenceValue" to 0L)))
    }

An then a service that return unique (and ascending) numbers:

@Service
class IdCounter(val mongoTemplate: MongoTemplate) {

    companion object {
        const val collection = "sequence"
    }

    private val idField = "_id"
    private val idValue = "globalCounter"
    private val sequence = "sequenceValue"

    fun nextValue(): Long {
        val filter = Document(mapOf(idField to idValue))
        val update = Document("\$inc", Document(mapOf(sequence to 1)))
        val updated: Document = mongoTemplate.db.getCollection(collection).findOneAndUpdate(filter, update)!!
        return updated[sequence] as Long
    }
}

I believe that id doesn't have the weaknesses related to concurrent environment that some of the other solutions may suffer from.

Upvotes: 1

Konstantin Pribluda
Konstantin Pribluda

Reputation: 12367

You can, but you should not https://web.archive.org/web/20151009224806/http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/

Each object in mongo already has an id, and they are sortable in insertion order. What is wrong with getting collection of user objects, iterating over it and use this as incremented ID? Er go for kind of map-reduce job entirely

Upvotes: 31

Darshan Vithani
Darshan Vithani

Reputation: 49

First Record should be add

"_id" = 1    in your db

$database = "demo";
$collections ="democollaction";
echo getnextid($database,$collections);

function getnextid($database,$collections){

     $m = new MongoClient();
    $db = $m->selectDB($database);
    $cursor = $collection->find()->sort(array("_id" => -1))->limit(1);
    $array = iterator_to_array($cursor);

    foreach($array as $value){



        return $value["_id"] + 1;

    }
 }

Upvotes: 1

expert
expert

Reputation: 30135

As selected answer says you can use findAndModify to generate sequential IDs.

But I strongly disagree with opinion that you should not do that. It all depends on your business needs. Having 12-byte ID may be very resource consuming and cause significant scalability issues in future.

I have detailed answer here.

Upvotes: 36

Related Questions