Permalink
Browse files

Merge pull request #3 from kalifg/orWhere

DBSelect.orWhere
  • Loading branch information...
2 parents b7ddfc3 + 3b36f87 commit a4068c04fee7428ff30a8a2a6293582f25655908 @DrBenton committed Aug 14, 2012
Showing with 194 additions and 16 deletions.
  1. +11 −4 README.md
  2. +113 −10 lib/dbSelect.js
  3. +3 −2 package.json
  4. +67 −0 test/db-select.js
View
@@ -125,10 +125,17 @@ All these methods returns exactly the sames results, whatever the chosen databas
Furthermore, Node-DBI provides a DBSelect class which allows easy and readable SQL "SELECT" Strings building. At the moment, it provides the following methods :
* __from( tableName, fieldsArray )__ : adds a table in the FROM clause, and adds its fields to the SELECT
- * __where( whereStr, value )__ : adds a WHERE clause ; if "value" is not null, all the "?" occurences of the "whereStr" will be replaced with the safely escaped value
- * __limit( nbResults, startIndex )__ : set the LIMIT clause ; "startIndex" param is optionnal
- * __order( fieldName, direction )__ : adds a ORDER BY clause ; if "direction" is not set, it will be set to "ASC"
- * __join( tableName, joinStr, fieldsArray, joinType )__ : adds a JOIN clause ; if "joinType" is not set, it will be set to "INNER"
+ * __where( whereStr, value )__: adds a WHERE clause using AND ; if __value__ is not null, all the "?" occurences in __whereStr__ will be replaced with the safely escaped value
+ * __orWhere( whereStr, value )__ : just like __where__ but adds a WHERE clause using OR
+ * __whereGroup( num )__ : opens __num__ parenthetical groupings to WHERE clause (ie adds __num__ open parentheses) ; __num__ defaults to 1
+ * __whereGroupClose( num )__ :
+ * closes __num__ parenthetical groupings of WHERE clause (ie adds __num__ closed parentheses)
+ * __num__ defaults to 1
+ * will not close groups that do not exist
+ * open groups will be closed automatically
+ * __limit( nbResults, startIndex )__ : set the LIMIT clause ; __startIndex__ param is optionnal
+ * __order( fieldName, direction )__ : adds a ORDER BY clause ; if __direction__ is not set, it will be set to "ASC"
+ * __join( tableName, joinStr, fieldsArray, joinType )__ : adds a JOIN clause ; if __joinType__ is not set, it will be set to "INNER"
* __distinct()__ : adds a DISTINCT() to the query
* __groupyBy( fieldName )__ : adds a GROUPY BY clause
* __assemble()__ : converts ou DBSelect object to an SQL SELECT string.
View
@@ -88,13 +88,19 @@ DBSelect.prototype.from = function( tableName, fieldsArray )
* @param {String|Number|Array|null} value
* @returns {DBSelect}
*/
-DBSelect.prototype.where = function( whereStr, value )
+DBSelect.prototype.where = function( whereStr, value, clauseType )
{
+ var pushCount = 1;
+
+ if (!clauseType) {
+ clauseType = 'AND';
+ }
+
+ if ((clauseType == 'OR' || clauseType == 'AND') && !whereStr) {
+ throw new Error('WHERE String is mandatory !');
+ }
- if( ! whereStr )
- throw new Error('WHERE String is mandatory !');
-
- if( value ) {
+ if( (clauseType == 'OR' || clauseType == 'AND') && whereStr && value ) {
if(_.isArray(value))
value = value.map((function (v) { return this._adapter.escape(v); }).bind(this)).join(', ');
else
@@ -103,12 +109,63 @@ DBSelect.prototype.where = function( whereStr, value )
whereStr = whereStr.replace( /\?/g, value );
}
- this._where.push( whereStr );
+ if ((clauseType == '(' || clauseType == ')') && typeof value == 'number') {
+ pushCount = value;
+ }
+
+ for (var i = 0; i < pushCount; i++) {
+ this._where.push( { clauseType: clauseType, string: whereStr } );
+ }
return this;
};
+/**
+ * Adds a WHERE clause with an OR instead of AND.
+ *
+ * @param {String} whereStr
+ * @param {String|Number|Array|null} value
+ * @returns {DBSelect}
+ */
+DBSelect.prototype.orWhere = function(whereStr, value) {
+ return this.where(whereStr, value, 'OR');
+};
+
+/**
+ * Adds a WHERE clause grouping start
+ *
+ * @param {Number} groupsToOpen
+ * @returns {DBSelect}
+ */
+DBSelect.prototype.whereGroup = function(groupsToOpen) {
+ return this.whereGroupHandler(groupsToOpen, '(');
+};
+
+/**
+ * Adds a WHERE clause grouping end
+ *
+ * @param {Number} groupsToClose
+ * @returns {DBSelect}
+ */
+DBSelect.prototype.whereGroupClose = function(groupsToClose) {
+ return this.whereGroupHandler(groupsToClose, ')');
+};
+
+/**
+ * Adds WHERE grouping clauses
+ *
+ * @param {Number} groups
+ * @param {String} groupType
+ * @returns {DBSelect}
+ */
+DBSelect.prototype.whereGroupHandler = function(groups, groupType) {
+ if (typeof groups === 'undefined') {
+ groups = 1;
+ }
+
+ return this.where(null, groups, groupType);
+};
/**
* Adds a LIMIT clause.
@@ -264,10 +321,56 @@ DBSelect.prototype.assemble = DBSelect.prototype.toString = function()
}
// "WHERE" clause : this._where
- if( this._where.length > 0 )
- {
- this._where = _.unique( this._where );
- sql += ( (completeQuery) ? ' WHERE ' : '' ) + this._where.join(' AND ');
+ if (this._where.length > 0) {
+ this._where = _.unique( this._where );
+ sql += ( (completeQuery) ? ' WHERE ' : '' );
+
+ var firstValue = true;
+ var openGroups = 0;
+ var whereClause = null;
+ var whereString = null;
+ var clauseType = null;
+ var startGroups = 0;
+
+ for (var i = 0; i < this._where.length; i++) {
+ whereClause = this._where[i];
+ clauseType = whereClause.clauseType;
+ whereString = whereClause.string;
+
+ switch (clauseType) {
+ case '(':
+ startGroups++;
+ openGroups++;
+ break;
+
+ case ')':
+ if (openGroups > 0) {
+ sql += ')';
+ openGroups--;
+ }
+
+ break;
+
+ case 'AND':
+ case 'OR':
+ if (!firstValue) {
+ sql += ' ' + (clauseType == 'OR' ? 'OR' : 'AND') + ' ';
+ } else {
+ firstValue = false;
+ }
+
+ for (var j = 0; j < startGroups; j++) {
+ sql += '(';
+ }
+
+ startGroups = 0;
+ sql += whereString;
+ }
+ }
+
+ for (var k = 0; k < openGroups; k++) {
+ sql += ')';
+ }
}
// "GROUP BY" clause : this._group
View
@@ -1,7 +1,7 @@
{
"name": "node-dbi",
"description": "A Database abstraction layer for Node.js, bundled with several DB engines adapters",
- "version": "0.5.0",
+ "version": "0.6.0",
"homepage": "https://github.com/DrBenton/Node-DBI",
"repository": {
"type": "git",
@@ -10,7 +10,8 @@
"authors": [
"Dr. Benton (http://github.com/DrBenton)",
"David Schoen <dave@lyte.id.au>",
- "Fabian Bornhofen (http://fabianbornhofen.blogspot.com)"
+ "Fabian Bornhofen (http://fabianbornhofen.blogspot.com)",
+ "Michael Dwyer (http://github.com/kalifg)"
],
"main": "dbWrapper.js",
"directories": {
View
@@ -191,6 +191,73 @@ var adapterTestSuite = function( adapterName, callback )
}
},
+
+ 'an advanced WHERE clause, with disordered from(), where() and orWhere() calls': {
+ topic: function()
+ {
+ return dbWrapper.getSelect()
+ .where('enabled=1')
+ .where( 'id=?', 10 )
+ .from('user')
+ .where( 'first_name=?', 'Dr.')
+ .where( 'last_name LIKE ?', '%Benton%' )
+ .orWhere( 'nickname=?', '"`\'éàèç' );
+ },
+
+ 'assembled Select is OK': function( select )
+ {
+ var user = dbWrapper._adapter.escapeTable('user');
+ assert.equal( select.assemble(), 'SELECT '+user+'.* FROM '+user+' WHERE enabled=1 AND id=10 AND first_name=\'Dr.\' AND last_name LIKE \'%Benton%\' OR nickname='+dbWrapper.escape('"`\'éàèç') );
+ }
+
+ },
+
+ 'an advanced WHERE clause, with disordered from(), where() and orWhere() calls and parenthetical grouping': {
+ topic: function()
+ {
+ return dbWrapper.getSelect()
+ .where('enabled=1')
+ .where( 'id=?', 10 )
+ .from('user')
+ .where( 'first_name=?', 'Dr.')
+ .whereGroup()
+ .where( 'last_name LIKE ?', '%Benton%' )
+ .orWhere( 'nickname=?', '"`\'éàèç' );
+ },
+
+ 'assembled Select is OK': function( select )
+ {
+ var user = dbWrapper._adapter.escapeTable('user');
+ assert.equal( select.assemble(), 'SELECT '+user+'.* FROM '+user+' WHERE enabled=1 AND id=10 AND first_name=\'Dr.\' AND (last_name LIKE \'%Benton%\' OR nickname='+dbWrapper.escape('"`\'éàèç')+')' );
+ }
+
+ },
+
+ 'an advanced WHERE clause, with more parenthetical grouping': {
+ topic: function()
+ {
+ return dbWrapper.getSelect()
+ .from('user')
+ .whereGroupClose() // Erroneous whereGroupClose to be handled
+ .whereGroup(3) // Multiple group start
+ .where('enabled=1')
+ .where( 'id=?', 10 )
+ .whereGroupClose()
+ .where( 'first_name=?', 'Dr.')
+ .whereGroup()
+ .where( 'last_name LIKE ?', '%Benton%' )
+ .orWhere( 'nickname=?', '"`\'éàèç' )
+ .whereGroupClose(2);
+ // Automatic closing of leftover groups
+ },
+
+ 'assembled Select is OK': function( select )
+ {
+ var user = dbWrapper._adapter.escapeTable('user');
+ assert.equal( select.assemble(), 'SELECT '+user+'.* FROM '+user+' WHERE (((enabled=1 AND id=10) AND first_name=\'Dr.\' AND (last_name LIKE \'%Benton%\' OR nickname='+dbWrapper.escape('"`\'éàèç')+')))' );
+ }
+
+ },
'a "WHERE clause" only SELECT ': {
topic: function()

0 comments on commit a4068c0

Please sign in to comment.