builder change

This commit is contained in:
Julian Krauser 2024-12-14 15:44:17 +01:00
parent 63a0a60b12
commit 2518a1046f
2 changed files with 54 additions and 93 deletions

View file

@ -1,4 +1,4 @@
import { Brackets, DataSource, ObjectLiteral, SelectQueryBuilder } from "typeorm"; import { Brackets, DataSource, NotBrackets, ObjectLiteral, SelectQueryBuilder, WhereExpressionBuilder } from "typeorm";
import { dataSource } from "../data-source"; import { dataSource } from "../data-source";
import { ConditionStructure, DynamicQueryStructure } from "../type/dynamicQueries"; import { ConditionStructure, DynamicQueryStructure } from "../type/dynamicQueries";
import { TableMeta } from "../type/tableMeta"; import { TableMeta } from "../type/tableMeta";
@ -52,103 +52,93 @@ export default abstract class DynamicQueryBuilder {
count: number = 25 count: number = 25
): SelectQueryBuilder<ObjectLiteral> { ): SelectQueryBuilder<ObjectLiteral> {
let query = dataSource.getRepository(queryObj.table).createQueryBuilder(queryObj.table + "_0"); let query = dataSource.getRepository(queryObj.table).createQueryBuilder(queryObj.table + "_0");
query = this.buildDynamicQuery(query, queryObj);
query = query.offset(offset); this.buildDynamicQuery(query, queryObj);
query = query.limit(count);
query.offset(offset);
query.limit(count);
return query; return query;
dataSource
.getRepository("member")
.createQueryBuilder("member_0")
.select("member_0.firstname")
.addSelect("member_0.lastname")
.where("member_0.mail LIKE '%@gmail.com'")
.andWhere(
new Brackets((qb) => {
qb.where("user.firstName LIKE '%J'").orWhere("user.lastName LIKE '%K'");
})
)
.leftJoinAndSelect("member_0.sendNewsletter", "communication_0")
.addSelect("communication_0.*")
.orderBy("member_0.firstname", "ASC")
.addOrderBy("member_0.lastname", "ASC");
} }
public static buildDynamicQuery( private static buildDynamicQuery(
queryBuilder: SelectQueryBuilder<ObjectLiteral>, query: SelectQueryBuilder<ObjectLiteral>,
queryObject: DynamicQueryStructure, queryObject: DynamicQueryStructure,
depth: number = 0 depth: number = 0
): SelectQueryBuilder<ObjectLiteral> { ): void {
const alias = queryObject.table + "_" + depth; const alias = queryObject.table + "_" + depth;
// Handle SELECT
if (queryObject.select == "*") { if (queryObject.select == "*") {
queryBuilder.select(`${alias}.*`); query.addSelect(`${alias}.*`);
} else { } else {
queryBuilder.select(queryObject.select.map((col) => `${alias}.${col}`)); for (const select of queryObject.select) {
query.addSelect(`${alias}.${select}`);
}
} }
// Handle WHERE
if (queryObject.where) { if (queryObject.where) {
this.applyWhere(queryBuilder, queryObject.where, alias); this.applyWhere(query, queryObject.where, alias);
} }
// Handle JOINS
if (queryObject.join) { if (queryObject.join) {
queryObject.join.forEach((join) => { for (const join of queryObject.join) {
const joinAlias = join.table; query.leftJoinAndSelect(`${alias}.${join.foreignColumn}`, join.table + "_" + (depth + 1));
const joinType = "leftJoin"; // Default join type
const joinCondition = `${alias}.${join.foreignColumn} = ${joinAlias}.${join.foreignColumn}`;
queryBuilder[joinType](`${join.table}`, joinAlias, joinCondition); this.buildDynamicQuery(query, join, depth + 1);
}
// Recursively handle sub-joins and their conditions
this.buildDynamicQuery(queryBuilder, join, depth + 1);
});
} }
// Handle ORDER BY
if (queryObject.orderBy) { if (queryObject.orderBy) {
queryObject.orderBy.forEach((order) => { queryObject.orderBy.forEach((order) => {
queryBuilder.addOrderBy(`${alias}.${order.column}`, order.order); query.addOrderBy(`${alias}.${order.column}`, order.order);
}); });
} }
return queryBuilder;
} }
// Helper: Apply WHERE conditions public static applyWhere(
public static applyWhere<T>( query: SelectQueryBuilder<ObjectLiteral> | WhereExpressionBuilder,
queryBuilder: SelectQueryBuilder<T>,
conditions: Array<ConditionStructure>, conditions: Array<ConditionStructure>,
alias: string alias: string
): void { ): void {
conditions.forEach((condition, index) => { for (const condition of conditions) {
if (condition.structureType === "condition") { if (condition.structureType == "condition") {
const whereClause = this.buildConditionClause(condition, alias); const whereClause = this.buildConditionClause(condition, alias);
const whereMethod = condition.concat === "_" ? "where" : "andWhere";
if (condition.concat === "OR") { if (condition.concat == "_" || condition.concat == "AND") {
queryBuilder.orWhere(whereClause.query, whereClause.parameters); query.andWhere(whereClause.query, whereClause.parameters);
} else { } else {
queryBuilder[whereMethod](whereClause.query, whereClause.parameters); 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);
})
);
} }
} else if (condition.structureType === "nested") {
const nestedQuery = this.conditionsToQuery(condition.condition, alias);
const whereMethod = condition.concat === "OR" ? "orWhere" : "andWhere";
queryBuilder[whereMethod](`(${nestedQuery.query})`, nestedQuery.parameters);
} }
}); }
} }
// Helper: Build a single condition clause private static buildConditionClause(
public static buildConditionClause( condition: ConditionStructure & { structureType: "condition" },
condition: ConditionStructure,
alias: string alias: string
): { query: string; parameters: Record<string, unknown> } { ): { query: string; parameters: Record<string, unknown> } {
if (condition.structureType == "nested") return;
const parameterKey = `${alias}_${condition.column}_${Math.random().toString(36).substring(2)}`; const parameterKey = `${alias}_${condition.column}_${Math.random().toString(36).substring(2)}`;
let query = `${alias}.${condition.column}`; let query = `${alias}.${condition.column}`;
let parameters: Record<string, unknown> = {}; let parameters: Record<string, unknown> = {};
@ -213,37 +203,8 @@ export default abstract class DynamicQueryBuilder {
query += ` LIKE :${parameterKey}`; query += ` LIKE :${parameterKey}`;
parameters[parameterKey] = `%${condition.value}`; parameters[parameterKey] = `%${condition.value}`;
break; break;
default:
throw new Error(`Unsupported operation: ${condition.operation}`);
} }
return { query, parameters }; return { query, parameters };
} }
// Helper: Convert nested conditions to a query
public static conditionsToQuery(
conditions: Array<ConditionStructure>,
alias: string
): { query: string; parameters: Record<string, unknown> } {
let queryParts: string[] = [];
let parameters: Record<string, unknown> = {};
conditions.forEach((condition, index) => {
if (condition.structureType === "condition") {
const clause = this.buildConditionClause(condition, alias);
queryParts.push(clause.query);
parameters = { ...parameters, ...clause.parameters };
} else if (condition.structureType === "nested") {
const nested = this.conditionsToQuery(condition.condition, alias);
queryParts.push(`(${nested.query})`);
parameters = { ...parameters, ...nested.parameters };
}
});
return { query: queryParts.join(" AND "), parameters };
}
// use switch... for compare functions
// use NotBrackets/Brackets for nested conditions
// use joins by requesting table schema and setting correct column
} }

View file

@ -16,7 +16,7 @@ export type ConditionStructure = (
| { | {
structureType: "nested"; structureType: "nested";
invert?: boolean; invert?: boolean;
condition: Array<ConditionStructure>; conditions: Array<ConditionStructure>;
} }
) & { ) & {
concat: WhereType; concat: WhereType;
@ -52,7 +52,7 @@ export type OrderByStructure = {
export type OrderByType = "ASC" | "DESC"; export type OrderByType = "ASC" | "DESC";
const exampleQuery: DynamicQueryStructure = { export const exampleQuery: DynamicQueryStructure = {
select: ["firstname", "lastname"], select: ["firstname", "lastname"],
table: "member", table: "member",
where: [ where: [
@ -66,7 +66,7 @@ const exampleQuery: DynamicQueryStructure = {
{ {
structureType: "nested", structureType: "nested",
concat: "AND", concat: "AND",
condition: [ conditions: [
{ {
structureType: "condition", structureType: "condition",
concat: "_", concat: "_",