diff --git a/lib/dao.js b/lib/dao.js index 279b05ae..0ea878a7 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -383,23 +383,52 @@ DataAccessObject._coerce = function (where) { return new OrigDate(arg); }; } else if (DataType === Boolean) { - DataType = function(val) { - if(val === 'true') { - return true; - } else if(val === 'false') { - return false; - } else { - return Boolean(val); - } - }; + DataType = function(val) { + if(val === 'true') { + return true; + } else if(val === 'false') { + return false; + } else { + return Boolean(val); + } + }; + } else if(DataType === Number) { + // This fixes a regression in mongodb connector + // For numbers, only convert it produces a valid number + // LoopBack by default injects a number id. We should fix it based + // on the connector's input, for example, MongoDB should use string + // while RDBs typically use number + DataType = function(val) { + var num = Number(val); + return !isNaN(num) ? num : val; + }; } + if (!DataType) { - continue; + continue; } + + if (DataType === geo.GeoPoint) { + // Skip the GeoPoint as the near operator breaks the assumption that + // an operation has only one property + // We should probably fix it based on + // http://docs.mongodb.org/manual/reference/operator/query/near/ + // The other option is to make operators start with $ + continue; + } + var val = where[p]; + if(val === null || val === undefined) { + continue; + } // Check there is an operator var operator = null; - if ('object' === typeof val && Object.keys(val).length === 1) { + if ('object' === typeof val) { + if (Object.keys(val).length !== 1) { + // Skip if there are not only one properties + // as the assumption for operators is not true here + continue; + } for (var op in operators) { if (op in val) { val = val[op]; @@ -455,6 +484,11 @@ DataAccessObject.find = function find(params, cb) { var constr = this; params = params || {}; + + if(params.where) { + params.where = this._coerce(params.where); + } + var fields = params.fields; var near = params && geo.nearFilter(params.where); var supportsGeo = !!this.getDataSource().connector.buildNearFilter; @@ -465,9 +499,6 @@ DataAccessObject.find = function find(params, cb) { } params = removeUndefined(params); - if(params.where) { - params.where = this._coerce(params.where); - } if(near) { if(supportsGeo) { // convert it diff --git a/lib/geo.js b/lib/geo.js index 6a43e4af..580308a7 100644 --- a/lib/geo.js +++ b/lib/geo.js @@ -106,8 +106,8 @@ function GeoPoint(data) { } assert(typeof data === 'object', 'must provide a lat and lng object when creating a GeoPoint'); - assert(typeof data.lat === 'number', 'lat must be a number when creating a GeoPoint'); - assert(typeof data.lng === 'number', 'lng must be a number when creating a GeoPoint'); + assert(typeof data.lat === 'number' && !isNaN(data.lat), 'lat must be a number when creating a GeoPoint'); + assert(typeof data.lng === 'number' && !isNaN(data.lng), 'lng must be a number when creating a GeoPoint'); assert(data.lng <= 180, 'lng must be <= 180'); assert(data.lng >= -180, 'lng must be >= -180'); assert(data.lat <= 90, 'lat must be <= 90'); diff --git a/test/loopback-dl.test.js b/test/loopback-dl.test.js index f324b97d..bb3c6457 100644 --- a/test/loopback-dl.test.js +++ b/test/loopback-dl.test.js @@ -582,6 +582,7 @@ describe('DataAccessObject', function () { age: Number, vip: Boolean, date: Date, + location: 'GeoPoint', scores: [Number] }); }); @@ -647,6 +648,34 @@ describe('DataAccessObject', function () { assert.deepEqual(where, {vip: false}); }); + + it('should skip GeoPoint', function () { + where = model._coerce({location: {near: {lng: 10, lat: 20}, maxDistance: 20}}); + assert.deepEqual(where, {location: {near: {lng: 10, lat: 20}, maxDistance: 20}}); + }); + + it('should skip null values', function () { + where = model._coerce({date: null}); + assert.deepEqual(where, {date: null}); + }); + + it('should skip undefined values', function () { + where = model._coerce({date: undefined}); + assert.deepEqual(where, {date: undefined}); + }); + + it('should skip conversion if a simple property produces NaN for numbers', + function () { + where = model._coerce({age: 'xyz'}); + assert.deepEqual(where, {age: 'xyz'}); + }); + + it('should skip conversion if an array property produces NaN for numbers', + function () { + where = model._coerce({age: {inq: ['xyz', '12']}}); + assert.deepEqual(where, {age: {inq: ['xyz', 12]}}); + }); + }); describe('Load models from json', function () {