210 lines
6.6 KiB
TypeScript
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 };
|
|
}
|
|
}
|