ff-admin-server/src/helpers/dynamicQueryBuilder.ts
2024-12-14 15:44:17 +01:00

210 lines
6.6 KiB
TypeScript

import { Brackets, DataSource, NotBrackets, ObjectLiteral, SelectQueryBuilder, WhereExpressionBuilder } from "typeorm";
import { dataSource } from "../data-source";
import { ConditionStructure, DynamicQueryStructure } from "../type/dynamicQueries";
import { TableMeta } from "../type/tableMeta";
export default abstract class DynamicQueryBuilder {
public static allowedTables: Array<string> = [
"award",
"communication",
"communicationType",
"executivePosition",
"membershipStatus",
"qualification",
"member",
"memberAwards",
"memberExecutivePositions",
"memberQualifications",
"membership",
];
public static getTableMeta(tableName: string): TableMeta {
let { name, columns, relations } = dataSource.getMetadata(tableName);
const uniqueColumns = columns.map((c) => ({ column: c.propertyName, type: c.type }));
return {
tableName: name,
columns: [
...uniqueColumns,
...relations
.filter((r) => !uniqueColumns.some((c) => r.propertyName == c.column))
.map((r) => ({
column: r.propertyName,
type: r.inverseEntityMetadata?.columns.find((col) => col.propertyName === r.inverseSidePropertyPath)?.type,
})),
],
relations: relations.map((r) => ({
column: r.propertyName,
relationType: r.relationType,
referencedTableName: r.inverseEntityMetadata?.tableName,
})),
};
}
public static getAllTableMeta(): Array<TableMeta> {
return this.allowedTables.map((table) => this.getTableMeta(table));
}
public static buildQuery(
queryObj: DynamicQueryStructure,
offset: number = 0,
count: number = 25
): SelectQueryBuilder<ObjectLiteral> {
let query = dataSource.getRepository(queryObj.table).createQueryBuilder(queryObj.table + "_0");
this.buildDynamicQuery(query, queryObj);
query.offset(offset);
query.limit(count);
return query;
}
private static buildDynamicQuery(
query: SelectQueryBuilder<ObjectLiteral>,
queryObject: DynamicQueryStructure,
depth: number = 0
): void {
const alias = queryObject.table + "_" + depth;
if (queryObject.select == "*") {
query.addSelect(`${alias}.*`);
} else {
for (const select of queryObject.select) {
query.addSelect(`${alias}.${select}`);
}
}
if (queryObject.where) {
this.applyWhere(query, queryObject.where, alias);
}
if (queryObject.join) {
for (const join of queryObject.join) {
query.leftJoinAndSelect(`${alias}.${join.foreignColumn}`, join.table + "_" + (depth + 1));
this.buildDynamicQuery(query, join, depth + 1);
}
}
if (queryObject.orderBy) {
queryObject.orderBy.forEach((order) => {
query.addOrderBy(`${alias}.${order.column}`, order.order);
});
}
}
public static applyWhere(
query: SelectQueryBuilder<ObjectLiteral> | WhereExpressionBuilder,
conditions: Array<ConditionStructure>,
alias: string
): void {
for (const condition of conditions) {
if (condition.structureType == "condition") {
const whereClause = this.buildConditionClause(condition, alias);
if (condition.concat == "_" || condition.concat == "AND") {
query.andWhere(whereClause.query, whereClause.parameters);
} else {
query.orWhere(whereClause.query, whereClause.parameters);
}
} else {
if (condition.concat == "AND") {
query.andWhere(
condition.invert == undefined || condition.invert == true
? new Brackets((qb) => {
this.applyWhere(qb, condition.conditions, alias);
})
: new NotBrackets((qb) => {
this.applyWhere(qb, condition.conditions, alias);
})
);
} else {
query.orWhere(
condition.invert == undefined || condition.invert == true
? new Brackets((qb) => {
this.applyWhere(qb, condition.conditions, alias);
})
: new NotBrackets((qb) => {
this.applyWhere(qb, condition.conditions, alias);
})
);
}
}
}
}
private static buildConditionClause(
condition: ConditionStructure & { structureType: "condition" },
alias: string
): { query: string; parameters: Record<string, unknown> } {
const parameterKey = `${alias}_${condition.column}_${Math.random().toString(36).substring(2)}`;
let query = `${alias}.${condition.column}`;
let parameters: Record<string, unknown> = {};
switch (condition.operation) {
case "eq":
query += ` = :${parameterKey}`;
parameters[parameterKey] = condition.value;
break;
case "neq":
query += ` != :${parameterKey}`;
parameters[parameterKey] = condition.value;
break;
case "lt":
query += ` < :${parameterKey}`;
parameters[parameterKey] = condition.value;
break;
case "lte":
query += ` <= :${parameterKey}`;
parameters[parameterKey] = condition.value;
break;
case "gt":
query += ` > :${parameterKey}`;
parameters[parameterKey] = condition.value;
break;
case "gte":
query += ` >= :${parameterKey}`;
parameters[parameterKey] = condition.value;
break;
case "in":
query += ` IN (:...${parameterKey})`;
parameters[parameterKey] = condition.value;
break;
case "notIn":
query += ` NOT IN (:...${parameterKey})`;
parameters[parameterKey] = condition.value;
break;
case "between":
query += ` BETWEEN :${parameterKey}_start AND :${parameterKey}_end`;
parameters[`${parameterKey}_start`] = (condition.value as { start: any }).start;
parameters[`${parameterKey}_end`] = (condition.value as { end: any }).end;
break;
case "null":
query += ` IS NULL`;
break;
case "notNull":
query += ` IS NOT NULL`;
break;
case "contains":
query += ` LIKE :${parameterKey}`;
parameters[parameterKey] = `%${condition.value}%`;
break;
case "notContains":
query += ` NOT LIKE :${parameterKey}`;
parameters[parameterKey] = `%${condition.value}%`;
break;
case "startsWith":
query += ` LIKE :${parameterKey}`;
parameters[parameterKey] = `${condition.value}%`;
break;
case "endsWith":
query += ` LIKE :${parameterKey}`;
parameters[parameterKey] = `%${condition.value}`;
break;
}
return { query, parameters };
}
}