UNPKG

tg-knex-query-resolver

Version:

TeselaGen's Knex based query resolver

171 lines (159 loc) 5.26 kB
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"; }