From 2df0c4b21dddbc49d2d7e54d7dfbbc7055c1a5ca Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 29 Nov 2013 14:45:50 -0800 Subject: [PATCH 1/3] Coerce types for values of where clause --- lib/dao.js | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/lib/dao.js b/lib/dao.js index 224e6cdf..18363028 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -351,6 +351,69 @@ DataAccessObject.all = function () { DataAccessObject.find.apply(this, arguments); }; +var operators = { + gt: '>', + gte: '>=', + lt: '<', + lte: '<=', + between: 'BETWEEN', + inq: 'IN', + nin: 'NOT IN', + neq: '!=', + like: 'LIKE', + nlike: 'NOT LIKE' +}; + +DataAccessObject.coerce = function (where) { + if (!where) { + return where; + } + + var props = this.getDataSource().getModelDefinition(this.modelName).properties; + for (var p in where) { + var DataType = props[p] && props[p].type; + if (!DataType) { + continue; + } + if (Array.isArray(DataType) || DataType === Array) { + DataType = List; + } else if (DataType.name === 'Date') { + var OrigDate = Date; + DataType = function Date(arg) { + return new OrigDate(arg); + }; + } + var val = where[p]; + // Check there is an operator + var operator = null; + if ('object' === typeof val) { + for (var op in operators) { + if (op in val) { + val = val[op]; + operator = op; + break; + } + } + } + // Coerce the array items + if (Array.isArray(val)) { + for (var i = 0; i < val.length; i++) { + val[i] = DataType(val[i]); + } + } else { + val = DataType(val); + } + // Rebuild {property: {operator: value}} + if (operator) { + var value = {}; + value[operator] = val; + val = value; + } + where[p] = val; + } + return where; +}; + /** * Find all instances of Model, matched by query * make sure you have marked as `index: true` fields for filter or sort @@ -389,6 +452,9 @@ DataAccessObject.find = function find(params, cb) { } params = removeUndefined(params); + if(params.where) { + params.where = this.coerce(params.where); + } if(near) { if(supportsGeo) { // convert it @@ -511,6 +577,7 @@ DataAccessObject.destroyAll = function destroyAll(where, cb) { } else { // Support an optional where object where = removeUndefined(where); + where = this.coerce(where); this.getDataSource().connector.destroyAll(this.modelName, where, function (err, data) { cb && cb(err, data); }.bind(this)); @@ -556,6 +623,7 @@ DataAccessObject.count = function (where, cb) { where = null; } where = removeUndefined(where); + where = this.coerce(where); this.getDataSource().connector.count(this.modelName, cb, where); }; From ed6d8839baa596313aae88692fd0dc1fa98ac9d4 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 3 Dec 2013 21:27:46 -0800 Subject: [PATCH 2/3] Add a test case --- lib/dao.js | 8 ++++---- test/loopback-dl.test.js | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/dao.js b/lib/dao.js index 18363028..497c7503 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -364,7 +364,7 @@ var operators = { nlike: 'NOT LIKE' }; -DataAccessObject.coerce = function (where) { +DataAccessObject._coerce = function (where) { if (!where) { return where; } @@ -453,7 +453,7 @@ DataAccessObject.find = function find(params, cb) { params = removeUndefined(params); if(params.where) { - params.where = this.coerce(params.where); + params.where = this._coerce(params.where); } if(near) { if(supportsGeo) { @@ -577,7 +577,7 @@ DataAccessObject.destroyAll = function destroyAll(where, cb) { } else { // Support an optional where object where = removeUndefined(where); - where = this.coerce(where); + where = this._coerce(where); this.getDataSource().connector.destroyAll(this.modelName, where, function (err, data) { cb && cb(err, data); }.bind(this)); @@ -623,7 +623,7 @@ DataAccessObject.count = function (where, cb) { where = null; } where = removeUndefined(where); - where = this.coerce(where); + where = this._coerce(where); this.getDataSource().connector.count(this.modelName, cb, where); }; diff --git a/test/loopback-dl.test.js b/test/loopback-dl.test.js index fdad6fbb..09b65b94 100644 --- a/test/loopback-dl.test.js +++ b/test/loopback-dl.test.js @@ -572,6 +572,33 @@ describe('Load models with relations', function () { }); +describe('DataAccessObject', function () { + it('should be able to coerce where clause based on the types', function () { + var ds = new DataSource('memory'); + var model = ds.createModel('M1', { + id: {type: String, id: true}, + age: Number + }); + var where = model._coerce({id: 1}); + assert.deepEqual(where, {id: '1'}); + + where = model._coerce({age: '10'}); + assert.deepEqual(where, {age: 10}); + + where = model._coerce({age: 10}); + assert.deepEqual(where, {age: 10}); + + where = model._coerce({age: {gt: 10}}); + assert.deepEqual(where, {age: {gt: 10}}); + + where = model._coerce({age: {gt: '10'}}); + assert.deepEqual(where, {age: {gt: 10}}); + + where = model._coerce({age: {between: ['10', '20']}}); + assert.deepEqual(where, {age: {between: [10, 20]}}); + }); +}); + describe('Load models from json', function () { it('should be able to define models from json', function () { var path = require('path'), From abe6d2bb229aaba5e3067d28bb244a187fb5f9a2 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Wed, 4 Dec 2013 10:00:33 -0800 Subject: [PATCH 3/3] Add more tests to address the PR comments --- lib/dao.js | 21 ++++++++++--- test/loopback-dl.test.js | 66 +++++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/lib/dao.js b/lib/dao.js index 497c7503..279b05ae 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -10,7 +10,6 @@ var util = require('util'); var jutil = require('./jutil'); var validations = require('./validations.js'); var ValidationError = validations.ValidationError; -var List = require('./list.js'); require('./relations.js'); var Inclusion = require('./include.js'); var Relation = require('./relations.js'); @@ -376,17 +375,31 @@ DataAccessObject._coerce = function (where) { continue; } if (Array.isArray(DataType) || DataType === Array) { - DataType = List; - } else if (DataType.name === 'Date') { + DataType = DataType[0]; + } + if (DataType === Date) { var OrigDate = Date; DataType = function Date(arg) { 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); + } + }; + } + if (!DataType) { + continue; } var val = where[p]; // Check there is an operator var operator = null; - if ('object' === typeof val) { + if ('object' === typeof val && Object.keys(val).length === 1) { for (var op in operators) { if (op in val) { val = val[op]; diff --git a/test/loopback-dl.test.js b/test/loopback-dl.test.js index 09b65b94..ca7f3e27 100644 --- a/test/loopback-dl.test.js +++ b/test/loopback-dl.test.js @@ -573,15 +573,27 @@ describe('Load models with relations', function () { }); describe('DataAccessObject', function () { - it('should be able to coerce where clause based on the types', function () { - var ds = new DataSource('memory'); - var model = ds.createModel('M1', { - id: {type: String, id: true}, - age: Number - }); - var where = model._coerce({id: 1}); - assert.deepEqual(where, {id: '1'}); + var ds, model, where; + before(function () { + ds = new DataSource('memory'); + model = ds.createModel('M1', { + id: {type: String, id: true}, + age: Number, + vip: Boolean, + date: Date, + scores: [Number] + }); + }); + + it('should be able to coerce where clause for string types', function () { + where = model._coerce({id: 1}); + assert.deepEqual(where, {id: '1'}); + where = model._coerce({id: '1'}); + assert.deepEqual(where, {id: '1'}); + }); + + it('should be able to coerce where clause for number types', function () { where = model._coerce({age: '10'}); assert.deepEqual(where, {age: 10}); @@ -597,6 +609,44 @@ describe('DataAccessObject', function () { where = model._coerce({age: {between: ['10', '20']}}); assert.deepEqual(where, {age: {between: [10, 20]}}); }); + + it('should be able to coerce where clause for array types', function () { + where = model._coerce({scores: ['10', '20']}); + assert.deepEqual(where, {scores: [10, 20]}); + }); + + it('should be able to coerce where clause for date types', function () { + var d = new Date(); + where = model._coerce({date: d}); + assert.deepEqual(where, {date: d}); + + where = model._coerce({date: d.toISOString()}); + assert.deepEqual(where, {date: d}); + }); + + it('should be able to coerce where clause for boolean types', function () { + where = model._coerce({vip: 'true'}); + assert.deepEqual(where, {vip: true}); + + where = model._coerce({vip: true}); + assert.deepEqual(where, {vip: true}); + + where = model._coerce({vip: 'false'}); + assert.deepEqual(where, {vip: false}); + + where = model._coerce({vip: false}); + assert.deepEqual(where, {vip: false}); + + where = model._coerce({vip: '1'}); + assert.deepEqual(where, {vip: true}); + + where = model._coerce({vip: 0}); + assert.deepEqual(where, {vip: false}); + + where = model._coerce({vip: ''}); + assert.deepEqual(where, {vip: false}); + + }); }); describe('Load models from json', function () {