tg-knex-query-resolver
Version:
TeselaGen's Knex based query resolver
171 lines (159 loc) • 5.26 kB
JavaScript
const log = require("../log");
module.exports = function (db, query, opts) {
var dialect = getDialectFromDbConn(db);
var qb;
if (opts && opts.filterOnly) {
qb = db;
} else {
qb = db.select(query.projection).from(query.entity);
}
resolveFilter(qb, query.filter, dialect);
return qb;
};
// resolveFilter recursively iterates through a
// filter object which is a JSON array.
// This array contains objects
// that always have an 'op' property. This 'op'
// property maps directly to a function that can be called
// on the knex query builder.
//
// Additionally there are three different types of operations
// which are detected by the existence of the following keys
// on each filter object:
// args - A knex function which gets passed the value of args
// Example:
// filter object:
// { op: 'where', args: { userId: 53 }}
// knex function call
// knex['where']({ userId:53 })
// grp - A knex function which has several nested functions
// Example:
// filter object:
// { op: 'where',
// grp: [
// { op: 'where', args: { userId: 53 }},
// { op: 'andWhere', args: [ 'age', '<', 21 ]}
// ]}
// knex function call
// knex['where'](function(){
// this['where']({ userId: 53 })['andWhere']('age', '<', 21);
// })
//
// subqry - A knex function which is performing a sub query. This
// function will have both an additional key 'col'
// whose value will get passed to the knex subqry 'op'
// and set of nested functions
// Example:
// filter object:
// { op: 'whereIn',
// col: 'roleType_code'
// subqry: [
// { op: 'select', args: ['code']},
// { op: 'from', args: ['roleType']},
// { op: 'where', args: { active: true }}
// ]}
// knex function call
// knex['whereIn']('roleType_code', function(){
// this['select']('code')['from']('roleType')['where']({active: true});
// })
//
// Below is an example query that combines all three types of operations
// var filter = [
// { op: "where", args: ["name", "like", "%adam%"]},
// { op: "orWhere", grp: [
// { col: "id", op: "whereIn", subqry: [
// { op: "select", args: ["id"]},
// { op: "from", args: ["access_control"]},
// { op: "where", args: [{id: 5}]}
// ]},
// { op: "andWhere", args:[{ role: "admin" }]}
// ]}
// ];
//
// A select all from the user table combined
// with the above filter would look like this SQL statement:
// select
// *
// from
// "user"
// where
// "name" like '%adam%'
// or (
// "id" in (
// select
// "id"
// from
// "access_control"
// where
// "id" = 5
// )
// and "role" = 'admin'
// )
function resolveFilter(qb, filter, dialect) {
filter.forEach((op) => {
// log("\nQB=========");
// log(qb);
// log("\n");
// log("OP>>>>>>>>>>");
// log(op);
if (!isDefined(op, "op")) {
log(op);
throw new Error(
"The 'op' property is not for resolver. Unable to resolve"
);
}
if (typeof op.op !== "string") {
log(op);
throw new Error(
"The 'op' property is not a string. The op property must be a string. Instead it's type: " +
typeof op.op
);
}
if (op.isDialectSpecific) {
if (op[dialect]) {
let dialectSpecificFilter = [op[dialect]];
return resolveFilter(qb, dialectSpecificFilter, dialect);
} else {
log(op);
throw new Error(
"Dialect specific op " +
op.op +
" for this dialect " +
dialect +
"is not supported."
);
}
}
if (typeof qb[op.op] !== "function") {
log(op);
throw new Error("Knex does not contain a function for a op: " + op.op);
}
if (isDefined(op, "args") && Array.isArray(op.args)) {
qb = qb[op.op].apply(qb, op.args);
} else if (isDefined(op, "args")) {
qb = qb[op.op].apply(qb, [op.args]);
} else if (isDefined(op, "grp") && Array.isArray(op.grp)) {
qb = qb[op.op](function () {
resolveFilter(this, op.grp, dialect);
});
} else if (isDefined(op, "subqry") && Array.isArray(op.subqry)) {
// qb = qb[op.op](op.col, function () {
// resolveFilter(this, op.subqry, dialect);
// });
// switching to exists / not exists for sub queries to improve performance
qb = qb[op.op](function () {
resolveFilter(this, op.subqry, dialect);
});
} else {
log(op);
throw new Error("Unknown operation format: " + op.op);
}
//console.log(qry.toString());
});
function isDefined(obj, key) {
return typeof obj[key] !== "undefined";
}
}
function getDialectFromDbConn(db) {
return "postgres";
}