Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
Merge pull request #3 from kalifg/orWhere
Browse files Browse the repository at this point in the history
DBSelect.orWhere
  • Loading branch information
olivierphi committed Aug 14, 2012
2 parents b7ddfc3 + 3b36f87 commit a4068c0
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 16 deletions.
15 changes: 11 additions & 4 deletions README.md
Expand Up @@ -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.
Expand Down
123 changes: 113 additions & 10 deletions lib/dbSelect.js
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions package.json
@@ -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",
Expand All @@ -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": {
Expand Down
67 changes: 67 additions & 0 deletions test/db-select.js
Expand Up @@ -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()
Expand Down

0 comments on commit a4068c0

Please sign in to comment.