From 5b825146425969368bdfdf7017bc40a57562f34f Mon Sep 17 00:00:00 2001 From: Bhanu Pratap Chaudhary Date: Fri, 8 Mar 2019 03:30:10 +0530 Subject: [PATCH 1/6] added fix to allow increase in scope without reseting counter --- incrementScopePrep.js | 26 ++++++ lib/sequence.js | 205 ++++++++++++++++++++++++++++++------------ 2 files changed, 172 insertions(+), 59 deletions(-) create mode 100644 incrementScopePrep.js diff --git a/incrementScopePrep.js b/incrementScopePrep.js new file mode 100644 index 0000000..42e5dba --- /dev/null +++ b/incrementScopePrep.js @@ -0,0 +1,26 @@ +const MongoClient = require('mongodb').MongoClient; +const url = "mongodb://localhost:27017"; +const dbName = 'test'; +const CounterCollection = "counters"; + +const id = 'member_id'; +const suffix = '_old'; + +try { + MongoClient.connect(url, async(err, client) => { + if (err) console.log(err); + + const db = client.db(dbName); + const Counter = db.collection(CounterCollection); + + const counters = await Counter.find({id}).toArray(); + + for (let index = 0; index < counters.length; index++) { + const counter = counters[index]; + await Counter.update({_id: counter._id}, {$set: {id : `${counter.id}_${suffix}`}}); + } + console.log('done') + }); +} catch (error) { + console.log(error) +} diff --git a/lib/sequence.js b/lib/sequence.js index e85c888..f5b53d6 100644 --- a/lib/sequence.js +++ b/lib/sequence.js @@ -18,7 +18,7 @@ var _ = require('lodash'), * @throws {Error} If id is missing for counter which referes other fields * @throws {Error} If A counter collide with another because of same id */ -Sequence = function(schema, options) { +Sequence = function (schema, options) { var defaults = { id: null, inc_field: '_id', @@ -61,12 +61,12 @@ Sequence = function(schema, options) { * * @static */ -Sequence.getInstance = function(schema, options) { +Sequence.getInstance = function (schema, options) { var sequence = new Sequence(schema, options), id = sequence.getId(); - if (sequenceArchive.existsSequence(id)){ - throw new Error('Counter already defined for field "'+id+'"'); + if (sequenceArchive.existsSequence(id)) { + throw new Error('Counter already defined for field "' + id + '"'); } sequence.enable(); sequenceArchive.addSequence(id, sequence); @@ -78,7 +78,7 @@ Sequence.getInstance = function(schema, options) { * * @method enable */ -Sequence.prototype.enable = function(){ +Sequence.prototype.enable = function () { this._counterModel = this._createCounterModel(); this._createSchemaKeys(); @@ -96,7 +96,7 @@ Sequence.prototype.enable = function(){ * @method getId * @return {String} The id of the sequence */ -Sequence.prototype.getId = function() { +Sequence.prototype.getId = function () { return this._options.id; }; @@ -106,18 +106,26 @@ Sequence.prototype.getId = function() { * * @method _getCounterReferenceField * @param {object} doc A mongoose document + * @param {Boolean} incrScope If set, returns reference fields, used to find old counte * @return {Array} An array of strings which represent the value of the * reference */ -Sequence.prototype._getCounterReferenceField = function(doc) { +Sequence.prototype._getCounterReferenceField = function (doc, incrScope) { var reference = {}; + if (incrScope) { + for (var i in this._options.old_reference_fields) { + reference[this._options.reference_fields[i]] = doc[this._options.reference_fields[i]]; + } + return reference; + } + if (this._useReference === false) { reference = null; } else { for (var i in this._options.reference_fields) { reference[this._options.reference_fields[i]] = doc[this._options.reference_fields[i]]; - // reference.push(JSON.stringify(doc[this._options.reference_fields[i]])); + // reference.push(JSON.stringify(doc[this._options.reference_fields[i]])); } } @@ -129,7 +137,7 @@ Sequence.prototype._getCounterReferenceField = function(doc) { * * @method _createSchemaKeys */ -Sequence.prototype._createSchemaKeys = function() { +Sequence.prototype._createSchemaKeys = function () { var schemaKey = this._schema.path(this._options.inc_field); if (_.isUndefined(schemaKey)) { var fieldDesc = {}; @@ -148,29 +156,41 @@ Sequence.prototype._createSchemaKeys = function() { * @method _createCounterModel * @return {Mongoose~Model} A mongoose model */ -Sequence.prototype._createCounterModel = function() { +Sequence.prototype._createCounterModel = function () { var CounterSchema; - CounterSchema = mongoose.Schema( - { - id: {type: String, required: true}, - reference_value: { type: mongoose.Schema.Types.Mixed, required: true}, - seq: {type:Number, default: 0, required: true} + CounterSchema = mongoose.Schema({ + id: { + type: String, + required: true }, - { - collection: this._options.collection_name, - validateBeforeSave: false, - versionKey: false, - _id: false + reference_value: { + type: mongoose.Schema.Types.Mixed, + required: true + }, + seq: { + type: Number, + default: 0, + required: true } - ); + }, { + collection: this._options.collection_name, + validateBeforeSave: false, + versionKey: false, + _id: false + }); - CounterSchema.index({id: 1, reference_value: 1}, {unique: true}); + CounterSchema.index({ + id: 1, + reference_value: 1 + }, { + unique: true + }); - /* Unused. Enable when is useful */ - // CounterSchema.static('getNext', function(id, referenceValue, callback) { - // this.findOne({ id: id, reference_value: referenceValue }, callback); - // }); + /* Unused. Enable when is useful */ + // CounterSchema.static('getNext', function(id, referenceValue, callback) { + // this.findOne({ id: id, reference_value: referenceValue }, callback); + // }); return mongoose.model('Counter_' + this._options.id, CounterSchema); }; @@ -180,15 +200,15 @@ Sequence.prototype._createCounterModel = function() { * * @method _setHooks */ -Sequence.prototype._setHooks = function() { - this._schema.pre('save', true, (function(sequence){ - return function(next, done) { +Sequence.prototype._setHooks = function () { + this._schema.pre('save', true, (function (sequence) { + return function (next, done) { var doc = this; next(); if (!doc.isNew) { return done(); } - sequence._setNextCounter(doc, function(err, seq) { + sequence._setNextCounter(doc, function (err, seq) { if (err) return done(err); doc[sequence._options.inc_field] = seq; done(); @@ -202,43 +222,49 @@ Sequence.prototype._setHooks = function() { * * @method _setMethods */ -Sequence.prototype._setMethods = function() { - // this._schema.static('getNext', function(id, referenceValue, callback) { - // this._counterModel.getNext(id, referenceValue, function(err, counter) { - // if (err) return callback(err); - // return callback(null, ++counter.seq); - // }); - // }.bind(this)); - - this._schema.method('setNext', function(id, callback) { +Sequence.prototype._setMethods = function () { + // this._schema.static('getNext', function(id, referenceValue, callback) { + // this._counterModel.getNext(id, referenceValue, function(err, counter) { + // if (err) return callback(err); + // return callback(null, ++counter.seq); + // }); + // }.bind(this)); + + this._schema.method('setNext', function (id, callback) { var sequence = sequenceArchive.getSequence(id); if (_.isNull(sequence)) { return callback(new Error('Trying to increment a wrong sequence using the id ' + id)); } - // sequence = sequence.sequence; + // sequence = sequence.sequence; - sequence._setNextCounter(this, function(err, seq) { + sequence._setNextCounter(this, function (err, seq) { if (err) return callback(err); this[sequence._options.inc_field] = seq; this.save(callback); }.bind(this)); }); - this._schema.static('counterReset', function(id, reference, callback) { + this._schema.static('counterReset', function (id, reference, callback) { var sequence = sequenceArchive.getSequence(id); sequence._resetCounter(id, reference, callback); }.bind(this)); }; -Sequence.prototype._resetCounter = function(id, reference, callback){ - var condition = { id: id }; +Sequence.prototype._resetCounter = function (id, reference, callback) { + var condition = { + id: id + }; if (reference instanceof Function) { callback = reference; } else { condition.reference_value = this._getCounterReferenceField(reference); } - this._counterModel.updateMany(condition, {$set: {seq:0}}, null, callback); + this._counterModel.updateMany(condition, { + $set: { + seq: 0 + } + }, null, callback); }; /** @@ -249,31 +275,92 @@ Sequence.prototype._resetCounter = function(id, reference, callback){ * increment * @param {Function} callback Called with the sequence counter */ -Sequence.prototype._setNextCounter = function(doc, callback) { - - var retriable = function(callback) { - var id = this.getId(); - var referenceValue = this._getCounterReferenceField(doc); - this._counterModel.findOneAndUpdate( - { id: id, reference_value: referenceValue }, - { $inc: { seq: 1 } }, - { new: true, upsert: true }, - function(err, counter) { +Sequence.prototype._setNextCounter = function (doc, callback) { + + var retriable = function (callback) { + var that = this; + var id = that.getId(); + var referenceValue = that._getCounterReferenceField(doc); + if (that._options.old_reference_fields) { + that._counterModel.findOne({ + id: id, + reference_value: referenceValue + }, function (err, counter) { + if (err) return callback(err); + if (counter) { + that._counterModel.findOneAndUpdate({ + id: id, + reference_value: referenceValue + }, { + $inc: { + seq: 1 + } + }, { + new: true, + upsert: true, + passRawResult: true + }, + function (err, counter) { if (err) return callback(err); return callback(null, counter.seq); } - - ); + ); + } else { + const oldReferenceID = `${id}_${ that._options.old_ref_field_suffix|| 'old'}`; + that._counterModel.findOne({ + id: oldReferenceID, + reference_value: that._getCounterReferenceField(doc, true) + }, function (err, counter) { + if (err) return callback(err); + if (!counter) return callback(new Error('Old Counter not found')); + that._counterModel.findOneAndUpdate({ + id: id, + reference_value: that._getCounterReferenceField(doc) + }, { + seq: counter.seq + 1 + }, { + new: true, + upsert: true, + passRawResult: true + }, + function (err, counter) { + if (err) return callback(err); + return callback(null, counter.seq); + } + ); + }); + + } + }); + } else { + that._counterModel.findOneAndUpdate({ + id: id, + reference_value: referenceValue + }, { + $inc: { + seq: 1 + } + }, { + new: true, + upsert: true, + passRawResult: true + }, + function (err, counter) { + if (err) return callback(err); + return callback(null, counter.seq); + } + ); + } }; async.retry(0, retriable.bind(this), callback); }; -module.exports = function(goose){ +module.exports = function (goose) { if (arguments.length !== 1) { throw new Error('Please, pass mongoose while requiring mongoose-sequence: https://github.com/ramiel/mongoose-sequence#requiring'); } mongoose = goose; return Sequence.getInstance; -}; +}; \ No newline at end of file From 72c4711402727931548be96d03b10d5efda7ebd2 Mon Sep 17 00:00:00 2001 From: Bhanu Pratap Chaudhary Date: Fri, 8 Mar 2019 13:58:37 +0530 Subject: [PATCH 2/6] lint and minor fix --- lib/sequence.js | 331 +++++++++++++++++++++--------------------------- 1 file changed, 142 insertions(+), 189 deletions(-) diff --git a/lib/sequence.js b/lib/sequence.js index f5b53d6..15825e1 100644 --- a/lib/sequence.js +++ b/lib/sequence.js @@ -6,19 +6,19 @@ var _ = require('lodash'), Sequence; /** - * Sequence plugin constructor - * @class Sequence - * @param {string} schema the schema object - * @param {object} options A set of options for this plugin - * @param {string} [options.inc_field='_id'] The field to increment - * @param {string} [options.id='same as inc_field'] The id of this sequence. Mandatory only if the sequence use reference fields - * @param {string|string[]} [options.reference_fields=['_id']] Any field to consider as reference for the counter - * @param {boolean} [options.disable_hooks] If true any hook will be disabled - * @param {string} [options.collection_name='counters'] A name for the counter collection - * @throws {Error} If id is missing for counter which referes other fields - * @throws {Error} If A counter collide with another because of same id - */ -Sequence = function (schema, options) { +* Sequence plugin constructor +* @class Sequence +* @param {string} schema the schema object +* @param {object} options A set of options for this plugin +* @param {string} [options.inc_field='_id'] The field to increment +* @param {string} [options.id='same as inc_field'] The id of this sequence. Mandatory only if the sequence use reference fields +* @param {string|string[]} [options.reference_fields=['_id']] Any field to consider as reference for the counter +* @param {boolean} [options.disable_hooks] If true any hook will be disabled +* @param {string} [options.collection_name='counters'] A name for the counter collection +* @throws {Error} If id is missing for counter which referes other fields +* @throws {Error} If A counter collide with another because of same id +*/ +Sequence = function(schema, options) { var defaults = { id: null, inc_field: '_id', @@ -51,22 +51,22 @@ Sequence = function (schema, options) { }; /** - * Create an instance for a sequence - * - * @method getInstance - * @param {Object} schema A mongoose Schema - * @param {object} options Options as accepted by A sequence - * constructor - * @return {Sequence} A sequence - * - * @static - */ -Sequence.getInstance = function (schema, options) { +* Create an instance for a sequence +* +* @method getInstance +* @param {Object} schema A mongoose Schema +* @param {object} options Options as accepted by A sequence +* constructor +* @return {Sequence} A sequence +* +* @static +*/ +Sequence.getInstance = function(schema, options) { var sequence = new Sequence(schema, options), id = sequence.getId(); - if (sequenceArchive.existsSequence(id)) { - throw new Error('Counter already defined for field "' + id + '"'); + if (sequenceArchive.existsSequence(id)){ + throw new Error('Counter already defined for field "'+id+'"'); } sequence.enable(); sequenceArchive.addSequence(id, sequence); @@ -74,11 +74,11 @@ Sequence.getInstance = function (schema, options) { }; /** - * Enable the sequence creating all the necessary models - * - * @method enable - */ -Sequence.prototype.enable = function () { +* Enable the sequence creating all the necessary models +* +* @method enable +*/ +Sequence.prototype.enable = function(){ this._counterModel = this._createCounterModel(); this._createSchemaKeys(); @@ -91,31 +91,31 @@ Sequence.prototype.enable = function () { }; /** - * Return the id of the sequence - * - * @method getId - * @return {String} The id of the sequence - */ -Sequence.prototype.getId = function () { +* Return the id of the sequence +* +* @method getId +* @return {String} The id of the sequence +*/ +Sequence.prototype.getId = function() { return this._options.id; }; /** - * Given a mongoose document, retrieve the values of the fields set as reference - * for the sequence. - * - * @method _getCounterReferenceField - * @param {object} doc A mongoose document - * @param {Boolean} incrScope If set, returns reference fields, used to find old counte - * @return {Array} An array of strings which represent the value of the - * reference - */ -Sequence.prototype._getCounterReferenceField = function (doc, incrScope) { +* Given a mongoose document, retrieve the values of the fields set as reference +* for the sequence. +* +* @method _getCounterReferenceField +* @param {object} doc A mongoose document +* @param {Boolean} incrScope If set, returns reference fields, used to find old counter +* @return {Array} An array of strings which represent the value of the +* reference +*/ +Sequence.prototype._getCounterReferenceField = function(doc, incrScope) { var reference = {}; if (incrScope) { - for (var i in this._options.old_reference_fields) { - reference[this._options.reference_fields[i]] = doc[this._options.reference_fields[i]]; + for (var index in this._options.old_reference_fields) { + reference[this._options.reference_fields[index]] = doc[this._options.reference_fields[index]]; } return reference; } @@ -125,7 +125,7 @@ Sequence.prototype._getCounterReferenceField = function (doc, incrScope) { } else { for (var i in this._options.reference_fields) { reference[this._options.reference_fields[i]] = doc[this._options.reference_fields[i]]; - // reference.push(JSON.stringify(doc[this._options.reference_fields[i]])); + // reference.push(JSON.stringify(doc[this._options.reference_fields[i]])); } } @@ -133,11 +133,11 @@ Sequence.prototype._getCounterReferenceField = function (doc, incrScope) { }; /** - * Enrich the schema with keys needed by this sequence - * - * @method _createSchemaKeys - */ -Sequence.prototype._createSchemaKeys = function () { +* Enrich the schema with keys needed by this sequence +* +* @method _createSchemaKeys +*/ +Sequence.prototype._createSchemaKeys = function() { var schemaKey = this._schema.path(this._options.inc_field); if (_.isUndefined(schemaKey)) { var fieldDesc = {}; @@ -151,64 +151,52 @@ Sequence.prototype._createSchemaKeys = function () { }; /** - * Create a model for the counter handled by this sequence - * - * @method _createCounterModel - * @return {Mongoose~Model} A mongoose model - */ -Sequence.prototype._createCounterModel = function () { +* Create a model for the counter handled by this sequence +* +* @method _createCounterModel +* @return {Mongoose~Model} A mongoose model +*/ +Sequence.prototype._createCounterModel = function() { var CounterSchema; - CounterSchema = mongoose.Schema({ - id: { - type: String, - required: true - }, - reference_value: { - type: mongoose.Schema.Types.Mixed, - required: true + CounterSchema = mongoose.Schema( + { + id: {type: String, required: true}, + reference_value: { type: mongoose.Schema.Types.Mixed, required: true}, + seq: {type:Number, default: 0, required: true} }, - seq: { - type: Number, - default: 0, - required: true + { + collection: this._options.collection_name, + validateBeforeSave: false, + versionKey: false, + _id: false } - }, { - collection: this._options.collection_name, - validateBeforeSave: false, - versionKey: false, - _id: false - }); + ); - CounterSchema.index({ - id: 1, - reference_value: 1 - }, { - unique: true - }); + CounterSchema.index({id: 1, reference_value: 1}, {unique: true}); - /* Unused. Enable when is useful */ - // CounterSchema.static('getNext', function(id, referenceValue, callback) { - // this.findOne({ id: id, reference_value: referenceValue }, callback); - // }); +/* Unused. Enable when is useful */ +// CounterSchema.static('getNext', function(id, referenceValue, callback) { +// this.findOne({ id: id, reference_value: referenceValue }, callback); +// }); return mongoose.model('Counter_' + this._options.id, CounterSchema); }; /** - * Set and handler for some hooks on the schema referenced by this sequence - * - * @method _setHooks - */ -Sequence.prototype._setHooks = function () { - this._schema.pre('save', true, (function (sequence) { - return function (next, done) { +* Set and handler for some hooks on the schema referenced by this sequence +* +* @method _setHooks +*/ +Sequence.prototype._setHooks = function() { + this._schema.pre('save', true, (function(sequence){ + return function(next, done) { var doc = this; next(); if (!doc.isNew) { return done(); } - sequence._setNextCounter(doc, function (err, seq) { + sequence._setNextCounter(doc, function(err, seq) { if (err) return done(err); doc[sequence._options.inc_field] = seq; done(); @@ -218,137 +206,102 @@ Sequence.prototype._setHooks = function () { }; /** - * Set some useful methods on the schema - * - * @method _setMethods - */ -Sequence.prototype._setMethods = function () { - // this._schema.static('getNext', function(id, referenceValue, callback) { - // this._counterModel.getNext(id, referenceValue, function(err, counter) { - // if (err) return callback(err); - // return callback(null, ++counter.seq); - // }); - // }.bind(this)); - - this._schema.method('setNext', function (id, callback) { +* Set some useful methods on the schema +* +* @method _setMethods +*/ +Sequence.prototype._setMethods = function() { +// this._schema.static('getNext', function(id, referenceValue, callback) { +// this._counterModel.getNext(id, referenceValue, function(err, counter) { +// if (err) return callback(err); +// return callback(null, ++counter.seq); +// }); +// }.bind(this)); + + this._schema.method('setNext', function(id, callback) { var sequence = sequenceArchive.getSequence(id); if (_.isNull(sequence)) { return callback(new Error('Trying to increment a wrong sequence using the id ' + id)); } - // sequence = sequence.sequence; + // sequence = sequence.sequence; - sequence._setNextCounter(this, function (err, seq) { + sequence._setNextCounter(this, function(err, seq) { if (err) return callback(err); this[sequence._options.inc_field] = seq; this.save(callback); }.bind(this)); }); - this._schema.static('counterReset', function (id, reference, callback) { + this._schema.static('counterReset', function(id, reference, callback) { var sequence = sequenceArchive.getSequence(id); sequence._resetCounter(id, reference, callback); }.bind(this)); }; -Sequence.prototype._resetCounter = function (id, reference, callback) { - var condition = { - id: id - }; +Sequence.prototype._resetCounter = function(id, reference, callback){ + var condition = { id: id }; if (reference instanceof Function) { callback = reference; } else { condition.reference_value = this._getCounterReferenceField(reference); } - this._counterModel.updateMany(condition, { - $set: { - seq: 0 - } - }, null, callback); + this._counterModel.updateMany(condition, {$set: {seq:0}}, null, callback); }; /** - * Utility function to increment a counter in a transaction - * - * @method _setNextCounter - * @param {object} doc A mongoose model which need to receive the - * increment - * @param {Function} callback Called with the sequence counter - */ -Sequence.prototype._setNextCounter = function (doc, callback) { - - var retriable = function (callback) { +* Utility function to increment a counter in a transaction +* +* @method _setNextCounter +* @param {object} doc A mongoose model which need to receive the +* increment +* @param {Function} callback Called with the sequence counter +*/ +Sequence.prototype._setNextCounter = function(doc, callback) { + + var retriable = function(callback) { var that = this; var id = that.getId(); var referenceValue = that._getCounterReferenceField(doc); if (that._options.old_reference_fields) { - that._counterModel.findOne({ - id: id, - reference_value: referenceValue - }, function (err, counter) { + that._counterModel.findOne({id : id, reference_value: referenceValue}, function (err, counter) { if (err) return callback(err); if (counter) { - that._counterModel.findOneAndUpdate({ - id: id, - reference_value: referenceValue - }, { - $inc: { - seq: 1 - } - }, { - new: true, - upsert: true, - passRawResult: true - }, - function (err, counter) { - if (err) return callback(err); - return callback(null, counter.seq); - } + that._counterModel.findOneAndUpdate( + { id: id, reference_value: referenceValue }, + { $inc: { seq: 1 } }, + { new: true, upsert: true, passRawResult: true }, + function(err, counter) { + if (err) return callback(err); + return callback(null, counter.seq); + } ); } else { const oldReferenceID = `${id}_${ that._options.old_ref_field_suffix|| 'old'}`; - that._counterModel.findOne({ - id: oldReferenceID, - reference_value: that._getCounterReferenceField(doc, true) - }, function (err, counter) { + that._counterModel.findOne({id : oldReferenceID, reference_value: that._getCounterReferenceField(doc, true)}, function (err, counter) { if (err) return callback(err); if (!counter) return callback(new Error('Old Counter not found')); - that._counterModel.findOneAndUpdate({ - id: id, - reference_value: that._getCounterReferenceField(doc) - }, { - seq: counter.seq + 1 - }, { - new: true, - upsert: true, - passRawResult: true - }, - function (err, counter) { - if (err) return callback(err); - return callback(null, counter.seq); - } + that._counterModel.findOneAndUpdate( + { id: id, reference_value: referenceValue }, + { seq: counter.seq + 1 } , + { new: true, upsert: true, passRawResult: true }, + function(err, counter) { + if (err) return callback(err); + return callback(null, counter.seq); + } ); }); - } }); } else { - that._counterModel.findOneAndUpdate({ - id: id, - reference_value: referenceValue - }, { - $inc: { - seq: 1 - } - }, { - new: true, - upsert: true, - passRawResult: true - }, - function (err, counter) { - if (err) return callback(err); - return callback(null, counter.seq); - } + that._counterModel.findOneAndUpdate( + { id: id, reference_value: referenceValue }, + { $inc: { seq: 1 } }, + { new: true, upsert: true, passRawResult: true }, + function(err, counter) { + if (err) return callback(err); + return callback(null, counter.seq); + } ); } }; @@ -357,10 +310,10 @@ Sequence.prototype._setNextCounter = function (doc, callback) { }; -module.exports = function (goose) { +module.exports = function(goose){ if (arguments.length !== 1) { throw new Error('Please, pass mongoose while requiring mongoose-sequence: https://github.com/ramiel/mongoose-sequence#requiring'); } mongoose = goose; return Sequence.getInstance; -}; \ No newline at end of file +}; From ec8ba324a4b957b379ca3da4d537777e444b32e3 Mon Sep 17 00:00:00 2001 From: Bhanu Pratap Chaudhary Date: Sat, 30 Mar 2019 02:45:16 +0530 Subject: [PATCH 3/6] added fix --- lib/sequence.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/sequence.js b/lib/sequence.js index 15825e1..cd5d419 100644 --- a/lib/sequence.js +++ b/lib/sequence.js @@ -280,10 +280,10 @@ Sequence.prototype._setNextCounter = function(doc, callback) { const oldReferenceID = `${id}_${ that._options.old_ref_field_suffix|| 'old'}`; that._counterModel.findOne({id : oldReferenceID, reference_value: that._getCounterReferenceField(doc, true)}, function (err, counter) { if (err) return callback(err); - if (!counter) return callback(new Error('Old Counter not found')); + var seq = counter ? counter.seq + 1 : 1; that._counterModel.findOneAndUpdate( { id: id, reference_value: referenceValue }, - { seq: counter.seq + 1 } , + { seq } , { new: true, upsert: true, passRawResult: true }, function(err, counter) { if (err) return callback(err); From 0d45fa4dee54757344a09f2c041669df37388cbd Mon Sep 17 00:00:00 2001 From: Bhanu Pratap Chaudhary Date: Wed, 10 Apr 2019 17:05:36 +0530 Subject: [PATCH 4/6] added configuration for sharing couters --- lib/sequence.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sequence.js b/lib/sequence.js index cd5d419..d54389a 100644 --- a/lib/sequence.js +++ b/lib/sequence.js @@ -65,7 +65,7 @@ Sequence.getInstance = function(schema, options) { var sequence = new Sequence(schema, options), id = sequence.getId(); - if (sequenceArchive.existsSequence(id)){ + if (!options.duplicate && sequenceArchive.existsSequence(id)){ throw new Error('Counter already defined for field "'+id+'"'); } sequence.enable(); From 108f09b694bc1b546bf19e6a80643a53d601c7c1 Mon Sep 17 00:00:00 2001 From: Bhanu Pratap Chaudhary Date: Wed, 10 Apr 2019 18:01:19 +0530 Subject: [PATCH 5/6] added cache for shared counters --- lib/sequence.js | 23 ++++++++++++++++++----- lib/sequence_archive.js | 24 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/lib/sequence.js b/lib/sequence.js index d54389a..3a756c2 100644 --- a/lib/sequence.js +++ b/lib/sequence.js @@ -68,7 +68,13 @@ Sequence.getInstance = function(schema, options) { if (!options.duplicate && sequenceArchive.existsSequence(id)){ throw new Error('Counter already defined for field "'+id+'"'); } - sequence.enable(); + + if (options.duplicate && !options.super) { + sequence.enable(options); + return sequence; + } + + sequence.enable(options); sequenceArchive.addSequence(id, sequence); return sequence; }; @@ -78,8 +84,13 @@ Sequence.getInstance = function(schema, options) { * * @method enable */ -Sequence.prototype.enable = function(){ - this._counterModel = this._createCounterModel(); +Sequence.prototype.enable = function(options){ + if (options.duplicate) { + const key = 'Counter_' + this._options.id; + this._counterModel = sequenceArchive.getSequenceModel(key) || this._createCounterModel(); + } else { + this._counterModel = this._createCounterModel(); + } this._createSchemaKeys(); @@ -179,8 +190,10 @@ Sequence.prototype._createCounterModel = function() { // CounterSchema.static('getNext', function(id, referenceValue, callback) { // this.findOne({ id: id, reference_value: referenceValue }, callback); // }); - - return mongoose.model('Counter_' + this._options.id, CounterSchema); + const modal = mongoose.model('Counter_' + this._options.id, CounterSchema); + sequenceArchive.setSequenceModel('Counter_' + this._options.id, modal); + + return modal; }; /** diff --git a/lib/sequence_archive.js b/lib/sequence_archive.js index 68fa42f..7a79729 100644 --- a/lib/sequence_archive.js +++ b/lib/sequence_archive.js @@ -9,6 +9,7 @@ var _ = require('lodash'), */ SequenceArchive = function() { this.sequences = []; + this.models = {}; }; /** @@ -47,6 +48,29 @@ SequenceArchive.prototype.getSequence = function(id) { return null; }; + +/** + * Get a sequenceModel by id + * + * @method getSequence + * @param {string} id An id for the sequenceModel + * @return {object|null} Return the found sequenceModel or null + */ +SequenceArchive.prototype.getSequenceModel = function(id) { + return this.models[id]; +}; + +/** + * Get a sequenceModel by id + * + * @method getSequence + * @param {string} id An id for the sequenceModel + * @return {object|null} Return the found sequenceModel or null + */ +SequenceArchive.prototype.setSequenceModel = function(id, model) { + this.models[id] = model; +}; + /** * Check if a sequence already exists * From 787e8227c5f8053318ebf7f910e81bca8cc057b2 Mon Sep 17 00:00:00 2001 From: Bhanu Pratap Chaudhary Date: Wed, 10 Apr 2019 18:12:38 +0530 Subject: [PATCH 6/6] updated readme --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/README.md b/README.md index f8704a1..6f79901 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,58 @@ user.setNext('inhabitant_seq', function(err, user){ Of course this example is a bit forced and this is for sure not the perfect use case. The fields `country` and `city` have to be present and must not change during the life of the document because no automatic hooks are set on the change of those values. But there are situations when you want a similar behavior. +### Shared counters + +Let say we want to share counter between User and Person document. +The schema is like this: + +```js +UserSchema = mongoose.Schema({ + name: String, + country: String, + city: String, + inhabitant_number: Number +}); + +PersonSchema = mongoose.Schema({ + name: String, + country: String, + city: String, + inhabitant_number: Number +}); +``` + +Every time a new User or Person is added, we want the inhabitant_number to be incremented and to be shared between the two collection. We do so by setting duplicate and super field. + +```js +UserSchema.plugin(AutoIncrement, {id: 'inhabitant_seq', inc_field: 'inhabitant_number', reference_fields: ['country'] , duplicate: true, super: true}); + +PersonSchema.plugin(AutoIncrement, {id: 'inhabitant_seq', inc_field: 'inhabitant_number', reference_fields: ['country'] , duplicate: true, super: false}); +``` + +Notice that we have to set super to only one of the Schema, and duplicate to all the schemas for we want a shared counter. + +Now save a new user and a person: +```js +var user = new User({ + name: 'Patrice', + country: 'France', + city: 'Paris' +}); +user.save(); + +var person = new PersonSchema({ + name: 'Patrice', + country: 'France', + city: 'Paris' +}); +person.save(); +``` + +This user will have the `inhabitant_number` counter set to 1 and person will have the `inhabitant_number` counter set to 1 . + +Important Note: For the shared counter to work as inteneded, reference field property must be same and have type in all the models. + ### Reset a counter It's possible to programmatically reset a counter through the Model's static method `counterReset(id, reference, callback)`. The method takes these parameters: