2014-10-23 10:53:16 -04:00
|
|
|
'use strict';
|
|
|
|
|
2014-10-20 16:56:45 -04:00
|
|
|
/** @module query-builder */
|
2014-10-23 10:53:16 -04:00
|
|
|
var getArgs = require('getargs'),
|
|
|
|
helpers = require('./helpers');
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Variables controlling the sql building
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
var state = {};
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/*
|
|
|
|
* SQL generation object
|
|
|
|
*
|
|
|
|
* @param {driver} - The syntax driver for the database
|
|
|
|
* @param {adapter} - The database module adapter for running queries
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
var QueryBuilder = function(driver, adapter) {
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
// That 'new' keyword is annoying
|
|
|
|
if ( ! (this instanceof QueryBuilder)) return new QueryBuilder(driver, adapter);
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-27 10:35:16 -04:00
|
|
|
var parser = require('./query-parser')(driver);
|
|
|
|
|
2014-10-27 13:36:10 -04:00
|
|
|
this.driver = driver;
|
|
|
|
this.adapter = adapter;
|
|
|
|
|
2014-10-20 16:56:45 -04:00
|
|
|
/**
|
|
|
|
* "Private" methods
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
var _p = {
|
|
|
|
/**
|
|
|
|
* Complete the sql building based on the type provided
|
|
|
|
*
|
|
|
|
* @param {String} type
|
|
|
|
* @param {String} table
|
2014-10-23 10:53:16 -04:00
|
|
|
* @private
|
2014-10-20 16:56:45 -04:00
|
|
|
* @return {String}
|
|
|
|
*/
|
|
|
|
compile: function (type, table) {
|
2014-10-23 10:53:16 -04:00
|
|
|
// Put together the basic query
|
|
|
|
var sql = _p.compileType(type, table);
|
|
|
|
|
|
|
|
// Set each subClause
|
|
|
|
['queryMap', 'groupString', 'orderString', 'havingMap'].forEach(function(clause) {
|
|
|
|
var param = state[clause];
|
|
|
|
|
|
|
|
if ( ! helpers.isScalar(param))
|
|
|
|
{
|
|
|
|
Object.keys(param).forEach(function(part) {
|
|
|
|
sql += param[part].conjunction + param[part].string;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sql += param;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Append the limit, if it exists
|
|
|
|
if (helpers.isNumber(state.limit))
|
|
|
|
{
|
2014-10-23 13:50:11 -04:00
|
|
|
sql = driver.limit(sql, state.limit, state.offset);
|
2014-10-23 10:53:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return sql;
|
|
|
|
},
|
|
|
|
compileType: function (type, table) {
|
|
|
|
var sql = '';
|
|
|
|
|
2014-10-20 16:56:45 -04:00
|
|
|
switch(type) {
|
|
|
|
case "insert":
|
2014-10-27 10:35:16 -04:00
|
|
|
var params = new Array(state.setArrayKeys.length);
|
|
|
|
params.fill('?');
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
sql = "INSERT INTO " + table + " (";
|
|
|
|
sql += state.setArrayKeys.join(',');
|
|
|
|
sql += ") VALUES (";
|
|
|
|
sql += params.join(',') + ')';
|
2014-10-20 16:56:45 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case "update":
|
2014-10-23 10:53:16 -04:00
|
|
|
sql = "UPDATE " + table + " SET " + state.setString;
|
2014-10-20 16:56:45 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case "delete":
|
2014-10-23 10:53:16 -04:00
|
|
|
sql = "DELETE FROM " + table;
|
2014-10-20 16:56:45 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2014-10-23 10:53:16 -04:00
|
|
|
sql = "SELECT * FROM " + state.fromString;
|
|
|
|
|
|
|
|
// Set the select string
|
|
|
|
if (state.selectString.length > 0)
|
|
|
|
{
|
|
|
|
// Replace the star with the selected fields
|
|
|
|
sql = sql.replace('*', state.selectString);
|
|
|
|
}
|
2014-10-20 16:56:45 -04:00
|
|
|
break;
|
|
|
|
}
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
return sql;
|
2014-10-20 16:56:45 -04:00
|
|
|
},
|
2014-10-23 10:53:16 -04:00
|
|
|
like: function (field, val, pos, like, conj) {
|
2014-10-23 13:50:11 -04:00
|
|
|
field = driver.quoteIdentifiers(field);
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
like = field + " " + like + " ?";
|
|
|
|
|
|
|
|
if (pos == 'before')
|
|
|
|
{
|
|
|
|
val = "%" + val;
|
|
|
|
}
|
|
|
|
else if (pos == 'after')
|
|
|
|
{
|
|
|
|
val = val + "%";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
val = "%" + val + "%";
|
|
|
|
}
|
|
|
|
|
2014-10-24 10:30:54 -04:00
|
|
|
conj = (state.queryMap.length < 1) ? ' WHERE ' : ' ' + conj + ' ';
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.appendMap(conj, like, 'like');
|
|
|
|
|
|
|
|
state.whereValues.push(val);
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Append a clause to the query map
|
|
|
|
*
|
|
|
|
* @param {String} conjunction
|
|
|
|
* @param {String} string
|
|
|
|
* @param {String} type
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
appendMap: function(conjunction, string, type) {
|
|
|
|
state.queryMap.push({
|
|
|
|
type: type,
|
|
|
|
conjunction: conjunction,
|
|
|
|
string: string
|
|
|
|
});
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Handle key/value pairs in an object the same way as individual arguments,
|
|
|
|
* when appending to state
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
2014-10-27 10:35:16 -04:00
|
|
|
mixedSet: function(/* $varName, $valType, $key, [$val] */) {
|
|
|
|
var args = getArgs('$varName:string, $valType:string, $key:object|string|number, [$val]', arguments);
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
var obj = {};
|
|
|
|
|
2014-10-27 10:35:16 -04:00
|
|
|
if (helpers.isScalar(args.$key) && !helpers.isUndefined(args.$val) && !helpers.isNull(args.$val))
|
|
|
|
{
|
|
|
|
obj[args.$key] = args.$val;
|
|
|
|
}
|
2014-10-31 11:57:44 -04:00
|
|
|
else
|
2014-10-23 10:53:16 -04:00
|
|
|
{
|
2014-10-27 10:35:16 -04:00
|
|
|
obj = args.$key;
|
2014-10-23 10:53:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Object.keys(obj).forEach(function(k) {
|
2014-10-23 15:33:20 -04:00
|
|
|
// If a single value for the return
|
2014-10-27 10:35:16 -04:00
|
|
|
if (['key','value'].indexOf(args.$valType) !== -1)
|
2014-10-23 10:53:16 -04:00
|
|
|
{
|
2014-10-27 10:35:16 -04:00
|
|
|
var pushVal = (args.$valType === 'key') ? k : obj[k];
|
|
|
|
state[args.$varName].push(pushVal);
|
2014-10-23 10:53:16 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-10-27 10:35:16 -04:00
|
|
|
state[args.$varName][k] = obj[k];
|
2014-10-23 10:53:16 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-10-27 10:35:16 -04:00
|
|
|
return state[args.$varName];
|
2014-10-23 10:53:16 -04:00
|
|
|
},
|
2014-10-23 15:33:20 -04:00
|
|
|
whereMixedSet: function(/*key, val*/) {
|
|
|
|
var args = getArgs('key:string|object, [val]', arguments);
|
|
|
|
|
|
|
|
state.whereMap = [];
|
|
|
|
|
|
|
|
_p.mixedSet('whereMap', 'both', args.key, args.val);
|
|
|
|
_p.mixedSet('whereValues', 'value', args.key, args.val);
|
2014-10-23 10:53:16 -04:00
|
|
|
},
|
2014-10-30 11:08:00 -04:00
|
|
|
where: function(key, val, defaultConj) {
|
2014-10-23 15:33:20 -04:00
|
|
|
// Normalize key and value and insert into state.whereMap
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.whereMixedSet(key, val);
|
|
|
|
|
|
|
|
Object.keys(state.whereMap).forEach(function(field) {
|
|
|
|
// Split each key by spaces, in case there
|
|
|
|
// is an operator such as >, <, !=, etc.
|
2014-10-24 10:30:54 -04:00
|
|
|
var fieldArray = field.trim().split(' ').map(helpers.stringTrim);
|
2014-10-23 10:53:16 -04:00
|
|
|
|
2014-10-23 13:50:11 -04:00
|
|
|
var item = driver.quoteIdentifiers(fieldArray[0]);
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
// Simple key value, or an operator?
|
2014-10-24 10:30:54 -04:00
|
|
|
item += (fieldArray.length === 1 || fieldArray[1] === '') ? '=?' : " " + fieldArray[1] + " ?";
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
var firstItem = state.queryMap[0],
|
|
|
|
lastItem = state.queryMap[state.queryMap.length - 1];
|
|
|
|
|
|
|
|
// Determine the correct conjunction
|
2014-10-30 11:08:00 -04:00
|
|
|
var conj = defaultConj;
|
|
|
|
if (state.queryMap.length === 0 || firstItem.conjunction.contains('JOIN'))
|
2014-10-23 10:53:16 -04:00
|
|
|
{
|
|
|
|
conj = " WHERE ";
|
|
|
|
}
|
|
|
|
else if (lastItem.type === 'groupStart')
|
|
|
|
{
|
|
|
|
conj = '';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
conj = ' ' + conj + ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
_p.appendMap(conj, item, 'where');
|
2014-10-28 16:46:48 -04:00
|
|
|
|
|
|
|
// Clear the where Map
|
|
|
|
state.whereMap = {};
|
2014-10-23 10:53:16 -04:00
|
|
|
});
|
|
|
|
},
|
2014-10-23 15:33:20 -04:00
|
|
|
having: function(/*key, val, conj*/) {
|
|
|
|
var args = getArgs('key:string|object, [val]:string|number, [conj]:string', arguments);
|
|
|
|
args.conj = args.conj || 'AND';
|
|
|
|
args.val = args.val || null;
|
2014-10-23 10:53:16 -04:00
|
|
|
|
2014-10-23 15:33:20 -04:00
|
|
|
// Normalize key/val and put in state.whereMap
|
|
|
|
_p.whereMixedSet(args.key, args.val);
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
Object.keys(state.whereMap).forEach(function(field) {
|
|
|
|
// Split each key by spaces, in case there
|
|
|
|
// is an operator such as >, <, !=, etc.
|
|
|
|
var fieldArray = field.split(' ').map(helpers.stringTrim);
|
|
|
|
|
2014-10-23 13:50:11 -04:00
|
|
|
var item = driver.quoteIdentifiers(fieldArray[0]);
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
// Simple key value, or an operator?
|
|
|
|
item += (fieldArray.length === 1) ? '=?' : " " + fieldArray[1] + " ?";
|
|
|
|
|
|
|
|
// Put in the having map
|
|
|
|
state.havingMap.push({
|
2014-10-23 15:33:20 -04:00
|
|
|
conjunction: (state.havingMap.length > 0) ? " " + args.conj + " " : ' HAVING ',
|
2014-10-23 10:53:16 -04:00
|
|
|
string: item
|
|
|
|
});
|
|
|
|
});
|
2014-10-28 16:46:48 -04:00
|
|
|
|
|
|
|
// Clear the where Map
|
|
|
|
state.whereMap = {};
|
2014-10-23 10:53:16 -04:00
|
|
|
},
|
2014-10-24 10:30:54 -04:00
|
|
|
whereIn: function(/*key, val, inClause, conj*/) {
|
|
|
|
var args = getArgs('key:string, val:array, inClause:string, conj:string', arguments);
|
|
|
|
|
|
|
|
args.key = driver.quoteIdentifiers(args.key);
|
|
|
|
var params = new Array(args.val.length);
|
|
|
|
params.fill('?');
|
2014-10-23 10:53:16 -04:00
|
|
|
|
2014-10-24 10:30:54 -04:00
|
|
|
args.val.forEach(function(value) {
|
2014-10-23 10:53:16 -04:00
|
|
|
state.whereValues.push(value);
|
|
|
|
});
|
|
|
|
|
2014-10-24 10:30:54 -04:00
|
|
|
args.conj = (state.queryMap.length > 0) ? " " + args.conj + " " : ' WHERE ';
|
|
|
|
var str = args.key + " " + args.inClause + " (" + params.join(',') + ") ";
|
2014-10-23 10:53:16 -04:00
|
|
|
|
2014-10-24 10:30:54 -04:00
|
|
|
_p.appendMap(args.conj, str, 'whereIn');
|
2014-10-23 10:53:16 -04:00
|
|
|
},
|
|
|
|
run: function(type, table, callback, sql, vals) {
|
2014-10-30 09:48:03 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
if ( ! sql)
|
|
|
|
{
|
|
|
|
sql = _p.compile(type, table);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! vals)
|
|
|
|
{
|
|
|
|
vals = state.values.concat(state.whereValues);
|
|
|
|
}
|
|
|
|
|
2014-10-30 11:08:00 -04:00
|
|
|
//console.log(state.queryMap);
|
2014-10-30 09:48:03 -04:00
|
|
|
//console.log(sql);
|
|
|
|
//console.log(vals);
|
2014-11-04 12:34:05 -05:00
|
|
|
//console.log(callback);
|
2014-10-30 09:48:03 -04:00
|
|
|
//console.log('------------------------');
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
// Reset the state so another query can be built
|
|
|
|
_p.resetState();
|
|
|
|
|
|
|
|
// Pass the sql and values to the adapter to run on the database
|
|
|
|
adapter.execute(sql, vals, callback);
|
|
|
|
|
|
|
|
},
|
|
|
|
getCompile: function(type, table, reset) {
|
|
|
|
reset = reset || false;
|
|
|
|
|
|
|
|
var sql = _p.compile(type, table);
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
if (reset) _p.resetState();
|
|
|
|
|
|
|
|
return sql;
|
|
|
|
},
|
|
|
|
resetState: function() {
|
|
|
|
state = {
|
|
|
|
// Arrays/Maps
|
2014-10-23 15:33:20 -04:00
|
|
|
queryMap: [],
|
2014-10-23 10:53:16 -04:00
|
|
|
values: [],
|
|
|
|
whereValues: [],
|
|
|
|
setArrayKeys: [],
|
|
|
|
orderArray: [],
|
|
|
|
groupArray: [],
|
|
|
|
havingMap: [],
|
2014-10-28 16:46:48 -04:00
|
|
|
whereMap: {},
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
// Partials
|
|
|
|
selectString: '',
|
|
|
|
fromString: '',
|
|
|
|
setString: '',
|
|
|
|
orderString: '',
|
|
|
|
groupString: '',
|
|
|
|
|
|
|
|
// Other various values
|
|
|
|
limit: null,
|
|
|
|
offset: null
|
|
|
|
};
|
2014-10-20 16:56:45 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// ! Miscellaneous Methods
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset the object state for a new query
|
|
|
|
*
|
|
|
|
* @memberOf query-builder
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
this.resetQuery = function() {
|
|
|
|
_p.resetState();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the current class state for testing or other purposes
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @return {Object}
|
|
|
|
*/
|
|
|
|
this.getState = function() {
|
|
|
|
return state;
|
|
|
|
};
|
|
|
|
|
2014-10-20 16:56:45 -04:00
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Set up state object
|
|
|
|
this.resetQuery();
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
// ! Query Builder Methods
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specify rows to select in the query
|
|
|
|
*
|
2014-10-23 10:53:16 -04:00
|
|
|
* @param {String|Array} fields - The fields to select from the current table
|
|
|
|
* @return this
|
2014-10-20 16:56:45 -04:00
|
|
|
*/
|
|
|
|
this.select = function(fields) {
|
|
|
|
|
|
|
|
// Split/trim fields by comma
|
2014-10-23 10:53:16 -04:00
|
|
|
fields = (Array.isArray(fields)) ? fields : fields.split(",").map(helpers.stringTrim);
|
2014-10-20 16:56:45 -04:00
|
|
|
|
|
|
|
// Split on 'As'
|
|
|
|
fields.forEach(function (field, index) {
|
|
|
|
if (field.match(/as/i))
|
|
|
|
{
|
2014-10-23 10:53:16 -04:00
|
|
|
fields[index] = field.split(/ as /i).map(helpers.stringTrim);
|
2014-10-20 16:56:45 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-10-23 13:50:11 -04:00
|
|
|
var safeArray = driver.quoteIdentifiers(fields);
|
2014-10-20 16:56:45 -04:00
|
|
|
|
|
|
|
// Join the strings back together
|
|
|
|
safeArray.forEach(function (field, index) {
|
|
|
|
if (Array.isArray(field))
|
|
|
|
{
|
|
|
|
safeArray[index] = safeArray[index].join(' AS ');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
state.selectString += safeArray.join(', ');
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specify the database table to select from
|
|
|
|
*
|
2014-10-23 10:53:16 -04:00
|
|
|
* @param {String} tableName - The table to use for the current query
|
|
|
|
* @return this
|
2014-10-20 16:56:45 -04:00
|
|
|
*/
|
|
|
|
this.from = function(tableName) {
|
|
|
|
// Split identifiers on spaces
|
2014-10-23 10:53:16 -04:00
|
|
|
var identArray = tableName.trim().split(' ').map(helpers.stringTrim);
|
2014-10-20 16:56:45 -04:00
|
|
|
|
|
|
|
// Quote/prefix identifiers
|
2014-10-23 13:50:11 -04:00
|
|
|
identArray[0] = driver.quoteTable(identArray[0]);
|
|
|
|
identArray = driver.quoteIdentifiers(identArray);
|
2014-10-20 16:56:45 -04:00
|
|
|
|
|
|
|
// Put it back together
|
|
|
|
state.fromString = identArray.join(' ');
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Add a 'like/ and like' clause to the query
|
|
|
|
*
|
|
|
|
* @param {String} field - The name of the field to compare to
|
|
|
|
* @param {String} val - The value to compare to
|
|
|
|
* @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.like = function(field, val, pos) {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.like(field, val, pos, ' LIKE ', 'AND');
|
|
|
|
return this;
|
|
|
|
};
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Add a 'not like/ and not like' clause to the query
|
|
|
|
*
|
|
|
|
* @param {String} field - The name of the field to compare to
|
|
|
|
* @param {String} val - The value to compare to
|
|
|
|
* @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
this.notLike = function(field, val, pos) {
|
|
|
|
_p.like(field, val, pos, ' NOT LIKE ', 'AND');
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Add an 'or like' clause to the query
|
|
|
|
*
|
|
|
|
* @param {String} field - The name of the field to compare to
|
|
|
|
* @param {String} val - The value to compare to
|
|
|
|
* @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.orLike = function(field, val, pos) {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.like(field, val, pos, ' LIKE ', 'OR');
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Add an 'or not like' clause to the query
|
|
|
|
*
|
|
|
|
* @param {String} field - The name of the field to compare to
|
|
|
|
* @param {String} val - The value to compare to
|
|
|
|
* @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.orNotLike = function(field, val, pos) {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.like(field, val, pos, ' NOT LIKE ', 'OR');
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Add a 'having' clause
|
|
|
|
*
|
|
|
|
* @param {String|Object} key - The name of the field and the comparision operator, or an object
|
|
|
|
* @param {String|Number} [val] - The value to compare if the value of key is a string
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-23 15:33:20 -04:00
|
|
|
this.having = function(/*key, [val]*/) {
|
|
|
|
var args = getArgs('key:string|object, [val]:string|number', arguments);
|
|
|
|
|
|
|
|
_p.having(args.key, args.val, 'AND');
|
2014-10-23 10:53:16 -04:00
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Add an 'or having' clause
|
|
|
|
*
|
|
|
|
* @param {String|Object} key - The name of the field and the comparision operator, or an object
|
|
|
|
* @param {String|Number} [val] - The value to compare if the value of key is a string
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-23 15:33:20 -04:00
|
|
|
this.orHaving = function(/*key, [val]*/) {
|
|
|
|
var args = getArgs('key:string|object, [val]:string|number', arguments);
|
|
|
|
|
|
|
|
_p.having(args.key, args.val, 'OR');
|
2014-10-23 10:53:16 -04:00
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Set a 'where' clause
|
|
|
|
*
|
|
|
|
* @param {String|Object} key - The name of the field and the comparision operator, or an object
|
|
|
|
* @param {String|Number} [val] - The value to compare if the value of key is a string
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.where = function(key, val) {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.where(key, val, 'AND');
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Set a 'or where' clause
|
|
|
|
*
|
|
|
|
* @param {String|Object} key - The name of the field and the comparision operator, or an object
|
|
|
|
* @param {String|Number} [val] - The value to compare if the value of key is a string
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.orWhere = function(key, val) {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.where(key, val, 'OR');
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Set a 'where in' clause
|
|
|
|
*
|
|
|
|
* @param {String} key - the field to search
|
|
|
|
* @param {Array} val - the array of items to search in
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.whereIn = function(key, val) {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.whereIn(key, val, 'IN', 'AND');
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Set a 'or where in' clause
|
|
|
|
*
|
|
|
|
* @param {String} key - the field to search
|
|
|
|
* @param {Array} val - the array of items to search in
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.orWhereIn = function(key, val) {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.whereIn(key, val, 'IN', 'OR');
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Set a 'where not in' clause
|
|
|
|
*
|
|
|
|
* @param {String} key - the field to search
|
|
|
|
* @param {Array} val - the array of items to search in
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.whereNotIn = function(key, val) {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.whereIn(key, val, 'NOT IN', 'AND');
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Set a 'or where not in' clause
|
|
|
|
*
|
|
|
|
* @param {String} key - the field to search
|
|
|
|
* @param {Array} val - the array of items to search in
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.orWhereNotIn = function(key, val) {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.whereIn(key, val, 'NOT IN', 'OR');
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Set values for insertion or updating
|
|
|
|
*
|
|
|
|
* @param {String|Object} key - The key or object to use
|
|
|
|
* @param {String} [val] - The value if using a scalar key
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-27 10:35:16 -04:00
|
|
|
this.set = function(/* $key, [$val] */) {
|
|
|
|
var args = getArgs('$key, [$val]', arguments);
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
// Set the appropriate state variables
|
2014-10-27 10:35:16 -04:00
|
|
|
_p.mixedSet('setArrayKeys', 'key', args.$key, args.$val);
|
|
|
|
_p.mixedSet('values', 'value', args.$key, args.$val);
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
// Use the keys of the array to make the insert/update string
|
|
|
|
// and escape the field names
|
2014-10-23 13:50:11 -04:00
|
|
|
state.setArrayKeys = state.setArrayKeys.map(driver._quote);
|
2014-10-23 10:53:16 -04:00
|
|
|
|
|
|
|
// Generate the "set" string
|
|
|
|
state.setString = state.setArrayKeys.join('=?,');
|
|
|
|
state.setString += '=?';
|
|
|
|
|
2014-10-20 16:56:45 -04:00
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Add a join clause to the query
|
|
|
|
*
|
2014-10-27 10:35:16 -04:00
|
|
|
* @param {String} table - The table you are joining
|
|
|
|
* @param {String} cond - The join condition.
|
2014-10-23 10:53:16 -04:00
|
|
|
* @param {String} [type='inner'] - The type of join, which defaults to inner
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-27 10:35:16 -04:00
|
|
|
this.join = function(table, cond, type) {
|
|
|
|
type = type || "inner";
|
|
|
|
|
|
|
|
// Prefix/quote table name
|
|
|
|
var table = table.split(' ').map(helpers.stringTrim);
|
|
|
|
table[0] = driver.quoteTable(table[0]);
|
|
|
|
table = table.map(driver.quoteIdentifiers);
|
|
|
|
table = table.join(' ');
|
|
|
|
|
|
|
|
// Parse out the join condition
|
|
|
|
var parsedCondition = parser.compileJoin(cond);
|
|
|
|
var condition = table + ' ON ' + parsedCondition;
|
|
|
|
|
|
|
|
// Append the join condition to the query map
|
|
|
|
_p.appendMap("\n" + type.toUpperCase() + ' JOIN ', condition, 'join');
|
2014-10-20 16:56:45 -04:00
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Group the results by the selected field(s)
|
|
|
|
*
|
|
|
|
* @param {String|Array} field
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.groupBy = function(field) {
|
2014-10-28 09:05:27 -04:00
|
|
|
if ( ! helpers.isScalar(field))
|
2014-10-23 10:53:16 -04:00
|
|
|
{
|
2014-10-23 13:50:11 -04:00
|
|
|
var newGroupArray = field.map(driver.quoteIdentifiers);
|
2014-10-28 09:05:27 -04:00
|
|
|
state.groupArray = state.groupArray.concat(newGroupArray);
|
2014-10-23 10:53:16 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-10-23 13:50:11 -04:00
|
|
|
state.groupArray.push(driver.quoteIdentifiers(field));
|
2014-10-23 10:53:16 -04:00
|
|
|
}
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
state.groupString = ' GROUP BY ' + state.groupArray.join(',');
|
|
|
|
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Order the results by the selected field(s)
|
|
|
|
*
|
2014-10-23 15:49:17 -04:00
|
|
|
* @param {String} field - The field(s) to order by
|
2014-10-23 10:53:16 -04:00
|
|
|
* @param {String} [type='ASC'] - The order direction, ASC or DESC
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
this.orderBy = function(field, type) {
|
|
|
|
type = type || 'ASC';
|
|
|
|
|
|
|
|
// Set the fields for later manipulation
|
2014-10-23 13:50:11 -04:00
|
|
|
field = driver.quoteIdentifiers(field);
|
2014-10-23 15:49:17 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
state.orderArray[field] = type;
|
|
|
|
|
|
|
|
var orderClauses = [];
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
// Flatten key/val pairs into an array of space-separated pairs
|
|
|
|
Object.keys(state.orderArray).forEach(function(key) {
|
|
|
|
orderClauses.push(key + ' ' + state.orderArray[key].toUpperCase());
|
|
|
|
});
|
|
|
|
|
|
|
|
// Set the final string
|
|
|
|
state.orderString = ' ORDER BY ' + orderClauses.join(', ');
|
|
|
|
|
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Put a limit on the query
|
|
|
|
*
|
|
|
|
* @param {Number} limit - The maximum number of rows to fetch
|
|
|
|
* @param {Number} [offset] - The row number to start from
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.limit = function(limit, offset) {
|
|
|
|
state.limit = limit;
|
2014-10-23 10:53:16 -04:00
|
|
|
state.offset = offset || null;
|
2014-10-20 16:56:45 -04:00
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Adds an open paren to the current query for logical grouping
|
|
|
|
*
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.groupStart = function() {
|
2014-10-28 16:46:48 -04:00
|
|
|
var conj = (state.queryMap.length < 1) ? ' WHERE ' : ' AND ';
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.appendMap(conj, '(', 'groupStart');
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Adds an open paren to the current query for logical grouping,
|
|
|
|
* prefixed with 'OR'
|
|
|
|
*
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.orGroupStart = function() {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.appendMap('', ' OR (', 'groupStart');
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Adds an open paren to the current query for logical grouping,
|
|
|
|
* prefixed with 'OR NOT'
|
|
|
|
*
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.orNotGroupStart = function() {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.appendMap('', ' OR NOT (', 'groupStart');
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Ends a logical grouping started with one of the groupStart methods
|
|
|
|
*
|
|
|
|
* @return this
|
|
|
|
*/
|
2014-10-20 16:56:45 -04:00
|
|
|
this.groupEnd = function() {
|
2014-10-23 10:53:16 -04:00
|
|
|
_p.appendMap('', ')', 'groupEnd');
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
return this;
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
// ! Result Methods
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Get the results of the compiled query
|
|
|
|
*
|
|
|
|
* @param {String} [table] - The table to select from
|
|
|
|
* @param {Number} [limit] - A limit for the query
|
|
|
|
* @param {Number} [offset] - An offset for the query
|
|
|
|
* @param {Function} callback - A callback for receiving the result
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
this.get = function(/* [table], [limit], [offset], callback */) {
|
|
|
|
var args = getArgs('[table]:string, [limit]:number, [offset]:number, callback:function', arguments);
|
|
|
|
|
|
|
|
if (args.table) {
|
|
|
|
this.from(args.table);
|
|
|
|
}
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
if (args.limit) {
|
|
|
|
this.limit(args.limit, args.offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the query
|
|
|
|
_p.run('get', args.table, args.callback);
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Run the generated insert query
|
|
|
|
*
|
|
|
|
* @param {String} table - The table to insert into
|
|
|
|
* @param {Object} [data] - Data to insert, if not already added with the 'set' method
|
|
|
|
* @param {Function} callback - Callback for handling response from the database
|
|
|
|
* @return void
|
|
|
|
*/
|
2014-10-27 10:35:16 -04:00
|
|
|
this.insert = function(/* table, data, callback */) {
|
|
|
|
var args = getArgs('table:string, [data]:object, callback:function', arguments);
|
2014-11-04 12:34:05 -05:00
|
|
|
|
2014-10-27 10:35:16 -04:00
|
|
|
if (args.data) {
|
|
|
|
this.set(args.data);
|
2014-10-23 10:53:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the query
|
2014-11-04 12:34:05 -05:00
|
|
|
_p.run('insert', driver.quoteTable(args.table), args.callback);
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-30 09:48:03 -04:00
|
|
|
/**
|
|
|
|
* Insert multiple sets of rows at a time
|
|
|
|
*
|
|
|
|
* @param {String} table - The table to insert into
|
|
|
|
* @param {Array} data - The array of objects containing data rows to insert
|
|
|
|
* @param {Function} callback - Callback for handling database response
|
2014-10-30 11:08:00 -04:00
|
|
|
* @example query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}], callbackFunction);
|
2014-10-30 09:48:03 -04:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
this.insertBatch = function(/* table, data, callback */) {
|
|
|
|
var args = getArgs('table:string, data:array, callback:function', arguments);
|
|
|
|
var batch = driver.insertBatch(args.table, args.data);
|
|
|
|
|
|
|
|
// Run the query
|
|
|
|
_p.run('', '', args.callback, batch.sql, batch.values);
|
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Run the generated update query
|
|
|
|
*
|
|
|
|
* @param {String} table - The table to insert into
|
|
|
|
* @param {Object} [data] - Data to insert, if not already added with the 'set' method
|
|
|
|
* @param {Function} callback - Callback for handling response from the database
|
|
|
|
* @return void
|
|
|
|
*/
|
2014-10-27 13:36:10 -04:00
|
|
|
this.update = function(/*table, data, callback*/) {
|
|
|
|
var args = getArgs('table:string, [data]:object, callback:function', arguments);
|
2014-11-04 12:34:05 -05:00
|
|
|
|
2014-10-27 13:36:10 -04:00
|
|
|
if (args.data) {
|
|
|
|
this.set(args.data);
|
2014-10-23 10:53:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the query
|
2014-11-04 12:34:05 -05:00
|
|
|
_p.run('update', driver.quoteTable(args.table), args.callback);
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Run the generated delete query
|
|
|
|
*
|
|
|
|
* @param {String} table - The table to insert into
|
2014-10-27 13:37:43 -04:00
|
|
|
* @param {Object} [where] - Where clause for delete statement
|
2014-10-23 10:53:16 -04:00
|
|
|
* @param {Function} callback - Callback for handling response from the database
|
|
|
|
* @return void
|
|
|
|
*/
|
2014-10-28 14:40:03 -04:00
|
|
|
this.delete = function (/*table, [where], callback*/) {
|
2014-10-30 09:48:03 -04:00
|
|
|
var args = getArgs('table:string, [where]:object, callback:function', arguments);
|
2014-10-27 13:36:10 -04:00
|
|
|
|
|
|
|
if (args.where)
|
|
|
|
{
|
|
|
|
this.where(args.where);
|
|
|
|
}
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
// Run the query
|
2014-11-04 12:34:05 -05:00
|
|
|
_p.run('delete', driver.quoteTable(args.table), args.callback);
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
// ! Methods returning SQL
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return generated select query SQL
|
|
|
|
*
|
2014-10-23 10:53:16 -04:00
|
|
|
* @param {String} [table] - the name of the table to retrieve from
|
|
|
|
* @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
|
|
|
|
* @return String
|
2014-10-20 16:56:45 -04:00
|
|
|
*/
|
2014-10-28 16:46:48 -04:00
|
|
|
this.getCompiledSelect = function(/*table, reset*/) {
|
|
|
|
var args = getArgs('[table]:string, [reset]:boolean', arguments);
|
|
|
|
if (args.table)
|
2014-10-23 10:53:16 -04:00
|
|
|
{
|
2014-10-28 16:46:48 -04:00
|
|
|
this.from(args.table);
|
2014-10-23 10:53:16 -04:00
|
|
|
}
|
2014-10-20 16:56:45 -04:00
|
|
|
|
2014-10-28 16:46:48 -04:00
|
|
|
return _p.getCompile('get', args.table, args.reset);
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
2014-10-23 10:53:16 -04:00
|
|
|
/**
|
|
|
|
* Return generated insert query SQL
|
|
|
|
*
|
|
|
|
* @param {String} table - the name of the table to insert into
|
|
|
|
* @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
|
|
|
|
* @return {String}
|
|
|
|
*/
|
|
|
|
this.getCompiledInsert = function(table, reset) {
|
2014-11-04 12:34:05 -05:00
|
|
|
return _p.getCompile('insert', driver.quoteTable(table), reset);
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-10-23 10:53:16 -04:00
|
|
|
* Return generated update query SQL
|
2014-10-20 16:56:45 -04:00
|
|
|
*
|
2014-10-23 10:53:16 -04:00
|
|
|
* @param {String} table - the name of the table to update
|
|
|
|
* @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
|
|
|
|
* @return {String}
|
2014-10-20 16:56:45 -04:00
|
|
|
*/
|
2014-10-23 10:53:16 -04:00
|
|
|
this.getCompiledUpdate = function(table, reset) {
|
2014-11-04 12:34:05 -05:00
|
|
|
return _p.getCompile('update', driver.quoteTable(table), reset);
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-10-23 10:53:16 -04:00
|
|
|
* Return generated delete query SQL
|
2014-10-20 16:56:45 -04:00
|
|
|
*
|
2014-10-23 10:53:16 -04:00
|
|
|
* @param {String} table - the name of the table to delete from
|
|
|
|
* @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
|
|
|
|
* @return {String}
|
2014-10-20 16:56:45 -04:00
|
|
|
*/
|
2014-10-23 10:53:16 -04:00
|
|
|
this.getCompiledDelete = function(table, reset) {
|
2014-11-04 12:34:05 -05:00
|
|
|
return _p.getCompile('delete', driver.quoteTable(table), reset);
|
2014-10-20 16:56:45 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
return this;
|
2014-10-23 10:53:16 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = QueryBuilder;
|