From f17716272c8aaf56cb5e397a363f0147ffb54300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Drouyer?= Date: Sat, 27 Oct 2012 20:00:40 +0200 Subject: [PATCH 1/4] Fixed IN and NOT IN when searching on strings --- lib/adapters/mysql.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/adapters/mysql.js b/lib/adapters/mysql.js index 2fd19a06..eb1155e3 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -157,6 +157,11 @@ MySQL.prototype.toDatabase = function (prop, val) { this.toDatabase(prop, val[1]); } else if (operator == 'inq' || operator == 'nin') { if (!(val.propertyIsEnumerable('length')) && typeof val === 'object' && typeof val.length === 'number') { //if value is array + for (var i = 0; i < val.length; i++) { + if (typeof val[i] === 'string') { + val[i] = this.client.escape(val[i]); + } + } return val.join(','); } else { return val; From d5b00033500c061302172daeb1cf843e1b307730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Drouyer?= Date: Sat, 27 Oct 2012 20:47:25 +0200 Subject: [PATCH 2/4] Fixed postgres adapter for IN and NIN when values are strings and where there is 0 value --- lib/adapters/postgres.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/adapters/postgres.js b/lib/adapters/postgres.js index 1e634de1..4eefe8ac 100644 --- a/lib/adapters/postgres.js +++ b/lib/adapters/postgres.js @@ -164,6 +164,12 @@ PG.prototype.toDatabase = function (prop, val) { if (operator === 'between') { return this.toDatabase(prop, val[0]) + ' AND ' + this.toDatabase(prop, val[1]); } + if (operator === 'inq' || operator === 'nin') { + for (var i = 0; i < val.length; i++) { + val[i] = escape(val[i]); + } + return val.join(','); + } } if (prop.type.name === 'Number') { if (!val && val!=0) { @@ -239,6 +245,10 @@ PG.prototype.toFilter = function (model, filter) { } else if (conds[key].constructor.name === 'Object') { var condType = Object.keys(conds[key])[0]; var sqlCond = '"' + key + '"'; + if ((condType == 'inq' || condType == 'nin') && filterValue.length == 0) { + fields.push(condType == 'inq' ? 'FALSE' : 'TRUE'); + return true; + } switch (condType) { case 'gt': sqlCond += ' > '; From fc46de162f9df84742d03f456b124a63df72e7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Drouyer?= Date: Sat, 27 Oct 2012 20:48:21 +0200 Subject: [PATCH 3/4] generalize escape on IN and NOT IN values --- lib/adapters/mysql.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/adapters/mysql.js b/lib/adapters/mysql.js index eb1155e3..5d464029 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -158,9 +158,7 @@ MySQL.prototype.toDatabase = function (prop, val) { } else if (operator == 'inq' || operator == 'nin') { if (!(val.propertyIsEnumerable('length')) && typeof val === 'object' && typeof val.length === 'number') { //if value is array for (var i = 0; i < val.length; i++) { - if (typeof val[i] === 'string') { - val[i] = this.client.escape(val[i]); - } + val[i] = this.client.escape(val[i]); } return val.join(','); } else { From 4c95f1dcad379d5ca4b25a1964c8807fd92ffe9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Drouyer?= Date: Sat, 27 Oct 2012 20:51:45 +0200 Subject: [PATCH 4/4] added test cases for IN and NOT IN. Only in mysql and postgres for the moment --- test/common_test.js | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/test/common_test.js b/test/common_test.js index 6c243f13..09efafc7 100644 --- a/test/common_test.js +++ b/test/common_test.js @@ -711,6 +711,82 @@ function testOrm(schema) { } }); + + if ( + schema.name === 'mysql' || + schema.name === 'postgres' + ) + it('should allow IN or NOT IN', function (test) { + User.destroyAll(function () { + User.create({name: 'User A', age: 21}, done); + User.create({name: 'User B', age: 22}, done); + User.create({name: 'User C', age: 23}, done); + User.create({name: 'User D', age: 24}, done); + User.create({name: 'User E', age: 25}, done); + }); + + var users = 5; + function done() { + if (--users === 0) makeTest(); + } + + function makeTest() { + // IN with empty array should return nothing + User.all({where: {name: {inq: []}}}, function (err, users) { + test.equal(users.length, 0, 'IN with empty array returns nothing'); + ok(); + }); + + // NOT IN with empty array should return everything + User.all({where: {name: {nin: []}}}, function (err, users) { + test.equal(users.length, 5, 'NOT IN with empty array returns everything'); + ok(); + }); + + // IN [User A] returns user with name = User A + User.all({where: {name: {inq: ['User A']}}}, function (err, users) { + test.equal(users.length, 1, 'IN searching one existing value returns 1 user'); + test.equal(users[0].name, 'User A', 'IN [User A] returns user with name = User A'); + ok(); + }); + + // NOT IN [User A] returns users with name != User A + User.all({where: {name: {nin: ['User A']}}}, function (err, users) { + test.equal(users.length, 4, 'IN [User A] returns users with name != User A'); + ok(); + }); + + // IN [User A, User B] returns users with name = User A OR name = User B + User.all({where: {name: {inq: ['User A', 'User B']}}}, function (err, users) { + test.equal(users.length, 2, 'IN searching two existing values returns 2 users'); + ok(); + }); + + // NOT IN [User A, User B] returns users with name != User A AND name != User B + User.all({where: {name: {nin: ['User A', 'User B']}}}, function (err, users) { + test.equal(users.length, 3, 'NOT IN searching two existing values returns users with name != User A AND name != User B'); + ok(); + }); + + // IN works with numbers too + User.all({where: {age: {inq: [21, 22]}}}, function (err, users) { + test.equal(users.length, 2, 'IN works with numbers too'); + ok(); + }); + + // NOT IN works with numbers too + User.all({where: {age: {nin: [21, 22]}}}, function (err, users) { + test.equal(users.length, 3, 'NOT IN works with numbers too'); + ok(); + }); + } + + var tests = 8; + function ok() { + if (--tests === 0) test.done(); + } + }); + it('should handle order clause with direction', function (test) { var wait = 0; var emails = [