mirror of
https://gitlab.com/Shinobi-Systems/ShinobiCE.git
synced 2025-03-09 15:40:15 +00:00
352 lines
No EOL
11 KiB
JavaScript
352 lines
No EOL
11 KiB
JavaScript
var _ = require('underscore')
|
|
sqlKeywords = ['select', 'top', 'from', 'join', 'where', 'groupby', 'orderby', 'having']; // keep "top x" in mind
|
|
logicalOperators = ['!=', '<=', '>=', '<', '>', '=', '!in', 'in', 'like'];
|
|
|
|
var hasTop, hasWhere, hasOrderBy, processed = [], whereValsWithSpaces, hasOr, filterFields, operators, sqlishFilter, filter;
|
|
parseOperatorsInArray = function(equation){
|
|
var completeArr = [], tmpArr = [];
|
|
sqlKeywords.forEach(function (e, k) { // for each operator
|
|
if (completeArr.length === 0) { // if empty, split equation and do first load to completeArr.
|
|
tmpArr = equation.split(e);
|
|
spliceOperatorIntoTmpArr(tmpArr, e); // adds operator between every item in array
|
|
tmpArr = tmpArr.filter(function (item) { return item.length > 0; });
|
|
tmpArr.forEach(function (e, k) {
|
|
|
|
if (e.indexOf('where') > -1){
|
|
buildWhere(e);
|
|
}
|
|
completeArr.push(e.replace(/\s/g, ''));
|
|
});
|
|
} else {
|
|
for (var n = 0; n < completeArr.length; n++) {
|
|
if (completeArr[n].indexOf(e) > -1 && completeArr[n].length > 1) {
|
|
var idx = n;
|
|
tmpArr = completeArr[n].split(e);
|
|
spliceOperatorIntoTmpArr(tmpArr, e);
|
|
tmpArr = tmpArr.filter(function (item) { return item.length > 0; });
|
|
completeArr.splice(idx, 1); // remove old text element
|
|
for (var x = 0; x < tmpArr.length ; x++) {
|
|
var newIdx = (idx + x);
|
|
completeArr.splice(newIdx, 0, tmpArr[x]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
return completeArr;
|
|
};
|
|
|
|
spliceOperatorIntoTmpArr = function(tmpArr, e){
|
|
var tmpLen = tmpArr.length + (tmpArr.length - 1);
|
|
for (var i = 1; i < tmpLen; i++) {
|
|
tmpArr.splice(i, 0, e);
|
|
i++;
|
|
}
|
|
};
|
|
|
|
buildWhere = function(e){
|
|
|
|
var originalWhere = hasOrderBy ? e.substring(e.indexOf('where'), e.indexOf(' order by')) : e.substring(e.indexOf('where'), e.length);
|
|
var splitWhere = originalWhere.split(' '), splitLength = splitWhere.length;
|
|
var isRightSide = false, isOperator = false, isKeyword = false, filterValsToPush = [];
|
|
|
|
splitWhere.forEach(function(el, idx){
|
|
|
|
isKeyword = ((sqlKeywords.indexOf(el) > -1) || el === 'and' || el === 'or');
|
|
isOperator = (logicalOperators.indexOf(el) > -1); // true if operator
|
|
|
|
if (isKeyword){
|
|
isRightSide = false; // false if sql keyword
|
|
}
|
|
|
|
if (isOperator){
|
|
operators.push(el);
|
|
isRightSide = true;
|
|
}
|
|
|
|
if (!isOperator && !isKeyword){
|
|
if (isRightSide){
|
|
filterValsToPush.push(el);
|
|
} else {
|
|
filterFields.push(el);
|
|
}
|
|
}
|
|
|
|
if ((el === 'and' || el === 'or' || idx === (splitLength - 1)) && filterValsToPush.length > 0){
|
|
var preservedVal = filterValsToPush.join(' ');
|
|
whereValsWithSpaces.push(preservedVal);
|
|
filterValsToPush = [];
|
|
}
|
|
});
|
|
|
|
for (var i = 0; i < filterFields.length; i++){
|
|
sqlishFilter.push(({ field: filterFields[i], operator: operators[i], value: whereValsWithSpaces[i] }));
|
|
}
|
|
};
|
|
|
|
getNext = function(arr, howMany){
|
|
howMany = howMany ? howMany : 1;
|
|
if (arr.length > 0){
|
|
|
|
var lastIn = arr.splice(0, howMany);
|
|
processed.push(lastIn);
|
|
return lastIn;
|
|
}
|
|
};
|
|
|
|
getLimit = function(arr){
|
|
var topN = arr[0].replace(/[^0-9.]/g, '');
|
|
// remove top n from fields portion of arr
|
|
arr[0] = arr[0].replace(/\d+/g, '');
|
|
return parseInt(topN);
|
|
};
|
|
|
|
getProjection = function(arr){
|
|
var projection = {}, selectFields = _.first(getNext(arr));
|
|
if (selectFields !== '*'){
|
|
selectFields.replace(/\s/g, '').split(',').forEach(function(e, k){
|
|
var show = 1;
|
|
if (e.substr(0,1) === '!'){
|
|
show = 0;
|
|
e = e.substr(1,1000);
|
|
}
|
|
projection[e] = show;
|
|
});
|
|
}
|
|
return projection;
|
|
};
|
|
|
|
processFilter = function(filterObj, filter){
|
|
var field = filterObj['field'];
|
|
var operator = filterObj['operator'];
|
|
var val = !isNaN(filterObj['value']) ? parseFloat(filterObj['value']) : filterObj['value'];
|
|
|
|
switch (operator){
|
|
case '=':
|
|
filter[field] = val;
|
|
break;
|
|
case '!=':
|
|
filter[field] = { $ne: val };
|
|
break;
|
|
case '>':
|
|
filter[field] = { $gt: val };
|
|
break;
|
|
case '<':
|
|
filter[field] = { $lt: val };
|
|
break;
|
|
case '>=':
|
|
filter[field] = { $gte: val };
|
|
break;
|
|
case '<=':
|
|
filter[field] = { $lte: val };
|
|
break;
|
|
case 'in':
|
|
filter[field] = { $in: val.split(',') };
|
|
break;
|
|
case '!in':
|
|
filter[field] = { $nin: val.split(',') };
|
|
break;
|
|
case 'like':
|
|
filter[field] = { $regex: '^' + val + '.*' };
|
|
break;
|
|
case '!like':
|
|
filter[field] = { $not: (new RegExp('/' + val + '/')) };
|
|
break;
|
|
}
|
|
return filter;
|
|
};
|
|
|
|
getSort = function(arr){
|
|
var sort = {}, sortFields = getNext(arr)[0], field, order, val;
|
|
sortFields.split(',').forEach(function(e, k){
|
|
if (e.substring(e.length - 4, e.length) === 'desc'){
|
|
field = e.substring(0, e.length - 4);
|
|
val = -1;
|
|
} else if (e.substring(e.length - 3, e.length) === 'asc'){
|
|
field = e.substring(0, e.length - 3);
|
|
val = 1;
|
|
} else {
|
|
field = e;
|
|
val = 1;
|
|
}
|
|
|
|
sort[field] = val;
|
|
});
|
|
return sort;
|
|
};
|
|
|
|
validateCollection = function(collection){
|
|
return _.contains(collections,collection) ? collection : 'Invalid Collection.';
|
|
};
|
|
|
|
// ######## Start of custom auto-complete code ########
|
|
function interceptAutoComplete(prefix, global, parts){
|
|
if (prefix.length === 0){ // space only
|
|
return ["')"];
|
|
}
|
|
|
|
var first = parts[0].toLowerCase();
|
|
var expandToText = snippetMap[first];
|
|
var lastChar = first.substring(first.length - 1, first.length);
|
|
var lastTwoChars = first.substring(first.length - 2, first.length);
|
|
|
|
if (first === 'sel'){
|
|
sqlQuery = "db.sql('select * from ";
|
|
return [sqlQuery];
|
|
} else if (expandToText){
|
|
return [expandToText];
|
|
} else if (!queryHasCollection && isNaN(lastChar)) {
|
|
return printCollections(first);
|
|
} else if (!queryHasCollection) {
|
|
return selectCollection(lastTwoChars, lastChar);
|
|
} else if (queryHasCollection && isNaN(lastChar)) {
|
|
return printFields(first);
|
|
} else {
|
|
return selectField(lastTwoChars, lastChar)
|
|
}
|
|
}
|
|
|
|
function printMatches(isField){
|
|
if (matches.length > 0){
|
|
print('\n');
|
|
matches.forEach(function(m, i){
|
|
var str = i + ': ' + m
|
|
print(colorize(str, 'green', true, false));
|
|
});
|
|
} else {
|
|
resetGlobalVars();
|
|
return [''];
|
|
}
|
|
}
|
|
|
|
function printCollections(first){
|
|
// No collection has been selected yet, and user isn't passing number for selection...
|
|
if (_.contains(collections, first)){
|
|
selectedCollection = first;
|
|
sqlQuery += selectedCollection;
|
|
|
|
return [selectedCollection];
|
|
}
|
|
|
|
matches = _.filter(collections, function(c){
|
|
return c.toLowerCase().substring(0, (first.length)) === first;
|
|
});
|
|
|
|
printMatches();
|
|
}
|
|
|
|
function selectCollection(lastTwoChars, lastChar){
|
|
// no collection is selected yet, but user is passing number for selection...
|
|
var num = !isNaN(lastTwoChars) ? lastTwoChars : lastChar;
|
|
selectedCollection = matches[num];
|
|
queryHasCollection = true;
|
|
print('\n');
|
|
generateFieldTable(selectedCollection);
|
|
|
|
if (sqlQuery === ''){
|
|
sqlQuery = "db.sql('select * from " + selectedCollection;
|
|
return [sqlQuery];
|
|
}
|
|
|
|
sqlQuery += selectedCollection;
|
|
return [selectedCollection];
|
|
}
|
|
|
|
function printFields(first){
|
|
// collection has been selected and user is trying to select field based on initial string
|
|
var collection = collectionFields[selectedCollection];
|
|
var filteredFields = _.filter(collection, function(c){
|
|
return c.field.toLowerCase().substring(0, (first.length)) === first;
|
|
});
|
|
matches = _.map(_.sortBy(filteredFields, 'field'), function(d, i) {
|
|
return d.field;
|
|
});
|
|
|
|
printMatches();
|
|
}
|
|
|
|
function selectField(lastTwoChars, lastChar){
|
|
// collection has been selected, as well as field string, now user is passing number to select one...
|
|
var num = !isNaN(lastTwoChars) ? lastTwoChars : lastChar;
|
|
var field = matches[num];
|
|
|
|
return [field];
|
|
}
|
|
|
|
function showCollections(){
|
|
return db.getCollectionNames();
|
|
}
|
|
|
|
function resetGlobalVars (){
|
|
queryHasCollection = false;
|
|
sqlQuery = '';
|
|
matches = null;
|
|
selectedCollection = '';
|
|
}
|
|
|
|
function generateFieldTable(collection){
|
|
var table = new AsciiTable(collection);
|
|
table.setHeading('#', 'Field', 'Types');
|
|
|
|
var fields = collectionFields[collection];
|
|
_.map(_.sortBy(fields, 'field'), function(d, i) {
|
|
return table.addRow(i, d.field, d.types)
|
|
});
|
|
return print(colorize(table, 'cyan', true, false));
|
|
}
|
|
module.exports = {
|
|
parseSQL : function(sql){
|
|
sql = sql.replace(/NOT LIKE/g,'!like').toLowerCase()
|
|
whereValsWithSpaces = [], filterFields = [], operators = [], sqlishFilter = [], filter = {};
|
|
hasTop = (sql.indexOf(' top ') > -1), hasWhere = (sql.indexOf('where') > -1), hasOrderBy = (sql.indexOf('order by') > -1), hasOr = (sql.indexOf(' or ') > -1);
|
|
var limit, join, sort;
|
|
var arr = parseOperatorsInArray(sql);
|
|
|
|
|
|
// getNext(arr); // remove Select
|
|
|
|
if (hasTop){
|
|
getNext(arr); // remove top
|
|
limit = getLimit(arr);
|
|
}
|
|
console.log(sql)
|
|
console.log(arr)
|
|
var projection = getProjection(arr);
|
|
|
|
getNext(arr); // remove From
|
|
|
|
|
|
if (hasWhere){
|
|
var orObj = {}, orArr = [];
|
|
sqlishFilter.forEach(function(f, fk){
|
|
if (hasOr){
|
|
orArr.push(processFilter(f, {}));
|
|
} else {
|
|
processFilter(f, filter);
|
|
}
|
|
});
|
|
|
|
if (hasOr){
|
|
filter = { $or: orArr };
|
|
}
|
|
|
|
getNext(arr, 2); // remove where and clause, since its handled earlier
|
|
}
|
|
|
|
if (hasOrderBy){
|
|
getNext(arr); // remove order by
|
|
sort = getSort(arr);
|
|
}
|
|
|
|
|
|
var ret = {
|
|
projection: projection,
|
|
filter: filter,
|
|
sort: sort || {},
|
|
limit: limit || 20
|
|
};
|
|
|
|
console.log('Converted Command: ' + 'db.' + ret.collection + '.find(' + JSON.stringify(ret.filter) + ', ' + JSON.stringify(ret.projection) + ').sort(' + JSON.stringify(ret.sort) + ').limit(' + ret.limit + ')');
|
|
return ret;
|
|
}
|
|
} |