"use strict"; const util = require("util"); let { select, onlyColumns, where, withRelations, withDerived, column, through, inValues, sql, postProcess } = require("../operations"); function withOwner() { return withRelations({ owner: "owner_id" }); } // TODO: Allow specifying placeholders that can be filled in later, for truly reusable query builders (so that eg. query planning only needs to be done once, this could even use prepared statements under the hood) let query = select("projects", [ onlyColumns([ "id", "name" ]), where({ active: true, visible: true, primary_category_id: inValues([ 2, 3, 5, 7, 8 ]) }), // FIXME: where pivot table entry exists for category in that list withRelations({ primaryCategory: belongsTo({ column: "primary_category_id", query: [ withOwner() ] }), categories: through({ path: [ "projects_categories.project_id", "category_id" ], query: [ // Optional extra clauses for the query on the pivot table, eg. for filtering entries where({ adminApproved: true }) ] }), // all user groups for a given project ID -> all memberships for the given user group IDs -> for each membership, the record referenced by the given user_id users: through([ "user_groups.project_id", "membership.user_group_id", "user_id" ]), // ... expands to ... users: through([ has(foreignColumn({ table: "user_groups", column: "project_id" })), has(foreignColumn({ table: "memberships", column: "user_group_id" })), belongsTo(column("project_id")), ]), owner: "owner_id", // ... expands to owner: belongsTo({ column: "owner_id" }), releases: "releases.project_id", // ... expands to ... releases: has({ column: "releases.project_id" }) // primaryCategory: [ // column("primary_category_id"), // withOwner() // ], // categories: has(through({ // table: "projects_categories", // localSide: "project_id", // remoteSide: "category_id", // })), // users: has(through({ // table: "user_groups", // localSide: "project_id", // remoteSide: through({ // table: "memberships", // localSide: "user_group_id", // remoteSide: "user_id" // }) // })), // categories: [ // through({ // table: "projects_categories", // localSide: "project_id", // remoteSide: "category_id", // query: [ // Optional extra clauses for the query on the pivot table, eg. for filtering entries // where({ adminApproved: true }) // ] // }), // withOwner() // ], }), withDerived({ capitalizedName: sql("UPPER(name)"), teamCount: sql("moderator_count + admin_count"), nameDistance: (project) => wordDistanceAlgorithm(project.name, "someReferenceName") // NOTE: This could have returned a Promise! }), mapCase({ from: "snake", to: "camel" }) ]); console.log(util.inspect(query, { depth: null, colors: true })); // FIXME: Pre-processing (eg. inverse case-mapping for inserted objects or WHERE clauses) - maybe pre-processing that subscribes to particular operations? Something along the lines of axios interceptors perhaps // FIXME: `either`/`all` for OR/AND representation respectively? // FIXME: I guess `withDerived` could be implemented externally, as something that (depending on value type) either a) adds a column selector or b) adds a post-processing hook // FIXME: Aggregrates (GROUP BY) // return db.execute(query); /* Hypothetical `mapCase` implementation */ function mapObjectCase(object, from, to) { return mapObject(object, (key, value) => { return [ caseMapper(key, from, to), value ]; }); } function mapCase({ from, to }) { return postProcess((results) => { // NOTE: This could have returned a Promise! return results.map((result) => mapObjectCase(result, from, to)); }); }