#5-intelligent-groups #23
2 changed files with 54 additions and 93 deletions
|
@ -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 { ConditionStructure, DynamicQueryStructure } from "../type/dynamicQueries";
|
||||
import { TableMeta } from "../type/tableMeta";
|
||||
|
@ -52,103 +52,93 @@ export default abstract class DynamicQueryBuilder {
|
|||
count: number = 25
|
||||
): SelectQueryBuilder<ObjectLiteral> {
|
||||
let query = dataSource.getRepository(queryObj.table).createQueryBuilder(queryObj.table + "_0");
|
||||
query = this.buildDynamicQuery(query, queryObj);
|
||||
query = query.offset(offset);
|
||||
query = query.limit(count);
|
||||
|
||||
this.buildDynamicQuery(query, queryObj);
|
||||
|
||||
query.offset(offset);
|
||||
query.limit(count);
|
||||
|
||||
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(
|
||||
queryBuilder: SelectQueryBuilder<ObjectLiteral>,
|
||||
private static buildDynamicQuery(
|
||||
query: SelectQueryBuilder<ObjectLiteral>,
|
||||
queryObject: DynamicQueryStructure,
|
||||
depth: number = 0
|
||||
): SelectQueryBuilder<ObjectLiteral> {
|
||||
): void {
|
||||
const alias = queryObject.table + "_" + depth;
|
||||
|
||||
// Handle SELECT
|
||||
if (queryObject.select == "*") {
|
||||
queryBuilder.select(`${alias}.*`);
|
||||
query.addSelect(`${alias}.*`);
|
||||
} else {
|
||||
queryBuilder.select(queryObject.select.map((col) => `${alias}.${col}`));
|
||||
for (const select of queryObject.select) {
|
||||
query.addSelect(`${alias}.${select}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle WHERE
|
||||
if (queryObject.where) {
|
||||
this.applyWhere(queryBuilder, queryObject.where, alias);
|
||||
this.applyWhere(query, queryObject.where, alias);
|
||||
}
|
||||
|
||||
// Handle JOINS
|
||||
if (queryObject.join) {
|
||||
queryObject.join.forEach((join) => {
|
||||
const joinAlias = join.table;
|
||||
const joinType = "leftJoin"; // Default join type
|
||||
const joinCondition = `${alias}.${join.foreignColumn} = ${joinAlias}.${join.foreignColumn}`;
|
||||
for (const join of queryObject.join) {
|
||||
query.leftJoinAndSelect(`${alias}.${join.foreignColumn}`, join.table + "_" + (depth + 1));
|
||||
|
||||
queryBuilder[joinType](`${join.table}`, joinAlias, joinCondition);
|
||||
|
||||
// Recursively handle sub-joins and their conditions
|
||||
this.buildDynamicQuery(queryBuilder, join, depth + 1);
|
||||
});
|
||||
this.buildDynamicQuery(query, join, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle ORDER BY
|
||||
if (queryObject.orderBy) {
|
||||
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<T>(
|
||||
queryBuilder: SelectQueryBuilder<T>,
|
||||
public static applyWhere(
|
||||
query: SelectQueryBuilder<ObjectLiteral> | WhereExpressionBuilder,
|
||||
conditions: Array<ConditionStructure>,
|
||||
alias: string
|
||||
): void {
|
||||
conditions.forEach((condition, index) => {
|
||||
if (condition.structureType === "condition") {
|
||||
for (const condition of conditions) {
|
||||
if (condition.structureType == "condition") {
|
||||
const whereClause = this.buildConditionClause(condition, alias);
|
||||
const whereMethod = condition.concat === "_" ? "where" : "andWhere";
|
||||
|
||||
if (condition.concat === "OR") {
|
||||
queryBuilder.orWhere(whereClause.query, whereClause.parameters);
|
||||
if (condition.concat == "_" || condition.concat == "AND") {
|
||||
query.andWhere(whereClause.query, whereClause.parameters);
|
||||
} 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
|
||||
public static buildConditionClause(
|
||||
condition: ConditionStructure,
|
||||
private static buildConditionClause(
|
||||
condition: ConditionStructure & { structureType: "condition" },
|
||||
alias: string
|
||||
): { query: string; parameters: Record<string, unknown> } {
|
||||
if (condition.structureType == "nested") return;
|
||||
const parameterKey = `${alias}_${condition.column}_${Math.random().toString(36).substring(2)}`;
|
||||
let query = `${alias}.${condition.column}`;
|
||||
let parameters: Record<string, unknown> = {};
|
||||
|
@ -213,37 +203,8 @@ export default abstract class DynamicQueryBuilder {
|
|||
query += ` LIKE :${parameterKey}`;
|
||||
parameters[parameterKey] = `%${condition.value}`;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported operation: ${condition.operation}`);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ export type ConditionStructure = (
|
|||
| {
|
||||
structureType: "nested";
|
||||
invert?: boolean;
|
||||
condition: Array<ConditionStructure>;
|
||||
conditions: Array<ConditionStructure>;
|
||||
}
|
||||
) & {
|
||||
concat: WhereType;
|
||||
|
@ -52,7 +52,7 @@ export type OrderByStructure = {
|
|||
|
||||
export type OrderByType = "ASC" | "DESC";
|
||||
|
||||
const exampleQuery: DynamicQueryStructure = {
|
||||
export const exampleQuery: DynamicQueryStructure = {
|
||||
select: ["firstname", "lastname"],
|
||||
table: "member",
|
||||
where: [
|
||||
|
@ -66,7 +66,7 @@ const exampleQuery: DynamicQueryStructure = {
|
|||
{
|
||||
structureType: "nested",
|
||||
concat: "AND",
|
||||
condition: [
|
||||
conditions: [
|
||||
{
|
||||
structureType: "condition",
|
||||
concat: "_",
|
||||
|
|
Loading…
Reference in a new issue