UNPKG

refql

Version:

A Node.js and Deno library for composing and running SQL queries.

219 lines (218 loc) 8.57 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isRQLTag = void 0; exports.createRQLTag = createRQLTag; const consts_1 = require("../common/consts"); const isEmptyTag_1 = __importDefault(require("../common/isEmptyTag")); const joinMembers_1 = __importDefault(require("../common/joinMembers")); const Prop_1 = __importDefault(require("../Prop")); const RefProp_1 = __importDefault(require("../Prop/RefProp")); const SQLProp_1 = __importDefault(require("../Prop/SQLProp")); const SQLTag_1 = require("../SQLTag"); const Raw_1 = __importDefault(require("../SQLTag/Raw")); const sql_1 = require("../SQLTag/sql"); const Limit_1 = __importDefault(require("./Limit")); const Offset_1 = __importDefault(require("./Offset")); const OrderBy_1 = __importDefault(require("./OrderBy")); const RefNode_1 = __importDefault(require("./RefNode")); const RQLNode_1 = require("./RQLNode"); const type = "refql/RQLTag"; let prototype = Object.assign({}, RQLNode_1.rqlNodePrototype, { constructor: createRQLTag, [consts_1.refqlType]: type, concat, [consts_1.flConcat]: concat, interpret, compile, run }); function createRQLTag(table, nodes, options) { const tag = ((params) => { return options.runner(tag, params); }); Object.setPrototypeOf(tag, Object.assign(Object.create(Function.prototype), prototype, { table, nodes, options })); return tag; } const concatDeep = (nodes) => { return nodes.reduce((acc, node) => { if (RefNode_1.default.isRefNode(node)) { const { table } = node.tag; const id = table.toString(); if (acc[id]) { acc[id] = acc[id].concat(node.tag); } else { acc[id] = node.tag; } } else { acc.nodes.push(node); } return acc; }, { nodes: [] }); }; function concat(other) { if (!this.table.equals(other.table)) { throw new Error("U can't concat RQLTags that come from different tables"); } const _a = concatDeep(this.nodes.concat(other.nodes)), { nodes } = _a, refs = __rest(_a, ["nodes"]); const refNodes = Object.values(this.table.props) .filter(prop => RefProp_1.default.isRefProp(prop) && refs[prop.child.toString()]) .map((prop) => { const ref = refs[prop.child.toString()]; return (0, RefNode_1.default)(createRQLTag(ref.table, ref.nodes, this.options), prop, this.table, this.options); }); return createRQLTag(this.table, [...nodes, ...refNodes], this.options); } function interpret(where = (0, sql_1.sqlX) `where 1 = 1`) { const { nodes, table } = this, next = [], members = []; let filters = (0, sql_1.sqlX) ``; let orderBies = (0, sql_1.sqlX) ``; let limit = (0, sql_1.sqlX) ``; let offset = (0, sql_1.sqlX) ``; let memberCount = 0; const caseOfRef = (tag, info, single) => { for (const lr of info.lRef) { members.push({ as: lr.as, node: (0, Raw_1.default)(lr), isOmitted: false }); } next.push({ tag, link: [info.as, info.lRef.map(lr => lr.as)], single }); }; for (const node of nodes) { if (Prop_1.default.isProp(node) || SQLProp_1.default.isSQLProp(node)) { const col = node.interpret(); members.push({ as: node.as, node: (0, sql_1.sqlX) `${col} ${(0, Raw_1.default)(`"${node.as}"`)}`, isOmitted: node.isOmitted }); if (node.operations.length === 0 && !node.isOmitted && !SQLProp_1.default.isSQLProp(node)) { memberCount += 1; } let propFilters = (0, sql_1.sqlX) ``; let filterIdx = 0; for (const op of node.operations) { if (OrderBy_1.default.isOrderBy(op)) { const delimiter = (0, isEmptyTag_1.default)(orderBies) ? "order by " : ", "; orderBies = orderBies.join(delimiter, op.interpret(col, true)); } else { propFilters = propFilters.join(filterIdx > 0 ? " " : "", op.interpret(col, filterIdx > 0)); filterIdx += 1; } } if (!(0, isEmptyTag_1.default)(propFilters)) { filters = filters.join(" ", (0, sql_1.sqlX) `and (${propFilters})`); } } else if (Limit_1.default.isLimit(node)) { limit = node.interpret(); } else if (Offset_1.default.isOffset(node)) { offset = node.interpret(); } else if (RefNode_1.default.isRefNode(node)) { caseOfRef(node.joinLateral(), node.info, node.single); } else if ((0, SQLTag_1.isSQLTag)(node)) { filters = filters.join(" ", node); } else { throw new Error(`Unknown RQLNode Type: "${String(node)}"`); } } // Select all columns if (memberCount === 0) { const fieldProps = Object.entries(table.props) .map(([, prop]) => prop) .filter(prop => Prop_1.default.isProp(prop) && !(0, SQLTag_1.isSQLTag)(prop.col)) .map(prop => ({ as: prop.as, node: (0, sql_1.sqlX) `${prop.interpret()} ${(0, Raw_1.default)(`"${prop.as}"`)}`, isOmitted: false })); members.push(...fieldProps); } let tag = (0, sql_1.sqlX) ` select ${(0, joinMembers_1.default)(members)} from ${(0, Raw_1.default)(table)} ` .concat(where) .join("", filters) .concat(orderBies) .concat(limit) .concat(offset); return { next, tag }; } function compile(params) { if (!this.interpreted) { let { next, tag } = this.interpret(); this.interpreted = { tag, next }; } return [ ...this.interpreted.tag.compile(params), this.interpreted.next ]; } function run(params, querier) { return __awaiter(this, void 0, void 0, function* () { const [query, values, next] = this.compile(params); const refQLRows = yield (querier || this.options.querier)(query, values); if (!refQLRows.length) return []; const nextData = yield Promise.all(next.map( // { ...null } = {} n => n.tag.run(Object.assign(Object.assign({}, params), { refQLRows }), querier))); return refQLRows.map(row => nextData.reduce((agg, nextRows, idx) => { const { single, link: [lAs, rAs] } = next[idx]; agg[lAs] = nextRows .filter((r) => rAs.reduce((acc, as) => acc && r[as] === row[as], true)) .map((r) => { let matched = Object.assign({}, r); for (const as of rAs) { delete matched[as]; } return matched; }); if (single) { agg[lAs] = agg[lAs][0] || null; } for (const as of rAs) { delete agg[as]; } return agg; }, row)); }); } const isRQLTag = function (x) { return x != null && x[consts_1.refqlType] === type; }; exports.isRQLTag = isRQLTag;