diff --git a/lib/sequence.js b/lib/sequence.js index dbad9b7..549e0c0 100644 --- a/lib/sequence.js +++ b/lib/sequence.js @@ -64,7 +64,7 @@ Sequence = 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+'"'); } @@ -119,7 +119,6 @@ Sequence.prototype._getCounterReferenceField = function(doc) { reference.push(JSON.stringify(doc[this._options.reference_fields[i]])); } } - return reference; }; @@ -180,6 +179,8 @@ Sequence.prototype._createCounterModel = function() { * @method _setHooks */ Sequence.prototype._setHooks = function() { + var _this = this; + this._schema.pre('save', true, (function(sequence){ return function(next, done) { var doc = this; @@ -194,6 +195,52 @@ Sequence.prototype._setHooks = function() { }.bind(doc)); }; })(this)); + + this._schema.pre('findOneAndUpdate', function(next) { + // jscs:disable + var _thisquery = this, + referenceValue; + + // jscs:enable + + this.model.findOne(_thisquery._conditions, function(err, obj) { + if (err) return next(); + if (obj) return next(); + else { + referenceValue = _this._getCounterReferenceField(_thisquery._update); + _this._setNextCounterByReference(_this.getId(), referenceValue, function(err, seq) { + if (err) return next(); + _thisquery._update['$setOnInsert'] = _thisquery._update['$setOnInsert'] || {}; + _thisquery._update['$setOnInsert'][_this._options.inc_field] = seq; + next(); + }); + } + }); + }); + + + this._schema.pre('update', function(next, done) { + // jscs:disable + var _thisquery = this, + referenceValue; + + // jscs:enable + + + this.model.findOne(_thisquery._conditions, function(err, obj) { + if (err) return next(); + if (obj) return next(); + else { + referenceValue = _this._getCounterReferenceField(_thisquery._update.$set); + _this._setNextCounterByReference(_this.getId(), referenceValue, function(err, seq) { + if (err) return next(); + _thisquery._update['$setOnInsert'] = _thisquery._update['$setOnInsert'] || {}; + _thisquery._update['$setOnInsert'][_this._options.inc_field] = seq; + next(); + }); + } + }); + }); }; /** @@ -215,7 +262,6 @@ Sequence.prototype._setMethods = function() { if (_.isNull(sequence)) { return callback(new Error('Trying to increment a wrong sequence using the id ' + id)); } - // sequence = sequence.sequence; sequence._setNextCounter(this, function(err, seq) { if (err) return callback(err); @@ -235,7 +281,11 @@ Sequence.prototype._setMethods = function() { */ Sequence.prototype._setNextCounter = function(doc, callback) { var id = this.getId(); - var referenceValue = this._getCounterReferenceField(doc); + var referenceValue = this._getCounterReferenceField(doc); + this._setNextCounterByReference(id, referenceValue, callback); +}; + +Sequence.prototype._setNextCounterByReference = function(id, referenceValue, callback) { this._counterModel.findOneAndUpdate( { id: id, reference_value: referenceValue }, { $inc: { seq: 1 } }, diff --git a/test/base.js b/test/base.js index 962c845..8722eb5 100644 --- a/test/base.js +++ b/test/base.js @@ -228,6 +228,156 @@ describe('Basic => ', function() { }); + describe('hook', function(){ + describe('simple counters', function(){ + before(function(done) { + var SimpleFieldSchema = new Schema({ + id: Number, + val: String, + tag: String + }); + var wrapper = function(schema, options) { + var instance = AutoIncrement(schema, options); + this.setNextCounterSpy = sinon.spy(instance, '_setNextCounterByReference'); + return instance; + }.bind(this); + SimpleFieldSchema.plugin(wrapper, {id: 'id_hook_test', inc_field: 'id'}); + this.SimpleField = mongoose.model('SimpleFieldHookTest', SimpleFieldSchema); + this.SimpleField.create({val: 'existing'}, function(err){ + this.setNextCounterSpy.reset(); + done(err); + }.bind(this)); + }); + + afterEach(function(){ + this.setNextCounterSpy.reset(); + }); + + it('is called when saving a new document', function(done){ + var t = new this.SimpleField({val: 'a'}); + t.save(function(err){ + sinon.assert.calledOnce(this.setNextCounterSpy); + done(err); + }.bind(this)); + }); + + it('is not called when saving an existing document', function(done){ + var t = new this.SimpleField({val: 'a'}); + t.isNew = false; + t.save(function(err){ + sinon.assert.notCalled(this.setNextCounterSpy); + done(err); + }.bind(this)); + }); + + it('is called when upserting in an update and result in an insert', function(done){ + this.SimpleField.update({val: '1234'}, {tag: 'nothing'}, {upsert: true}, function(err, doc){ + sinon.assert.calledOnce(this.setNextCounterSpy); + done(err); + }.bind(this)); + }); + + it('is not called when upserting in an update and not result in an insert', function(done){ + this.SimpleField.update({val: 'existing'}, {tag: 'update'}, {upsert: true}, function(err, doc){ + sinon.assert.notCalled(this.setNextCounterSpy); + done(err); + }.bind(this)); + }); + + it('is called when upserting in an findOneAndUpdate and result in an insert', function(done){ + this.SimpleField.findOneAndUpdate({val: '4567'}, {tag: 'nothing'}, {upsert: true}, function(err, doc){ + sinon.assert.calledOnce(this.setNextCounterSpy); + done(err); + }.bind(this)); + }); + + it('is not called when upserting in an findOneAndUpdate and not result in an insert', function(done){ + this.SimpleField.findOneAndUpdate({val: '1234'}, {tag: 'findOneAndUpdate'}, {upsert: true}, function(err, doc){ + sinon.assert.notCalled(this.setNextCounterSpy); + done(err); + }.bind(this)); + }); + }); + + describe('referenced counters', function() { + before(function(done) { + var SimpleFieldSchema = new Schema({ + same_tag_members: Number, + val: String, + tag: String + }); + var wrapper = function(schema, options) { + var instance = AutoIncrement(schema, options); + this.instance = instance; + this.setNextCounterSpy = sinon.spy(instance, '_setNextCounterByReference'); + return instance; + }.bind(this); + SimpleFieldSchema.plugin(wrapper, { + id: 'id_hook_test_referenced', + inc_field: 'same_tag_members', + reference_fields: ['tag']} + ); + this.SimpleField = mongoose.model('SimpleFieldHookTestReferenced', SimpleFieldSchema); + this.SimpleField.create({val: 'existing'}, function(err){ + this.setNextCounterSpy.reset(); + done(err); + }.bind(this)); + }); + + afterEach(function(){ + this.setNextCounterSpy.reset(); + }); + + it('are called when saving a new document', function(done){ + var t = new this.SimpleField({val: 'a', tag: 'red'}); + t.save(function(err){ + sinon.assert.calledWith( + this.setNextCounterSpy, + 'id_hook_test_referenced', + this.instance._getCounterReferenceField(t) + ); + done(err); + }.bind(this)); + }); + + it('are called when upserting in an update and result in an insert', function(done){ + this.SimpleField.update({val: '1234'}, {tag: 'blue'}, {upsert: true}, function(err, doc){ + sinon.assert.calledWith( + this.setNextCounterSpy, + 'id_hook_test_referenced', + this.instance._getCounterReferenceField({tag:'blue'}) + ); + done(err); + }.bind(this)); + }); + + it('are not called when upserting in an update and not result in an insert', function(done){ + this.SimpleField.update({val: 'existing'}, {tag: 'green'}, {upsert: true}, function(err, doc){ + sinon.assert.notCalled(this.setNextCounterSpy); + done(err); + }.bind(this)); + }); + + it('are called when upserting in an findOneAndUpdate and result in an insert', function(done){ + this.SimpleField.findOneAndUpdate({val: '4567'}, {tag: 'pink'}, {upsert: true}, function(err, doc){ + sinon.assert.calledWith( + this.setNextCounterSpy, + 'id_hook_test_referenced', + this.instance._getCounterReferenceField({tag:'pink'}) + ); + done(err); + }.bind(this)); + }); + + it('are not called when upserting in an findOneAndUpdate and not result in an insert', function(done){ + this.SimpleField.findOneAndUpdate({val: '1234'}, {tag: 'yellow'}, {upsert: true}, function(err, doc){ + sinon.assert.notCalled(this.setNextCounterSpy); + done(err); + }.bind(this)); + }); + }); + }); + describe('a manual increment field => ', function() { before(function(done) { @@ -342,7 +492,7 @@ describe('Basic => ', function() { assert.throws(function(){ UnusedSchema.plugin(AutoIncrement, {inc_field: 'inhabitant', reference_fields: ['country', 'city'], disable_hooks: true}); }, Error); - + }); }); @@ -427,7 +577,7 @@ describe('Basic => ', function() { }); }); }); - }); + }); }); });