From 7d78a7576ad0ff438fdf8af82089748b5274c64a Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 9 Apr 2017 00:14:49 -0600 Subject: [PATCH 1/2] feat(query): add PoC for runSetters option Re: #4569 --- lib/schema.js | 4 ++++ lib/schema/string.js | 3 +++ test/model.query.casting.test.js | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/lib/schema.js b/lib/schema.js index a5e72d3b18d..bd0feb6d4a9 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -689,6 +689,10 @@ Schema.interpretAsType = function(path, obj, options) { 'You can only nest using refs or arrays.'); } + obj = utils.clone(obj); + if (!('runSetters' in obj)) { + obj.runSetters = options.runSetters; + } return new MongooseTypes[name](path, obj); }; diff --git a/lib/schema/string.js b/lib/schema/string.js index d969280a9c6..8c5d95fd233 100644 --- a/lib/schema/string.js +++ b/lib/schema/string.js @@ -506,6 +506,9 @@ SchemaString.prototype.castForQuery = function($conditional, val) { if (Object.prototype.toString.call(val) === '[object RegExp]') { return val; } + if (this.options && this.options.runSetters) { + return this.applySetters(val, null); + } return this.cast(val); }; diff --git a/test/model.query.casting.test.js b/test/model.query.casting.test.js index 4a47bf88514..6f3a195ba07 100644 --- a/test/model.query.casting.test.js +++ b/test/model.query.casting.test.js @@ -1021,6 +1021,26 @@ describe('model query casting', function() { catch(done); }); + it('lowercase in query (gh-4569)', function(done) { + var db = start(); + + var testSchema = new Schema({ + name: { type: String, lowercase: true } + }, { runSetters: true }); + + var Test = db.model('gh-4569', testSchema); + Test.create({ name: 'val' }). + then(function() { + return Test.findOne({ name: 'VAL' }); + }). + then(function(doc) { + assert.ok(doc); + assert.equal(doc.name, 'val'); + done(); + }). + catch(done); + }); + it('_id = 0 (gh-4610)', function(done) { var db = start(); From 173d72efba76b5881eb604f0cbfc4ff3cf849231 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 9 Apr 2017 19:30:13 -0600 Subject: [PATCH 2/2] feat(query): add more fleshed out runSettersOnQuery re: #4569 --- lib/schema.js | 6 ++-- lib/schema/boolean.js | 4 +-- lib/schema/buffer.js | 2 +- lib/schema/date.js | 2 +- lib/schema/decimal128.js | 20 -------------- lib/schema/embedded.js | 4 +++ lib/schema/number.js | 2 +- lib/schema/objectid.js | 2 +- lib/schema/string.js | 6 ++-- lib/schematype.js | 47 ++++++++++++++++++++++++-------- test/model.query.casting.test.js | 16 ++++++++--- 11 files changed, 63 insertions(+), 48 deletions(-) diff --git a/lib/schema.js b/lib/schema.js index bd0feb6d4a9..cd376b9a705 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -689,9 +689,9 @@ Schema.interpretAsType = function(path, obj, options) { 'You can only nest using refs or arrays.'); } - obj = utils.clone(obj); - if (!('runSetters' in obj)) { - obj.runSetters = options.runSetters; + obj = utils.clone(obj, { retainKeyOrder: true }); + if (!('runSettersOnQuery' in obj)) { + obj.runSettersOnQuery = options.runSettersOnQuery; } return new MongooseTypes[name](path, obj); }; diff --git a/lib/schema/boolean.js b/lib/schema/boolean.js index 206d9fd98f5..5951dd0513d 100644 --- a/lib/schema/boolean.js +++ b/lib/schema/boolean.js @@ -90,10 +90,10 @@ SchemaBoolean.prototype.castForQuery = function($conditional, val) { return handler.call(this, val); } - return this.cast(val); + return this._castForQuery(val); } - return this.cast($conditional); + return this._castForQuery($conditional); }; /*! diff --git a/lib/schema/buffer.js b/lib/schema/buffer.js index 56490dea878..f7ca7588a36 100644 --- a/lib/schema/buffer.js +++ b/lib/schema/buffer.js @@ -178,7 +178,7 @@ SchemaBuffer.prototype.castForQuery = function($conditional, val) { return handler.call(this, val); } val = $conditional; - var casted = this.cast(val); + var casted = this._castForQuery(val); return casted ? casted.toObject({ transform: false, virtuals: false }) : casted; }; diff --git a/lib/schema/date.js b/lib/schema/date.js index b7bcf227b33..9f97096b5ba 100644 --- a/lib/schema/date.js +++ b/lib/schema/date.js @@ -277,7 +277,7 @@ SchemaDate.prototype.castForQuery = function($conditional, val) { var handler; if (arguments.length !== 2) { - return this.cast($conditional); + return this._castForQuery($conditional); } handler = this.$conditionalHandlers[$conditional]; diff --git a/lib/schema/decimal128.js b/lib/schema/decimal128.js index 3400e36ef63..67e35df4830 100644 --- a/lib/schema/decimal128.js +++ b/lib/schema/decimal128.js @@ -139,26 +139,6 @@ Decimal128.prototype.$conditionalHandlers = $lte: handleSingle }); -/** - * Casts contents for queries. - * - * @param {String} $conditional - * @param {any} [val] - * @api private - */ - -Decimal128.prototype.castForQuery = function($conditional, val) { - var handler; - if (arguments.length === 2) { - handler = this.$conditionalHandlers[$conditional]; - if (!handler) { - throw new Error('Can\'t use ' + $conditional + ' with ObjectId.'); - } - return handler.call(this, val); - } - return this.cast($conditional); -}; - /*! * Module exports. */ diff --git a/lib/schema/embedded.js b/lib/schema/embedded.js index d1e293fdceb..9938563adc8 100644 --- a/lib/schema/embedded.js +++ b/lib/schema/embedded.js @@ -156,6 +156,10 @@ Embedded.prototype.castForQuery = function($conditional, val) { return val; } + if (this.options.runSetters) { + val = this._applySetters(val); + } + return new this.caster(val); }; diff --git a/lib/schema/number.js b/lib/schema/number.js index a91f6dda6df..e5b9bc7fa62 100644 --- a/lib/schema/number.js +++ b/lib/schema/number.js @@ -279,7 +279,7 @@ SchemaNumber.prototype.castForQuery = function($conditional, val) { } return handler.call(this, val); } - val = this.cast($conditional); + val = this._castForQuery($conditional); return val; }; diff --git a/lib/schema/objectid.js b/lib/schema/objectid.js index 28858236893..6f684191c90 100644 --- a/lib/schema/objectid.js +++ b/lib/schema/objectid.js @@ -184,7 +184,7 @@ ObjectId.prototype.castForQuery = function($conditional, val) { } return handler.call(this, val); } - return this.cast($conditional); + return this._castForQuery($conditional); }; /*! diff --git a/lib/schema/string.js b/lib/schema/string.js index 8c5d95fd233..dfd044ad2f8 100644 --- a/lib/schema/string.js +++ b/lib/schema/string.js @@ -506,10 +506,8 @@ SchemaString.prototype.castForQuery = function($conditional, val) { if (Object.prototype.toString.call(val) === '[object RegExp]') { return val; } - if (this.options && this.options.runSetters) { - return this.applySetters(val, null); - } - return this.cast(val); + + return this._castForQuery(val); }; /*! diff --git a/lib/schematype.js b/lib/schematype.js index 83495f6ef09..16078815796 100644 --- a/lib/schematype.js +++ b/lib/schematype.js @@ -622,20 +622,17 @@ SchemaType.prototype.getDefault = function(scope, init) { return ret; }; -/** - * Applies setters +/*! + * Applies setters without casting * - * @param {Object} value - * @param {Object} scope - * @param {Boolean} init * @api private */ -SchemaType.prototype.applySetters = function(value, scope, init, priorVal, options) { - var v = value, - setters = this.setters, - len = setters.length, - caster = this.caster; +SchemaType.prototype._applySetters = function(value, scope, init, priorVal) { + var v = value; + var setters = this.setters; + var len = setters.length; + var caster = this.caster; while (len--) { v = setters[len].call(scope, v, this); @@ -649,7 +646,22 @@ SchemaType.prototype.applySetters = function(value, scope, init, priorVal, optio v = newVal; } - if (v === null || v === undefined) { + return v; +}; + +/** + * Applies setters + * + * @param {Object} value + * @param {Object} scope + * @param {Boolean} init + * @api private + */ + +SchemaType.prototype.applySetters = function(value, scope, init, priorVal, options) { + var v = this._applySetters(value, scope, init, priorVal, options); + + if (v == null) { return v; } @@ -971,6 +983,19 @@ SchemaType.prototype.castForQuery = function($conditional, val) { return handler.call(this, val); } val = $conditional; + return this._castForQuery(val); +}; + +/*! + * Internal switch for runSetters + * + * @api private + */ + +SchemaType.prototype._castForQuery = function(val) { + if (this.options && this.options.runSettersOnQuery) { + return this.applySetters(val, null); + } return this.cast(val); }; diff --git a/test/model.query.casting.test.js b/test/model.query.casting.test.js index 6f3a195ba07..98e1d4fbdd1 100644 --- a/test/model.query.casting.test.js +++ b/test/model.query.casting.test.js @@ -1025,19 +1025,27 @@ describe('model query casting', function() { var db = start(); var testSchema = new Schema({ - name: { type: String, lowercase: true } - }, { runSetters: true }); + name: { type: String, lowercase: true }, + num: { type: Number, set: function(v) { return Math.floor(v); } } + }, { runSettersOnQuery: true }); var Test = db.model('gh-4569', testSchema); - Test.create({ name: 'val' }). + Test.create({ name: 'val', num: 3 }). then(function() { return Test.findOne({ name: 'VAL' }); }). then(function(doc) { assert.ok(doc); assert.equal(doc.name, 'val'); - done(); }). + then(function() { + return Test.findOne({ num: 3.14 }); + }). + then(function(doc) { + assert.ok(doc); + assert.equal(doc.name, 'val'); + }). + then(function() { done(); }). catch(done); });