Automatically increase document number in Mongo / Mongoose

There are several users in my application, each user has documents. Each document should have a serial number, which can look something like this: 2013-1, 2013-2 (year and serial number) or, possibly, a prime number: 1, 2, 3 ...

I am currently assigning a sequence number from user preferences when creating a Mongoose document. Based on this serial number and number format from user settings, I create the final document number.

I realized that when 2 documents are created at the same time, they will receive exactly the same number, because I increase the sequence number in the settings immediately after saving the document. But I assign a sequence number when I create (not yet save) a document, so the sequence number will be the same for both documents.

I obviously need a way to handle this serial number with automatic increment at the time of saving ...

How can I assure that this number is unique and automatically incremented / generated?

+8
mongodb mongoose
source share
4 answers

@emre and @WiredPraire pointed me in the right direction, but I wanted to provide a complete answer to my question using Mongoose. I ended up with the following solution:

var Settings = new Schema({ nextSeqNumber: { type: Number, default: 1 } }); var Document = new Schema({ _userId: { type: Schema.Types.ObjectId, ref: "User" }, number: { type: String } }); // Create a compound unique index over _userId and document number Document.index({ "_userId": 1, "number": 1 }, { unique: true }); // I make sure this is the last pre-save middleware (just in case) Document.pre('save', function(next) { var doc = this; // You have to know the settings_id, for me, I store it in memory: app.current.settings.id Settings.findByIdAndUpdate( settings_id, { $inc: { nextSeqNumber: 1 } }, function (err, settings) { if (err) next(err); doc.number = settings.nextSeqNumber - 1; // substract 1 because I need the 'current' sequence number, not the next next(); }); }); 

Note that with this method there is no way to require the number path in the circuit, and there is no point, because it is automatically added.

+11
source share

You can achieve this through:

  • create a sequence generator, which is another document that stores the counter of the last number.
  • Use the mongoose middleware to update the automatic increase in the desired field.

Here is an example of working and testing with the todo application.

 var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/todoApp'); // Create a sequence function sequenceGenerator(name){ var SequenceSchema, Sequence; SequenceSchema = new mongoose.Schema({ nextSeqNumber: { type: Number, default: 1 } }); Sequence = mongoose.model(name + 'Seq', SequenceSchema); return { next: function(callback){ Sequence.find(function(err, data){ if(err){ throw(err); } if(data.length < 1){ // create if doesn't exist create and return first Sequence.create({}, function(err, seq){ if(err) { throw(err); } callback(seq.nextSeqNumber); }); } else { // update sequence and return next Sequence.findByIdAndUpdate(data[0]._id, { $inc: { nextSeqNumber: 1 } }, function(err, seq){ if(err) { throw(err); } callback(seq.nextSeqNumber); }); } }); } }; } // sequence instance var sequence = sequenceGenerator('todo'); var TodoSchema = new mongoose.Schema({ name: String, completed: Boolean, priority: Number, note: { type: String, default: '' }, updated_at: { type: Date, default: Date.now } }); TodoSchema.pre('save', function(next){ var doc = this; // get the next sequence sequence.next(function(nextSeq){ doc.priority = nextSeq; next(); }); }); var Todo = mongoose.model('Todo', TodoSchema); 

You can check it in node console as follows

 function cb(err, data){ console.log(err, data); } Todo.create({name: 'hola'}, cb); Todo.find(cb); 

With each newly created object, you will see an increase in priority. Hooray!

+3
source share

This code is taken from the MongoDB manual, and it actually describes how to automatically increase the number of _id fields. However, it can be applied to any field. You want to check if the pasted value exists in the database right after you pasted your document. If it is already inserted, increase the value again and then try again. This way you can detect duplicate values ​​and re-increment them.

 while (1) { var cursor = targetCollection.find( {}, { f: 1 } ).sort( { f: -1 } ).limit(1); var seq = cursor.hasNext() ? cursor.next().f + 1 : 1; doc.f = seq; targetCollection.insert(doc); var err = db.getLastErrorObj(); if( err && err.code ) { if( err.code == 11000 /* dup key */ ) continue; else print( "unexpected error inserting data: " + tojson( err ) ); } break; } 

In this example, f is the field in your document that you want to automatically increase. To make this work, you need to make your own UNIQUE field, which can be done using indexes.

 db.myCollection.ensureIndex( { "f": 1 }, { unique: true } ) 
+2
source share

You can use the mongoose-auto-increment package as follows:

 var mongoose = require('mongoose'); var autoIncrement = require('mongoose-auto-increment'); /* connect to your database here */ /* define your DocumentSchema here */ autoIncrement.initialize(mongoose.connection); DocumentSchema.plugin(autoIncrement.plugin, 'Document'); var Document = mongoose.model('Document', DocumentSchema); 

You only need to initialize autoIncrement once.

+2
source share

All Articles