From a0a9fae9c6856d4142516c4099de5aaa7d9b7919 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 6 Jun 2014 08:19:41 -0700 Subject: [PATCH 1/6] Enhance comparators for memory connector --- lib/connectors/memory.js | 55 +++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/lib/connectors/memory.js b/lib/connectors/memory.js index ce466221..670ece80 100644 --- a/lib/connectors/memory.js +++ b/lib/connectors/memory.js @@ -377,24 +377,61 @@ function applyFilter(filter) { } if (example.inq) { - if (!value) return false; - for (var i = 0; i < example.inq.length; i += 1) { - if (example.inq[i] == value) return true; + // if (!value) return false; + for (var i = 0; i < example.inq.length; i++) { + if (example.inq[i] == value) { + return true; + } } return false; } - if (isNum(example.gt) && example.gt < value) return true; - if (isNum(example.gte) && example.gte <= value) return true; - if (isNum(example.lt) && example.lt > value) return true; - if (isNum(example.lte) && example.lte >= value) return true; + if (testInEquality(example, value)) { + return true; + } } // not strict equality return (example !== null ? example.toString() : example) == (value !== null ? value.toString() : value); } - function isNum(n) { - return typeof n === 'number'; + /** + * Compare two values + * @param {*} val1 The 1st value + * @param {*} val2 The 2nd value + * @returns {number} 0: =, postive: >, negative < + * @private + */ + function compare(val1, val2) { + if (typeof val1 === 'number') { + return val1 - val2; + } + if (typeof val1 === 'string') { + return (val1 > val2) ? 1 : ((val1 < val2) ? -1 : 0); + } + if (typeof val1 === 'boolean') { + return val1 - val2; + } + if (val1 instanceof Date) { + return val1.getTime() - ((val2 && val2.getTime()) || 0); + } + // Return NaN if we don't know how to compare + return (val1 === val2) ? 0 : NaN; + } + + function testInEquality(example, val) { + if ('gt' in example) { + return compare(example.gt, val) > 0; + } + if ('gte' in example) { + return compare(example.gte, val) >= 0; + } + if ('lt' in example) { + return compare(example.lt, val) < 0; + } + if ('lte' in example) { + return compare(example.lte, val) <= 0; + } + return false; } } From 0191e3c2dbfe4d398c3fa82564d07a6b014f63db Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 6 Jun 2014 08:48:05 -0700 Subject: [PATCH 2/6] Add more tests --- lib/connectors/memory.js | 11 ++-- test/basic-querying.test.js | 122 +++++++++++++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 6 deletions(-) diff --git a/lib/connectors/memory.js b/lib/connectors/memory.js index 670ece80..e136e81f 100644 --- a/lib/connectors/memory.js +++ b/lib/connectors/memory.js @@ -412,7 +412,8 @@ function applyFilter(filter) { return val1 - val2; } if (val1 instanceof Date) { - return val1.getTime() - ((val2 && val2.getTime()) || 0); + var result = val1.getTime() - ((val2 && val2.getTime()) || 0); + return result; } // Return NaN if we don't know how to compare return (val1 === val2) ? 0 : NaN; @@ -420,16 +421,16 @@ function applyFilter(filter) { function testInEquality(example, val) { if ('gt' in example) { - return compare(example.gt, val) > 0; + return compare(val, example.gt) > 0; } if ('gte' in example) { - return compare(example.gte, val) >= 0; + return compare(val, example.gte) >= 0; } if ('lt' in example) { - return compare(example.lt, val) < 0; + return compare(val, example.lt) < 0; } if ('lte' in example) { - return compare(example.lte, val) <= 0; + return compare(val, example.lte) <= 0; } return false; } diff --git a/test/basic-querying.test.js b/test/basic-querying.test.js index ae72ca45..3b95dd68 100644 --- a/test/basic-querying.test.js +++ b/test/basic-querying.test.js @@ -10,6 +10,7 @@ describe('basic-querying', function () { User = db.define('User', { name: {type: String, index: true, sort: true}, email: {type: String, index: true}, + birthday: {type: Date, index: true}, role: {type: String, index: true}, order: {type: Number, index: true, sort: true} }); @@ -175,6 +176,123 @@ describe('basic-querying', function () { }); }); + it('should support date "gte" that is satisfied', function (done) { + User.find({where: { birthday: { "gte": new Date('1980-12-08') } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 1); + users[0].name.should.equal('John Lennon'); + done(); + }); + }); + + it('should support date "gt" that is not satisfied', function (done) { + User.find({where: { birthday: { "gt": new Date('1980-12-08') } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 0); + done(); + }); + }); + + it('should support date "gt" that is satisfied', function (done) { + User.find({where: { birthday: { "gt": new Date('1980-12-07') } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 1); + users[0].name.should.equal('John Lennon'); + done(); + }); + }); + + it('should support date "lt" that is satisfied', function (done) { + User.find({where: { birthday: { "lt": new Date('1980-12-07') } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 1); + users[0].name.should.equal('Paul McCartney'); + done(); + }); + }); + + it('should support number "gte" that is satisfied', function (done) { + User.find({where: { order: { "gte": 3} + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 4); + users[0].name.should.equal('George Harrison'); + done(); + }); + }); + + it('should support number "gt" that is not satisfied', function (done) { + User.find({where: { order: { "gt": 6 } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 0); + done(); + }); + }); + + it('should support number "gt" that is satisfied', function (done) { + User.find({where: { order: { "gt": 5 } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 1); + users[0].name.should.equal('Ringo Starr'); + done(); + }); + }); + + it('should support number "lt" that is satisfied', function (done) { + User.find({where: { order: { "lt": 2 } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 1); + users[0].name.should.equal('Paul McCartney'); + done(); + }); + }); + + it('should support string "gte" that is satisfied', function (done) { + User.find({where: { name: { "gte": 'Paul McCartney'} + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 4); + users[0].name.should.equal('Paul McCartney'); + done(); + }); + }); + + it('should support string "gt" that is not satisfied', function (done) { + User.find({where: { name: { "gt": 'xyz' } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 0); + done(); + }); + }); + + it('should support string "gt" that is satisfied', function (done) { + User.find({where: { name: { "gt": 'Paul McCartney' } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 3); + users[0].name.should.equal('Ringo Starr'); + done(); + }); + }); + + it('should support string "lt" that is satisfied', function (done) { + User.find({where: { name: { "lt": 'Paul McCartney' } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 2); + users[0].name.should.equal('John Lennon'); + done(); + }); + }); + it('should only include fields as specified', function (done) { var remaining = 0; @@ -214,7 +332,7 @@ describe('basic-querying', function () { } sample({name: true}).expect(['name']); - sample({name: false}).expect(['id', 'email', 'role', 'order']); + sample({name: false}).expect(['id', 'email', 'role', 'order', 'birthday']); sample({name: false, id: true}).expect(['id']); sample({id: true}).expect(['id']); sample('id').expect(['id']); @@ -363,12 +481,14 @@ function seed(done) { name: 'John Lennon', email: 'john@b3atl3s.co.uk', role: 'lead', + birthday: new Date('1980-12-08'), order: 2 }, { name: 'Paul McCartney', email: 'paul@b3atl3s.co.uk', role: 'lead', + birthday: new Date('1942-06-18'), order: 1 }, {name: 'George Harrison', order: 5}, From 171642ff4889b288008769a299f57024e5d34283 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 6 Jun 2014 09:09:56 -0700 Subject: [PATCH 3/6] Make sure the records are sorted by seq --- test/basic-querying.test.js | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/test/basic-querying.test.js b/test/basic-querying.test.js index 3b95dd68..764e6ad6 100644 --- a/test/basic-querying.test.js +++ b/test/basic-querying.test.js @@ -8,6 +8,7 @@ describe('basic-querying', function () { db = getSchema(); User = db.define('User', { + seq: {type: Number, index: true}, name: {type: String, index: true, sort: true}, email: {type: String, index: true}, birthday: {type: Date, index: true}, @@ -177,7 +178,7 @@ describe('basic-querying', function () { }); it('should support date "gte" that is satisfied', function (done) { - User.find({where: { birthday: { "gte": new Date('1980-12-08') } + User.find({order: 'seq', where: { birthday: { "gte": new Date('1980-12-08') } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -187,7 +188,7 @@ describe('basic-querying', function () { }); it('should support date "gt" that is not satisfied', function (done) { - User.find({where: { birthday: { "gt": new Date('1980-12-08') } + User.find({order: 'seq', where: { birthday: { "gt": new Date('1980-12-08') } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 0); @@ -196,7 +197,7 @@ describe('basic-querying', function () { }); it('should support date "gt" that is satisfied', function (done) { - User.find({where: { birthday: { "gt": new Date('1980-12-07') } + User.find({order: 'seq', where: { birthday: { "gt": new Date('1980-12-07') } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -206,7 +207,7 @@ describe('basic-querying', function () { }); it('should support date "lt" that is satisfied', function (done) { - User.find({where: { birthday: { "lt": new Date('1980-12-07') } + User.find({order: 'seq', where: { birthday: { "lt": new Date('1980-12-07') } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -216,7 +217,7 @@ describe('basic-querying', function () { }); it('should support number "gte" that is satisfied', function (done) { - User.find({where: { order: { "gte": 3} + User.find({order: 'seq', where: { order: { "gte": 3} }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 4); @@ -226,7 +227,7 @@ describe('basic-querying', function () { }); it('should support number "gt" that is not satisfied', function (done) { - User.find({where: { order: { "gt": 6 } + User.find({order: 'seq', where: { order: { "gt": 6 } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 0); @@ -235,7 +236,7 @@ describe('basic-querying', function () { }); it('should support number "gt" that is satisfied', function (done) { - User.find({where: { order: { "gt": 5 } + User.find({order: 'seq', where: { order: { "gt": 5 } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -245,7 +246,7 @@ describe('basic-querying', function () { }); it('should support number "lt" that is satisfied', function (done) { - User.find({where: { order: { "lt": 2 } + User.find({order: 'seq', where: { order: { "lt": 2 } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -255,7 +256,7 @@ describe('basic-querying', function () { }); it('should support string "gte" that is satisfied', function (done) { - User.find({where: { name: { "gte": 'Paul McCartney'} + User.find({order: 'seq', where: { name: { "gte": 'Paul McCartney'} }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 4); @@ -265,7 +266,7 @@ describe('basic-querying', function () { }); it('should support string "gt" that is not satisfied', function (done) { - User.find({where: { name: { "gt": 'xyz' } + User.find({order: 'seq', where: { name: { "gt": 'xyz' } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 0); @@ -274,7 +275,7 @@ describe('basic-querying', function () { }); it('should support string "gt" that is satisfied', function (done) { - User.find({where: { name: { "gt": 'Paul McCartney' } + User.find({order: 'seq', where: { name: { "gt": 'Paul McCartney' } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 3); @@ -284,7 +285,7 @@ describe('basic-querying', function () { }); it('should support string "lt" that is satisfied', function (done) { - User.find({where: { name: { "lt": 'Paul McCartney' } + User.find({order: 'seq', where: { name: { "lt": 'Paul McCartney' } }}, function (err, users) { should.not.exist(err); users.should.have.property('length', 2); @@ -332,7 +333,7 @@ describe('basic-querying', function () { } sample({name: true}).expect(['name']); - sample({name: false}).expect(['id', 'email', 'role', 'order', 'birthday']); + sample({name: false}).expect(['id', 'seq', 'email', 'role', 'order', 'birthday']); sample({name: false, id: true}).expect(['id']); sample({id: true}).expect(['id']); sample('id').expect(['id']); @@ -478,6 +479,7 @@ function seed(done) { var count = 0; var beatles = [ { + seq: 0, name: 'John Lennon', email: 'john@b3atl3s.co.uk', role: 'lead', @@ -485,16 +487,17 @@ function seed(done) { order: 2 }, { + seq: 1, name: 'Paul McCartney', email: 'paul@b3atl3s.co.uk', role: 'lead', birthday: new Date('1942-06-18'), order: 1 }, - {name: 'George Harrison', order: 5}, - {name: 'Ringo Starr', order: 6}, - {name: 'Pete Best', order: 4}, - {name: 'Stuart Sutcliffe', order: 3} + {seq: 2, name: 'George Harrison', order: 5}, + {seq: 3, name: 'Ringo Starr', order: 6}, + {seq: 4, name: 'Pete Best', order: 4}, + {seq: 5, name: 'Stuart Sutcliffe', order: 3} ]; User.destroyAll(function () { beatles.forEach(function (beatle) { From e0d3fec7436faa997a4910014e0a801871ba4b00 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 6 Jun 2014 09:10:47 -0700 Subject: [PATCH 4/6] Fix the typo --- lib/connectors/memory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/connectors/memory.js b/lib/connectors/memory.js index e136e81f..0b7a36cd 100644 --- a/lib/connectors/memory.js +++ b/lib/connectors/memory.js @@ -398,7 +398,7 @@ function applyFilter(filter) { * Compare two values * @param {*} val1 The 1st value * @param {*} val2 The 2nd value - * @returns {number} 0: =, postive: >, negative < + * @returns {number} 0: =, positive: >, negative < * @private */ function compare(val1, val2) { From b5816506e07e7c07c79621d9817efb4caa43cb3c Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 6 Jun 2014 09:28:08 -0700 Subject: [PATCH 5/6] Add boolean tests --- test/basic-querying.test.js | 57 ++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/test/basic-querying.test.js b/test/basic-querying.test.js index 764e6ad6..ef0bfb7f 100644 --- a/test/basic-querying.test.js +++ b/test/basic-querying.test.js @@ -13,7 +13,8 @@ describe('basic-querying', function () { email: {type: String, index: true}, birthday: {type: Date, index: true}, role: {type: String, index: true}, - order: {type: Number, index: true, sort: true} + order: {type: Number, index: true, sort: true}, + vip: {type: Boolean} }); db.automigrate(done); @@ -294,6 +295,46 @@ describe('basic-querying', function () { }); }); + it('should support boolean "gte" that is satisfied', function (done) { + User.find({order: 'seq', where: { vip: { "gte": true} + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 3); + users[0].name.should.equal('John Lennon'); + done(); + }); + }); + + it('should support boolean "gt" that is not satisfied', function (done) { + User.find({order: 'seq', where: { vip: { "gt": true } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 0); + done(); + }); + }); + + it('should support boolean "gt" that is satisfied', function (done) { + User.find({order: 'seq', where: { vip: { "gt": false } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 3); + users[0].name.should.equal('John Lennon'); + done(); + }); + }); + + it('should support boolean "lt" that is satisfied', function (done) { + User.find({order: 'seq', where: { vip: { "lt": true } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 2); + users[0].name.should.equal('George Harrison'); + done(); + }); + }); + + it('should only include fields as specified', function (done) { var remaining = 0; @@ -333,7 +374,7 @@ describe('basic-querying', function () { } sample({name: true}).expect(['name']); - sample({name: false}).expect(['id', 'seq', 'email', 'role', 'order', 'birthday']); + sample({name: false}).expect(['id', 'seq', 'email', 'role', 'order', 'birthday', 'vip']); sample({name: false, id: true}).expect(['id']); sample({id: true}).expect(['id']); sample('id').expect(['id']); @@ -484,7 +525,8 @@ function seed(done) { email: 'john@b3atl3s.co.uk', role: 'lead', birthday: new Date('1980-12-08'), - order: 2 + order: 2, + vip: true }, { seq: 1, @@ -492,12 +534,13 @@ function seed(done) { email: 'paul@b3atl3s.co.uk', role: 'lead', birthday: new Date('1942-06-18'), - order: 1 + order: 1, + vip: true }, - {seq: 2, name: 'George Harrison', order: 5}, - {seq: 3, name: 'Ringo Starr', order: 6}, + {seq: 2, name: 'George Harrison', order: 5, vip: false}, + {seq: 3, name: 'Ringo Starr', order: 6, vip: false}, {seq: 4, name: 'Pete Best', order: 4}, - {seq: 5, name: 'Stuart Sutcliffe', order: 3} + {seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true} ]; User.destroyAll(function () { beatles.forEach(function (beatle) { From bb57fcbe110a8ced8603f690ea92dd1d0203d8d7 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 10 Jun 2014 16:11:50 -0700 Subject: [PATCH 6/6] Fix the comparison for null/boolean values --- lib/connectors/memory.js | 10 +++++++--- lib/dao.js | 8 ++++++-- test/basic-querying.test.js | 27 +++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/lib/connectors/memory.js b/lib/connectors/memory.js index 0b7a36cd..ab54f959 100644 --- a/lib/connectors/memory.js +++ b/lib/connectors/memory.js @@ -402,21 +402,25 @@ function applyFilter(filter) { * @private */ function compare(val1, val2) { + if(val1 == null || val2 == null) { + // Either val1 or val2 is null or undefined + return val1 == val2 ? 0 : NaN; + } if (typeof val1 === 'number') { return val1 - val2; } if (typeof val1 === 'string') { - return (val1 > val2) ? 1 : ((val1 < val2) ? -1 : 0); + return (val1 > val2) ? 1 : ((val1 < val2) ? -1 : (val1 == val2) ? 0 : NaN); } if (typeof val1 === 'boolean') { return val1 - val2; } if (val1 instanceof Date) { - var result = val1.getTime() - ((val2 && val2.getTime()) || 0); + var result = val1 - val2; return result; } // Return NaN if we don't know how to compare - return (val1 === val2) ? 0 : NaN; + return (val1 == val2) ? 0 : NaN; } function testInEquality(example, val) { diff --git a/lib/dao.js b/lib/dao.js index f5a6886f..d4bd40de 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -551,10 +551,14 @@ DataAccessObject._coerce = function (where) { // Coerce the array items if (Array.isArray(val)) { for (var i = 0; i < val.length; i++) { - val[i] = DataType(val[i]); + if (val[i] !== null && val[i] !== undefined) { + val[i] = DataType(val[i]); + } } } else { - val = DataType(val); + if (val !== null && val !== undefined) { + val = DataType(val); + } } // Rebuild {property: {operator: value}} if (operator) { diff --git a/test/basic-querying.test.js b/test/basic-querying.test.js index ef0bfb7f..eaccb348 100644 --- a/test/basic-querying.test.js +++ b/test/basic-querying.test.js @@ -256,6 +256,33 @@ describe('basic-querying', function () { }); }); + it('should support number "gt" that is satisfied by null value', function (done) { + User.find({order: 'seq', where: { order: { "gt": null } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 0); + done(); + }); + }); + + it('should support number "lt" that is not satisfied by null value', function (done) { + User.find({order: 'seq', where: { order: { "lt": null } + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 0); + done(); + }); + }); + + it('should support string "gte" that is satisfied by null value', function (done) { + User.find({order: 'seq', where: { name: { "gte": null} + }}, function (err, users) { + should.not.exist(err); + users.should.have.property('length', 0); + done(); + }); + }); + it('should support string "gte" that is satisfied', function (done) { User.find({order: 'seq', where: { name: { "gte": 'Paul McCartney'} }}, function (err, users) {