tg-client-query-builder
Advanced tools
Comparing version 2.7.1 to 2.8.0
{ | ||
"name": "tg-client-query-builder", | ||
"version": "2.7.1", | ||
"version": "2.8.0", | ||
"description": "Teselagen Client Side (browser) SQL Query Builder", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
// const log = require('../log'); | ||
const FilterExpression = require('./filter-expression'); | ||
const _ = require('lodash'); | ||
const FilterExpression = require("./filter-expression"); | ||
const _ = require("lodash"); | ||
const combineQueries = require('./combine-queries'); | ||
const expressionOperators = [ | ||
'greaterThan', | ||
'inList', | ||
'lessThan', | ||
'lessThanOrEqual', | ||
'equals', | ||
'greaterThanOrEqual', | ||
'notEquals', | ||
'notNull', | ||
'isNull', | ||
'between', | ||
'notInList', | ||
'startsWith', | ||
'endsWith', | ||
'contains', | ||
'upperCase', | ||
'lowerCase', | ||
'matchesRegex', | ||
// 'subString', //tnr: not yet implemented | ||
// 'dateOnly', //tnr: not yet implemented | ||
]; | ||
const combineQueries = require("./combine-queries"); | ||
module.exports = (function () { | ||
function isDateOrNumber(opName) { | ||
return (...args) => { | ||
if (args.some((arg) => { | ||
return !(_.isDate(arg) || _.isString(arg) || _.isNumber(arg)) | ||
})) { | ||
throw new Error( | ||
`QueryBuilderError: You must pass a date or number as args to ${opName}. You passed: Args ${args.join(',')}` | ||
); | ||
} | ||
} | ||
} | ||
function isArray(opName) { | ||
return (arg) => { | ||
if (!_.isArray(arg)) { | ||
throw new Error( | ||
`QueryBuilderError: You must pass an array for ${opName} filters. You passed: ${arg}` | ||
); | ||
} | ||
} | ||
} | ||
function isString(opName) { | ||
return (arg) => { | ||
if (!_.isString(arg)) { | ||
throw new Error( | ||
`QueryBuilderError: You must pass a string for ${opName} filters. You passed: ${arg}` | ||
); | ||
} | ||
} | ||
} | ||
// to be implemented | ||
// | ||
// filter - done | ||
// lessThan - done | ||
// lessThanOrEqual - done | ||
// greaterThan - done | ||
// greaterThanOrEqual - done | ||
// equals - done | ||
// notEquals - done | ||
// notNull | ||
// isNull | ||
// between | ||
// inList - done | ||
// notInList | ||
// related - done | ||
// notRelated - done | ||
// startsWith LIKE adsd% | ||
// endsWith | ||
// contains LIKE | ||
// dateOnly | ||
// upperCase | ||
// lowerCase | ||
// subString | ||
// matchesRegex | ||
// count - Done | ||
function numberOfArgs(opName, argLength) { | ||
return (...args) => { | ||
if (args.length !== argLength) { | ||
throw new Error( | ||
`QueryBuilderError: Args for ${opName} are of length ${args.length}, but they should be of length ${argLength}` | ||
); | ||
} | ||
}; | ||
} | ||
// log("FilterBuilder in QueryBuilder"); | ||
// log(FilterBuilder); | ||
const expressionOperators = [ | ||
{ | ||
opName: "greaterThan", | ||
sanityChecks: [ | ||
numberOfArgs("greaterThan", 1), | ||
isDateOrNumber("greaterThan") | ||
] | ||
}, | ||
{ | ||
opName: "inList", | ||
sanityChecks: [numberOfArgs("inList", 1), isArray("inList")] | ||
}, | ||
{ | ||
opName: "lessThan", | ||
sanityChecks: [ | ||
numberOfArgs("lessThan", 1), | ||
isDateOrNumber("lessThan"), | ||
] | ||
}, | ||
{ | ||
opName: "lessThanOrEqual", | ||
sanityChecks: [ | ||
numberOfArgs("lessThanOrEqual", 1), | ||
isDateOrNumber("lessThanOrEqual"), | ||
] | ||
}, | ||
{ | ||
opName: "equals", | ||
sanityChecks: [ | ||
numberOfArgs("equals", 1), | ||
] | ||
}, | ||
{ | ||
opName: "greaterThanOrEqual", | ||
sanityChecks: [ | ||
numberOfArgs("greaterThanOrEqual", 1), | ||
isDateOrNumber("greaterThanOrEqual"), | ||
] | ||
}, | ||
{ | ||
opName: "notEquals", | ||
sanityChecks: [ | ||
numberOfArgs("notEquals", 1), | ||
] | ||
}, | ||
{ | ||
opName: "notNull", | ||
sanityChecks: [ | ||
numberOfArgs("notNull", 0), | ||
] | ||
}, | ||
{ | ||
opName: "isNull", | ||
sanityChecks: [ | ||
numberOfArgs("isNull", 0), | ||
] | ||
}, | ||
{ | ||
opName: "between", | ||
sanityChecks: [ | ||
numberOfArgs("between", 2), | ||
isDateOrNumber("between"), | ||
] | ||
}, | ||
{ | ||
opName: "notInList", | ||
sanityChecks: [ | ||
numberOfArgs("notInList", 1), | ||
isArray("notInList"), | ||
] | ||
}, | ||
{ | ||
opName: "startsWith", | ||
sanityChecks: [ | ||
numberOfArgs("startsWith", 1), | ||
isString("startsWith") | ||
] | ||
}, | ||
{ | ||
opName: "endsWith", | ||
sanityChecks: [ | ||
numberOfArgs("endsWith", 1), | ||
isString("endsWith") | ||
] | ||
}, | ||
{ | ||
opName: "contains", | ||
sanityChecks: [ | ||
numberOfArgs("contains", 1), | ||
isString("contains") | ||
] | ||
}, | ||
{ | ||
opName: "upperCase", | ||
sanityChecks: [ | ||
numberOfArgs("upperCase", 1), | ||
isString("upperCase") | ||
] | ||
}, | ||
{ | ||
opName: "lowerCase", | ||
sanityChecks: [ | ||
numberOfArgs("lowerCase", 1), | ||
isString("lowerCase") | ||
] | ||
}, | ||
{ | ||
opName: "matchesRegex", | ||
sanityChecks: [ | ||
numberOfArgs("matchesRegex", 1), | ||
isString("matchesRegex") | ||
] | ||
} | ||
// 'subString', //tnr: not yet implemented | ||
// 'dateOnly', //tnr: not yet implemented | ||
]; | ||
module.exports = (function() { | ||
// to be implemented | ||
// | ||
// filter - done | ||
// lessThan - done | ||
// lessThanOrEqual - done | ||
// greaterThan - done | ||
// greaterThanOrEqual - done | ||
// equals - done | ||
// notEquals - done | ||
// notNull | ||
// isNull | ||
// between | ||
// inList - done | ||
// notInList | ||
// related - done | ||
// notRelated - done | ||
// startsWith LIKE adsd% | ||
// endsWith | ||
// contains LIKE | ||
// dateOnly | ||
// upperCase | ||
// lowerCase | ||
// subString | ||
// matchesRegex | ||
// count - Done | ||
function QueryBuilder(entity) { | ||
this.query = {}; | ||
// log("FilterBuilder in QueryBuilder"); | ||
// log(FilterBuilder); | ||
if(entity == null) throw new Error("You must pass the name of the model being filtered!"); | ||
function QueryBuilder(entity) { | ||
this.query = {}; | ||
if(typeof entity === 'string'){ | ||
this.query = { | ||
__objectType: 'query', | ||
type: 'root', | ||
entity: entity, | ||
filters: [] | ||
}; | ||
}else{ | ||
let subQuery = entity; | ||
this.query = { | ||
type: 'subquery', | ||
key: subQuery.key, | ||
entity: subQuery.entity, | ||
foreignKey: subQuery.foreignKey, | ||
modifier: subQuery.modifier, | ||
filters: [], | ||
countExpression: undefined | ||
}; | ||
this.parentBuilder = subQuery.parentBuilder; | ||
let self = this; | ||
this.toFilter = function(filterBuilder, name){ | ||
this.query.foreignKey = name; | ||
return this.toJSON(); | ||
}; | ||
} | ||
} | ||
if (entity == null) | ||
throw new Error("You must pass the name of the model being filtered!"); | ||
QueryBuilder.combineQueries = combineQueries; | ||
QueryBuilder.prototype.field = function (fieldName) { | ||
return { | ||
__objectType: 'field', | ||
field: fieldName | ||
}; | ||
if (typeof entity === "string") { | ||
this.query = { | ||
__objectType: "query", | ||
type: "root", | ||
entity, | ||
filters: [] | ||
}; | ||
} else { | ||
let subQuery = entity; | ||
this.query = { | ||
type: "subquery", | ||
key: subQuery.key, | ||
entity: subQuery.entity, | ||
foreignKey: subQuery.foreignKey, | ||
modifier: subQuery.modifier, | ||
filters: [], | ||
countExpression: undefined | ||
}; | ||
this.parentBuilder = subQuery.parentBuilder; | ||
let self = this; | ||
this.toFilter = function(filterBuilder, name) { | ||
this.query.foreignKey = name; | ||
return this.toJSON(); | ||
}; | ||
} | ||
} | ||
QueryBuilder.prototype.related = function (relatedEntity) { | ||
var tokens = relatedEntity.split('.'); | ||
var entity = tokens[0]; | ||
var key = tokens[1]; | ||
QueryBuilder.combineQueries = combineQueries; | ||
// log("FilterBuilder in related"); | ||
// log(FilterBuilder); | ||
QueryBuilder.prototype.field = function(fieldName) { | ||
return { | ||
__objectType: "field", | ||
field: fieldName | ||
}; | ||
}; | ||
return createSubQueryBuilder(this, entity, key); | ||
} | ||
QueryBuilder.prototype.related = function(relatedEntity) { | ||
var tokens = relatedEntity.split("."); | ||
var entity = tokens[0]; | ||
var key = tokens[1]; | ||
QueryBuilder.prototype.notRelated = function (relatedEntity) { | ||
var tokens = relatedEntity.split('.'); | ||
var entity = tokens[0]; | ||
var key = tokens[1]; | ||
// log("FilterBuilder in related"); | ||
// log(FilterBuilder); | ||
// log("FilterBuilder in notRelated"); | ||
// log(FilterBuilder); | ||
return createSubQueryBuilder(this, entity, key); | ||
}; | ||
return createSubQueryBuilder(this, entity, key, 'not'); | ||
} | ||
QueryBuilder.prototype.notRelated = function(relatedEntity) { | ||
var tokens = relatedEntity.split("."); | ||
var entity = tokens[0]; | ||
var key = tokens[1]; | ||
// log("FilterBuilder in notRelated"); | ||
// log(FilterBuilder); | ||
QueryBuilder.prototype.toJSON = function () { | ||
return JSON.parse(JSON.stringify(this.query)) | ||
} | ||
return createSubQueryBuilder(this, entity, key, "not"); | ||
}; | ||
QueryBuilder.prototype.convertToFilter = function(argDef, operator){ | ||
var whereArgs = {}; | ||
var filters = []; | ||
QueryBuilder.prototype.toJSON = function() { | ||
return JSON.parse(JSON.stringify(this.query)); | ||
}; | ||
_.each(argDef, (arg, name) => { | ||
// log(name); | ||
// if(arg.constructor){ | ||
// log(arg.constructor.name) | ||
// }else{ | ||
// log("not a prototype") | ||
// } | ||
if(!isFilterExpresionOrSubQuery(name, arg)){ | ||
//log("Is Where Filter: " + name); | ||
whereArgs[name] = arg; | ||
}else{ | ||
//log("Is Expression or SubQuery Filter: " + name); | ||
filters.push(arg.toFilter(this, name)); | ||
} | ||
}); | ||
if(_.keys(whereArgs).length > 0){ | ||
filters.unshift( { | ||
type: 'where', | ||
args: whereArgs | ||
}); | ||
} | ||
QueryBuilder.prototype.convertToFilter = function(argDef, operator) { | ||
var whereArgs = {}; | ||
var filters = []; | ||
if(filters.length === 1){ | ||
return filters[0]; | ||
} | ||
var filterDef = { | ||
type: 'group', | ||
operator: operator || 'and', | ||
filters | ||
}; | ||
return filterDef; | ||
_.each(argDef, (arg, name) => { | ||
// log(name); | ||
// if(arg.constructor){ | ||
// log(arg.constructor.name) | ||
// }else{ | ||
// log("not a prototype") | ||
// } | ||
if (!isFilterExpresionOrSubQuery(name, arg)) { | ||
//log("Is Where Filter: " + name); | ||
whereArgs[name] = arg; | ||
} else { | ||
//log("Is Expression or SubQuery Filter: " + name); | ||
filters.push(arg.toFilter(this, name)); | ||
} | ||
}); | ||
if (_.keys(whereArgs).length > 0) { | ||
filters.unshift({ | ||
type: "where", | ||
args: whereArgs | ||
}); | ||
} | ||
QueryBuilder.prototype.where = function(){ | ||
var args = [].slice.call(arguments); | ||
return this.whereAll(args); | ||
if (filters.length === 1) { | ||
return filters[0]; | ||
} | ||
var filterDef = { | ||
type: "group", | ||
operator: operator || "and", | ||
filters | ||
}; | ||
return filterDef; | ||
}; | ||
QueryBuilder.prototype.orWhere = function(){ | ||
var args = [].slice.call(arguments); | ||
return this.orWhereAll(args); | ||
} | ||
QueryBuilder.prototype.where = function() { | ||
var args = [].slice.call(arguments); | ||
return this.whereAll(args); | ||
}; | ||
QueryBuilder.prototype.andWhere = function(){ | ||
var args = [].slice.call(arguments); | ||
return this.andWhereAll(args); | ||
} | ||
QueryBuilder.prototype.orWhere = function() { | ||
var args = [].slice.call(arguments); | ||
return this.orWhereAll(args); | ||
}; | ||
QueryBuilder.prototype.whereAny = function(){ | ||
var args = [].slice.call(arguments); | ||
return whereAny(this, args); | ||
} | ||
QueryBuilder.prototype.andWhere = function() { | ||
var args = [].slice.call(arguments); | ||
return this.andWhereAll(args); | ||
}; | ||
QueryBuilder.prototype.whereAll = function(){ | ||
var args = [].slice.call(arguments); | ||
return whereAll(this, args); | ||
} | ||
QueryBuilder.prototype.whereAny = function() { | ||
var args = [].slice.call(arguments); | ||
return whereAny(this, args); | ||
}; | ||
QueryBuilder.prototype.andWhereAny = function(){ | ||
var args = [].slice.call(arguments); | ||
return whereAny(this, args, 'and'); | ||
} | ||
QueryBuilder.prototype.whereAll = function() { | ||
var args = [].slice.call(arguments); | ||
return whereAll(this, args); | ||
}; | ||
QueryBuilder.prototype.orWhereAny = function(){ | ||
var args = [].slice.call(arguments); | ||
return whereAny(this, args, 'or'); | ||
} | ||
QueryBuilder.prototype.andWhereAny = function() { | ||
var args = [].slice.call(arguments); | ||
return whereAny(this, args, "and"); | ||
}; | ||
QueryBuilder.prototype.andWhereAll = function(){ | ||
var args = [].slice.call(arguments); | ||
return whereAll(this, args, 'and'); | ||
} | ||
QueryBuilder.prototype.orWhereAny = function() { | ||
var args = [].slice.call(arguments); | ||
return whereAny(this, args, "or"); | ||
}; | ||
QueryBuilder.prototype.orWhereAll = function(){ | ||
var args = [].slice.call(arguments); | ||
return whereAll(this, args, 'or'); | ||
} | ||
QueryBuilder.prototype.andWhereAll = function() { | ||
var args = [].slice.call(arguments); | ||
return whereAll(this, args, "and"); | ||
}; | ||
QueryBuilder.prototype.count = function(){ | ||
var args = [].slice.call(arguments); | ||
if(this.query.type==="subquery"){ | ||
if(this.query.countExpression){ | ||
throw new Error("QueryBuilder subquery can only have one count expression"); | ||
} | ||
this.query.countExpression = args[0].toFilter(this,"count"); | ||
} | ||
else { | ||
throw new Error("QueryBuilder is not subquery type on count expression"); | ||
} | ||
return this; | ||
QueryBuilder.prototype.orWhereAll = function() { | ||
var args = [].slice.call(arguments); | ||
return whereAll(this, args, "or"); | ||
}; | ||
QueryBuilder.prototype.count = function() { | ||
var args = [].slice.call(arguments); | ||
if (this.query.type === "subquery") { | ||
if (this.query.countExpression) { | ||
throw new Error( | ||
"QueryBuilder subquery can only have one count expression" | ||
); | ||
} | ||
this.query.countExpression = args[0].toFilter(this, "count"); | ||
} else { | ||
throw new Error("QueryBuilder is not subquery type on count expression"); | ||
} | ||
QueryBuilder.ExpressionJson = {} | ||
attachExpressionFunctions(); | ||
return QueryBuilder; | ||
return this; | ||
}; | ||
QueryBuilder.ExpressionJson = {}; | ||
attachExpressionFunctions(); | ||
function createSubQueryBuilder(qb, entity, key, modifier) { | ||
return new QueryBuilder({ | ||
parentBuilder: qb, | ||
entity, | ||
key: key, | ||
modifier: modifier | ||
}); | ||
} | ||
return QueryBuilder; | ||
function attachExpressionFunctions() { | ||
expressionOperators.forEach((opName) => { | ||
QueryBuilder.prototype[opName] = function () { | ||
var args = [].slice.call(arguments); | ||
return new FilterExpression(opName, args); | ||
}; | ||
QueryBuilder.ExpressionJson[opName] = function () { | ||
var args = [].slice.call(arguments); | ||
return new FilterExpression(opName, args).toFilter() | ||
}; | ||
}); | ||
} | ||
function createSubQueryBuilder(qb, entity, key, modifier) { | ||
return new QueryBuilder({ | ||
parentBuilder: qb, | ||
entity, | ||
key, | ||
modifier | ||
}); | ||
} | ||
function isFilterExpresionOrSubQuery(name, arg){ | ||
if(arg instanceof FilterExpression) return true; | ||
if(arg instanceof QueryBuilder) return true; | ||
return false; | ||
} | ||
function attachExpressionFunctions() { | ||
expressionOperators.forEach(({ opName, sanityChecks }) => { | ||
QueryBuilder.prototype[opName] = function(...args) { | ||
sanityChecks.forEach((sanityCheck) => {sanityCheck(...args)}) | ||
return new FilterExpression(opName, args); | ||
}; | ||
QueryBuilder.ExpressionJson[opName] = function(...args) { | ||
sanityChecks.forEach((sanityCheck) => {sanityCheck(...args)}) | ||
return new FilterExpression(opName, args).toFilter(); | ||
}; | ||
}); | ||
} | ||
function whereAny(filterBuilder, whereArgs, chainedWith){ | ||
return where(filterBuilder, 'or', whereArgs, chainedWith); | ||
} | ||
function isFilterExpresionOrSubQuery(name, arg) { | ||
if (arg instanceof FilterExpression) return true; | ||
if (arg instanceof QueryBuilder) return true; | ||
return false; | ||
} | ||
function whereAll(filterBuilder, whereArgs, chainedWith){ | ||
return where(filterBuilder, 'and', whereArgs, chainedWith); | ||
} | ||
function whereAny(filterBuilder, whereArgs, chainedWith) { | ||
return where(filterBuilder, "or", whereArgs, chainedWith); | ||
} | ||
function where(filterBuilder, operator, whereArgs, chainedWith){ | ||
if(!Array.isArray(whereArgs)) return where(filterBuilder, operator, [whereArgs], chainedWith); | ||
function whereAll(filterBuilder, whereArgs, chainedWith) { | ||
return where(filterBuilder, "and", whereArgs, chainedWith); | ||
} | ||
var filterDef = { | ||
type: 'group', | ||
operator, | ||
chainedWith, | ||
filters: [] | ||
}; | ||
function where(filterBuilder, operator, whereArgs, chainedWith) { | ||
if (!Array.isArray(whereArgs)) | ||
return where(filterBuilder, operator, [whereArgs], chainedWith); | ||
whereArgs.forEach((arg) => { | ||
//add check for object type TODO | ||
var filter = filterBuilder.convertToFilter(arg, operator); | ||
filterDef.filters.push(filter); | ||
}); | ||
var filterDef = { | ||
type: "group", | ||
operator, | ||
chainedWith, | ||
filters: [] | ||
}; | ||
if(filterDef.filters.length === 1){ | ||
filterBuilder.query.filters.push(filterDef.filters[0]); | ||
}else{ | ||
filterBuilder.query.filters.push(filterDef); | ||
} | ||
return filterBuilder; | ||
whereArgs.forEach(arg => { | ||
//add check for object type TODO | ||
var filter = filterBuilder.convertToFilter(arg, operator); | ||
filterDef.filters.push(filter); | ||
}); | ||
if (filterDef.filters.length === 1) { | ||
filterBuilder.query.filters.push(filterDef.filters[0]); | ||
} else { | ||
filterBuilder.query.filters.push(filterDef); | ||
} | ||
})(); | ||
return filterBuilder; | ||
} | ||
})(); |
21860
695