import { Knex } from "knex"; import { UnauthorizedError } from "../errors"; type TKnexDynamicPrimitiveOperator = | { operator: "eq" | "ne" | "startsWith" | "endsWith"; value: string; field: Extract; } | { operator: "notIn"; value: string[]; field: Extract; }; type TKnexDynamicInOperator = { operator: "in"; value: string[] | number[]; field: Extract; }; type TKnexNonGroupOperator = TKnexDynamicInOperator | TKnexDynamicPrimitiveOperator; type TKnexGroupOperator = { operator: "and" | "or" | "not"; value: (TKnexNonGroupOperator | TKnexGroupOperator)[]; }; export type TKnexDynamicOperator = TKnexGroupOperator | TKnexNonGroupOperator; export const buildDynamicKnexQuery = ( rootQueryBuild: Knex.QueryBuilder, dynamicQuery: TKnexDynamicOperator ) => { const stack = [{ filterAst: dynamicQuery, queryBuilder: rootQueryBuild }]; while (stack.length) { const { filterAst, queryBuilder } = stack.pop()!; switch (filterAst.operator) { case "eq": { void queryBuilder.where(filterAst.field, "=", filterAst.value); break; } case "ne": { void queryBuilder.whereNot(filterAst.field, filterAst.value); break; } case "startsWith": { void queryBuilder.whereILike(filterAst.field, `${filterAst.value}%`); break; } case "endsWith": { void queryBuilder.whereILike(filterAst.field, `%${filterAst.value}`); break; } case "notIn": { void queryBuilder.whereNotIn(filterAst.field, filterAst.value); break; } case "and": { filterAst.value.forEach((el) => { void queryBuilder.andWhere((subQueryBuilder) => { buildDynamicKnexQuery(subQueryBuilder, el); }); }); break; } case "or": { filterAst.value.forEach((el) => { void queryBuilder.orWhere((subQueryBuilder) => { buildDynamicKnexQuery(subQueryBuilder, el); }); }); break; } case "not": { filterAst.value.forEach((el) => { void queryBuilder.whereNot((subQueryBuilder) => { buildDynamicKnexQuery(subQueryBuilder, el); }); }); break; } default: throw new UnauthorizedError({ message: `Invalid knex dynamic operator: ${filterAst.operator}` }); } } };