Skip to content

Instantly share code, notes, and snippets.

@afranioce
Last active November 21, 2016 13:54
Show Gist options
  • Save afranioce/ce01ee663b55bdd5288983c2857f155a to your computer and use it in GitHub Desktop.
Save afranioce/ce01ee663b55bdd5288983c2857f155a to your computer and use it in GitHub Desktop.
Query Build
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* @author Afranio Martins<afranioce@gmail.com.br>
* Usando como exemplo DBAL QueryBuilder Doctrine 2.0
*
*/
var QueryBuilder = function(connection){
/* The query types. */
this.SELECT = 0;
this.DELETE = 1;
this.UPDATE = 2;
this.INSERT = 3;
/** The builder states. */
this.STATE_DIRTY = 0;
this.STATE_CLEAN = 1;
this.EQ = '=';
this.NEQ = '<>';
this.LT = '<';
this.LTE = '<=';
this.GT = '>';
this.GTE = '>=';
this.IN = 'IN';
this.NOT_IN = 'NOT IN';
this.LIKE = 'LIKE';
this.NOT_LIKE = 'NOT LIKE';
/**
* Constant that represents an AND composite expression
*/
this.TYPE_AND = 'AND';
/**
* Constant that represents an OR composite expression
*/
this.TYPE_OR = 'OR';
var _sql;
var _type = this.SELECT;
var _state = this.STATE_CLEAN;
var _firstResult = null;
var _maxResults = null;
var _result = null;
var _db;
var _sqlParts = {
'select' : [],
'from' : [],
'join' : [],
'set' : [],
'where' : null,
'groupBy' : [],
'having' : null,
'orderBy' : [],
'field' : {
'fields' : [],
'values' : []
}
};
var _operators = [
this.EQ,
this.NEQ,
this.LT,
this.LTE,
this.GT,
this.GTE,
this.IN,
this.NOT_IN,
this.LIKE,
this.NOT_LIKE
];
this.getConnection = function(){
return connection;
}
this.getState = function(){
return _state;
}
this.execute = function(){
var database = this.getConnection() +'.sqlite';
var file = Titanium.Filesystem.getFile(
Titanium.Filesystem.applicationSupportDirectory
+ '/db/' + database
);
//Install Database
if(!file.exists()) {
Ti.Database.install(database, 'db');
}
//Open DB
_db = Ti.Database.open('db');
_result = _db.execute(this.getSQL());
return this;
}
this.fetchAll = function(){
if(_.isNull(_result))
return this;
var rows = [];
var index = 0;
while (_result.isValidRow()){
var row = {};
row[_result.getFieldName(index)] = _result.field(index);
rows.push(row);
index++;
_result.next();
}
//Close result set
_result.close();
//Close database
_db.close();
return rows;
}
this.getSQL = function(){
if (_sql !== null && _state === this.STATE_CLEAN) {
return _sql;
}
_sql = '';
switch (_type) {
case this.DELETE:
_sql = getSQLForDelete();
break;
case this.UPDATE:
_sql = getSQLForUpdate();
break;
case this.INSERT:
_sql = getSQLForInsert();
break;
case this.SELECT:
default:
_sql = getSQLForSelect();
break;
}
_state = this.STATE_CLEAN;
_sql = _sql;
return _sql;
}
this.setFirstResult = function(firstResult){
_state = this.STATE_DIRTY;
_firstResult = firstResult;
return this;
}
this.getFirstResult = function(){
return _firstResult;
}
this.setMaxResults = function(maxResults){
_state = this.STATE_DIRTY;
_maxResults = maxResults;
return this;
}
this.getMaxResults = function (){
return _maxResults;
}
this.add = function(sqlPartName, sqlPart, append){
if(_.isUndefined(append)) append = false;
var isObject = _.isObject(sqlPart);
var isMultiple = _.isObject(_sqlParts[sqlPartName]);
if (isMultiple && !isObject) {
sqlPart = [sqlPart];
}
_state = this.STATE_DIRTY;
if (append) {
if(/field/i.exec(sqlPartName)){
_.each(_.keys(sqlPart), function(key){
_sqlParts[sqlPartName][key] = _.union(_sqlParts[sqlPartName][key], sqlPart[key]);
});
} else if(/orderBy|groupBy|select|set|where|having/i.exec(sqlPartName)){
if(/where|having/i.exec(sqlPartName))
_sqlParts[sqlPartName] = _.union(_.toArray(_sqlParts[sqlPartName]), [sqlPart]);
else
_sqlParts[sqlPartName] = _.union(_.toArray(_sqlParts[sqlPartName]), _.toArray(sqlPart));
} else if (isObject && _.isObject(sqlPart[_.keys(sqlPart)])) {
var key = _.keys(sqlPart);
_sqlParts[sqlPartName][key] = _.union(_.toArray(_sqlParts[sqlPartName][key]), [sqlPart[key]]);
} else if (isMultiple) {
_.extend(_sqlParts[sqlPartName], [sqlPart]);
} else {
_.extend(_sqlParts[sqlPartName], [sqlPart]);
}
return this;
}
_sqlParts[sqlPartName] = sqlPart;
return this;
}
this.select = function(select){
_type = this.SELECT;
if (_.isUndefined(select)) {
select = ['*'];
}
var selects = _.isArray(select) ? select : arguments;
return this.add('select', selects, false);
}
this.addSelect = function(select){
_type = this.SELECT;
if (select.length == 0) {
return this;
}
var selects = _.isArray(select) ? select : arguments;
return this.add('select', selects, true);
}
this.delete = function(del, alias){
_type = this.DELETE;
if (!del) {
return this;
}
return this.add('from', {
'table': del,
'alias': alias
});
}
this.update = function(update, alias){
_type = this.UPDATE;
if (!update) {
return this;
}
return this.add('from', {
'table': update,
'alias': alias
});
}
this.insert = function(insert){
_type = this.INSERT;
if (!insert) {
return this;
}
return this.add('from', {
'table': insert
});
}
this.from = function(from, alias){
return this.add('from', {
'table': from,
'alias': alias
}, true);
}
this.join = function(fromAlias, join, alias, condition){
if(_.isUndefined(condition)) condition = null;
return this.innerJoin(fromAlias, join, alias, condition);
}
this.innerJoin = function(fromAlias, join, alias, condition){
if(_.isUndefined(condition)) condition = null;
var obj = {};
obj[fromAlias] = {
'joinType': 'inner',
'joinTable': join,
'joinAlias': alias,
'joinCondition': condition
};
return this.add('join', obj, true);
}
this.leftJoin = function(fromAlias, join, alias, condition){
if(_.isUndefined(condition)) condition = null;
var obj = {};
obj[fromAlias] = {
'joinType': 'left',
'joinTable': join,
'joinAlias': alias,
'joinCondition': condition
};
return this.add('join', obj, true);
}
this.rightJoin = function(fromAlias, join, alias, condition){
if(_.isUndefined(condition)) condition = null;
var obj = {};
obj[fromAlias] = {
'joinType': 'right',
'joinTable': join,
'joinAlias': alias,
'joinCondition': condition
};
return this.add('join', obj, true);
}
this.set = function(key, value){
return this.add('set', key + ' = ' + value, true);
}
this.where = function(field, value, operator){
return this.condition('where', null, field, value, operator);
}
this.andWhere = function(field, value, operator){
return this.condition('where', this.TYPE_AND, field, value, operator);
}
this.orWhere = function(field, value, operator){
return this.condition('where', this.TYPE_OR, field, value, operator);
}
this.isNull = function(field){
return this.condition('where', this.TYPE_OR, field, null, 'IS NULL');
}
this.isNotNull = function(field){
return this.condition('where', this.TYPE_OR, field, null, 'IS NOT NULL');
}
this.condition = function(condition, type, field, value, operator){
value = !_.isNull(value) ? value : '';
if (_.isArray(value)) {
operator = !_.isUndefined(operator) ? operator : this.IN;
operator = operator.toUpperCase().trim();
var con = [this.IN, this.NOT_IN];
operator = con[_.indexOf(con, operator)];
value = "('" + implode("', '", value) + "')";
}
operator = !_.isUndefined(operator) && _.indexOf(_operators, operator) ? operator : this.EQ;
var parts = field + ' ' + operator + ' ' + value;
return this.add(condition, {
'type': !_.isNull(type) ? type : '',
'parts': parts.trim()
}, true);
}
this.groupBy = function(group){
if (group.length == 0) {
return this;
}
var groups = _.isArray(group) ? group : arguments;
return this.add('groupBy', groups, false);
}
this.addGroupBy = function(group){
if (group.length == 0) {
return this;
}
var groups = _.isArray(group) ? group : arguments;
return this.add('groupBy', groups, true);
}
this.having = function(field, value, operator){
return this.condition('having', '', field, value, operator);
}
this.andHaving = function(field, value, operator){
return this.condition('having', this.TYPE_AND, field, value, operator);
}
this.orHaving = function(field, value, operator){
return this.condition('having', this.TYPE_OR, field, value, operator);
}
this.orderBy = function(sort, order){
return this.add('orderBy', sort + ' ' + (!order ? 'ASC' : order), false);
}
this.addOrderBy = function(sort, order){
return this.add('orderBy', sort + ' ' + (!order ? 'ASC' : order), true);
}
this.field = function(field, value){
var fields = {};
fields[field] = value;
return this.fields(fields);
}
this.fields = function(fields){
if(!_.isObject(fields)){
return this;
}
return this.add('field', {
'fields': _.keys(fields),
'values': _.values(fields)
}, true);
}
this.getQueryPart = function(queryPartName){
return _sqlParts[queryPartName];
}
this.getQueryParts = function(){
return _sqlParts;
}
this.resetQueryParts = function(queryPartNames){
if (_.isUndefined(queryPartNames)) {
queryPartNames = key(_sqlParts);
}
for (var queryPartName in queryPartNames) {
this.resetQueryPart(queryPartNames[queryPartName]);
}
return this;
}
this.resetQueryPart = function(queryPartName){
_sqlParts[queryPartName] = _.isObject(_sqlParts[queryPartName])
? {} : null;
_state = this.STATE_DIRTY;
return this;
}
var getSQLForSelect = function(){
var query = 'SELECT ' + implode(', ', _sqlParts['select']) + ' FROM ';
var fromClauses = {};
// Loop through all FROM clauses
_.each(_sqlParts['from'], function(from){
var fromClause = from['table'] + ' ' + from['alias'];
if (!_.isUndefined(_sqlParts['join'][from['alias']])) {
_.each(_sqlParts['join'][from['alias']], function(join){
fromClause += ' ' + join['joinType'].toUpperCase()
+ ' JOIN ' + join['joinTable'] + ' ' + join['joinAlias']
+ ' ON ' + (join['joinCondition']);
})
}
fromClauses[from['alias']] = fromClause;
})
// loop through all JOIN clauses for validation purpose
_.each(_sqlParts['join'], function(fromAlias, joins){
if (_.isEmpty(fromClauses[fromAlias])) {
Ti.API.Error('Error: ' + fromAlias + ' >>> ' + key(fromClauses));
}
});
query += implode(', ', _(fromClauses).values())
+ (!_.isNull(_sqlParts['where']) ? ' WHERE' + doCondition('where') : '')
+ (!_.isEmpty(_sqlParts['groupBy']) ? ' GROUP BY '+ implode(', ', _sqlParts['groupBy']) : '')
+ (!_.isNull(_sqlParts['having']) ? ' HAVING' + doCondition('having') : '')
+ (!_.isEmpty(_sqlParts['orderBy']) ? 'ORDER BY ' + implode(', ', _sqlParts['orderBy']) : '')
return (_.isNull(_maxResults) && _.isNull(_firstResult))
? query
: doOffset(query, _maxResults, _firstResult);
}
var doCondition = function(condition){
var conditions = '';
_.each(_sqlParts[condition], function(cond){
conditions += cond['type'] + ' (' + cond['parts'] + ') ';
});
return conditions;
}
var doOffset = function(query, limit, offset){
if (!_.isNull(limit)) {
query += ' LIMIT ' + parseInt(limit, 10);
}
if (offset !== null) {
var offset = parseInt(offset, 10);
if (offset < 0) {
Ti.API.error("Error: LIMIT argument offset=offset is not valid");
}
query += ' OFFSET ' + offset;
}
return query;
}
/**
* Converts this instance into an UPDATE string in SQL.
*
* @return string
*/
var getSQLForUpdate = function(){
var table = _sqlParts['from']['table']
+ (_sqlParts['from']['alias'] ? ' ' + _sqlParts['from']['alias'] : '');
var query = 'UPDATE ' + table
+ ' SET ' + implode(', ', _sqlParts['set'])
+ (!_.isNull(_sqlParts['where']) ? ' WHERE' + doCondition('where') : '');
return query;
}
this.__toString = function(){
return this.getSQL();
}
/**
* Converts this instance into a DELETE string in SQL.
*
* @return string
*/
var getSQLForDelete = function (){
var table = _sqlParts['from']['table'] + (_sqlParts['from']['alias'] ? ' '
+ _sqlParts['from']['alias'] : '');
var query = 'DELETE FROM ' + table
+ (!_.isNull(_sqlParts['where']) ? ' WHERE' + doCondition('where') : '');
return query;
}
/**
* Converts this instance into a INSERT string in SQL.
*
* @return string
*/
var getSQLForInsert = function (){
var query = 'INSERT INTO ' + _sqlParts['from']['table'];
if(!_.isUndefined(_sqlParts['field']['fields']))
query += '('+ implode(', ', _sqlParts['field']['fields']) + ") VALUES ('";
query += implode("\', \'", _sqlParts['field']['values']) + "\')";
return query;
}
var implode = function(separator, array){
var array = _.toArray(array);
if(!_.isEmpty(array))
return array.length === 1 ? array[0] : array.join(separator)
}
}
var db = new QueryBuilder('db_teste')
.select('type')
.addSelect('body', 'created', 't.tid')
.from('node', 'n')
.setFirstResult(2)
.setMaxResults(10)
.orderBy('nid', 'DESC')
.addOrderBy('tid', 'ASC')
.groupBy('tid')
.addGroupBy('nid')
.innerJoin('n', 'taxonomy_term', 't', 'n.nid = t.tid')
.leftJoin('n', 'taxonomy_term_data', 'td', 'n.nid = td.tid')
.where('n.nid', '2')
.andWhere('n.nid', '4')
.orWhere('n.nid', '5')
.andWhere('n.nid', [1, 2, 3])
.orWhere('n.nid', [1, 2, 3], 'NOT IN')
.having('t.tid','1', '=')
.andHaving('t.tid2','1', '=')
.isNull('n.nid')
.isNotNull('n.nid');
/*
.delete('sdfs')
.where('n.nid', '5')
.andWhere('n.nid', [1, 2, 3])
.orWhere('n.nid', [1, 2, 3], 'NOT IN')
.update('node', 'n')
.set('nid', 2)
.where('n.nid', '5')
.andWhere('n.nid', [1, 2, 3])
.orWhere('n.nid', [1, 2, 3], 'NOT IN')
.insert('node')
.field('nid', 1)
.field('title', 1)
.fields({
'title': 'teste de titulo',
'body' : '<p>sdfsdf</p>'
})
/*
db.trucate(table);
db.drop('table').exists();
db.create('node').notExists().fields({
'nid': {
'type': 'serial',
'null': false,
'description': 'sdfsdf',
},
'title': {
'type': 'string',
'length': 32,
'null': false,
'description': 'sdfsdf',
}
})
.primaryKey('nid')
.uniqueKey()
.indexes()
.foreingKey()
*/
window.onload = document.write(db.getSQL());
//module.exports = QueryBuilder;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment