Skip to content
This repository was archived by the owner on May 18, 2024. It is now read-only.

Commit 75bb60f

Browse files
authored
Merge pull request #76 from samchon/v1.0
Close #75, let `JoinQueryBuilder` to block dupllicated join
2 parents 306b14f + 8bdca22 commit 75bb60f

File tree

2 files changed

+109
-86
lines changed

2 files changed

+109
-86
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "safe-typeorm",
3-
"version": "1.0.15",
3+
"version": "1.0.16",
44
"description": "Make TypeORM much safer",
55
"main": "lib/index.js",
66
"typings": "lib/index.d.ts",

src/builders/JoinQueryBuilder.ts

+108-85
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DomainError } from "tstl/exception/DomainError";
12
import * as orm from "typeorm";
23

34
import { Belongs } from "../decorators/Belongs";
@@ -50,6 +51,14 @@ export class JoinQueryBuilder<Mine extends object> {
5051
private readonly mine_: Creator<Mine>;
5152
private readonly alias_: string;
5253

54+
private readonly joined_: Map<
55+
SpecialFields<Mine, Relationship.Joinable<any>>,
56+
IJoined
57+
>;
58+
59+
/* -----------------------------------------------------------
60+
CONNSTRUCTOR
61+
----------------------------------------------------------- */
5362
/**
5463
* Default Constructor.
5564
*
@@ -65,6 +74,58 @@ export class JoinQueryBuilder<Mine extends object> {
6574
this.stmt_ = stmt;
6675
this.mine_ = mine;
6776
this.alias_ = alias === undefined ? mine.name : alias;
77+
this.joined_ = new Map();
78+
}
79+
80+
private _Take_join<
81+
Field extends SpecialFields<Mine, Relationship.Joinable<any>>,
82+
>(
83+
method: IJoined.Method,
84+
field: SpecialFields<Mine, Relationship.Joinable<any>>,
85+
alias: string | undefined,
86+
closure:
87+
| ((
88+
builder: JoinQueryBuilder<
89+
Relationship.Joinable.TargetType<Mine, Field>
90+
>,
91+
) => void)
92+
| undefined,
93+
joiner: (asset: IAsset<Mine, Field>) => void,
94+
): JoinQueryBuilder<Relationship.Joinable.TargetType<Mine, Field>> {
95+
// PREPARE ASSET
96+
const asset: IAsset<Mine, Field> = prepare_asset(
97+
this.mine_,
98+
field,
99+
alias,
100+
);
101+
102+
// CHECK OLDBIE
103+
const oldbie: IJoined | undefined = this.joined_.get(field);
104+
if (oldbie !== undefined) {
105+
if (method !== oldbie.method)
106+
throw new DomainError(
107+
`Error on safe.JoinQueryBuilder.${method}(): ${this.mine_.name}.${field} already has been joined by ${oldbie.method}().`,
108+
);
109+
else if (asset.alias !== oldbie.alias)
110+
throw new DomainError(
111+
`Error on safe.JoinQueryBuilder.${method}(): ${this.mine_.name}.${field} already has been joined with another alias "${oldbie.alias}".`,
112+
);
113+
return oldbie.builder;
114+
}
115+
116+
// DO JOIN
117+
joiner(asset);
118+
119+
// BUILDER
120+
const builder = new JoinQueryBuilder(
121+
this.stmt_,
122+
asset.metadata.target(),
123+
asset.alias,
124+
);
125+
this.joined_.set(field, { method, alias: asset.alias, builder });
126+
127+
if (closure) closure(builder);
128+
return builder;
68129
}
69130

70131
/* -----------------------------------------------------------
@@ -146,8 +207,7 @@ export class JoinQueryBuilder<Mine extends object> {
146207
) => void,
147208
): JoinQueryBuilder<Relationship.Joinable.TargetType<Mine, Field>> {
148209
return this._Join_atomic(
149-
(target, alias, condition) =>
150-
this.stmt_.innerJoin(target, alias, condition),
210+
"innerJoin",
151211
field,
152212
...get_parametric_tuple(alias, closure),
153213
);
@@ -229,8 +289,7 @@ export class JoinQueryBuilder<Mine extends object> {
229289
) => void,
230290
): JoinQueryBuilder<Relationship.Joinable.TargetType<Mine, Field>> {
231291
return this._Join_atomic(
232-
(target, alias, condition) =>
233-
this.stmt_.leftJoin(target, alias, condition),
292+
"leftJoin",
234293
field,
235294
...get_parametric_tuple(alias, closure),
236295
);
@@ -239,11 +298,7 @@ export class JoinQueryBuilder<Mine extends object> {
239298
private _Join_atomic<
240299
Field extends SpecialFields<Mine, Relationship.Joinable<any>>,
241300
>(
242-
joiner: (
243-
target: Creator<Relationship.Joinable.TargetType<Mine, Field>>,
244-
alias: string,
245-
condition: string,
246-
) => orm.SelectQueryBuilder<any>,
301+
method: "innerJoin" | "leftJoin",
247302
field: Field,
248303
alias: string | undefined,
249304
closure:
@@ -254,44 +309,31 @@ export class JoinQueryBuilder<Mine extends object> {
254309
) => void)
255310
| undefined,
256311
): JoinQueryBuilder<Relationship.Joinable.TargetType<Mine, Field>> {
257-
// PREPRAE ASSET
258-
const asset: IAsset<Mine, Field> = prepare_asset(
259-
this.mine_,
260-
field,
261-
alias,
262-
);
312+
return this._Take_join(method, field, alias, closure, (asset) => {
313+
// LIST UP EACH FIELDS
314+
const [myField, targetField] = (() => {
315+
if (asset.belongs === true)
316+
return [
317+
asset.metadata.foreign_key_field,
318+
get_primary_column(asset.metadata.target()),
319+
];
320+
321+
const inverseMetadata: Belongs.ManyToOne.IMetadata<Mine> =
322+
ReflectAdaptor.get(
323+
asset.metadata.target().prototype,
324+
asset.metadata.inverse,
325+
) as Belongs.ManyToOne.IMetadata<Mine>;
263326

264-
// LIST UP EACH FIELDS
265-
const [myField, targetField] = (() => {
266-
if (asset.belongs === true)
267327
return [
268-
asset.metadata.foreign_key_field,
269-
get_primary_column(asset.metadata.target()),
328+
get_primary_column(this.mine_),
329+
inverseMetadata.foreign_key_field,
270330
];
331+
})();
271332

272-
const inverseMetadata: Belongs.ManyToOne.IMetadata<Mine> =
273-
ReflectAdaptor.get(
274-
asset.metadata.target().prototype,
275-
asset.metadata.inverse,
276-
) as Belongs.ManyToOne.IMetadata<Mine>;
277-
278-
return [
279-
inverseMetadata.foreign_key_field,
280-
get_primary_column(this.mine_),
281-
];
282-
})();
283-
284-
// DO JOIN
285-
const condition: string = `${this.alias_}.${myField} = ${asset.alias}.${targetField}`;
286-
joiner(asset.metadata.target(), asset.alias, condition);
287-
288-
// CALL-BACK
289-
return call_back(
290-
this.stmt_,
291-
asset.metadata.target(),
292-
asset.alias,
293-
closure,
294-
);
333+
// DO JOIN
334+
const condition: string = `${this.alias_}.${myField} = ${asset.alias}.${targetField}`;
335+
this.stmt_[method](asset.metadata.target(), asset.alias, condition);
336+
});
295337
}
296338

297339
/* -----------------------------------------------------------
@@ -383,7 +425,7 @@ export class JoinQueryBuilder<Mine extends object> {
383425
) => void,
384426
): JoinQueryBuilder<Relationship.Joinable.TargetType<Mine, Field>> {
385427
return this._Join_and_select(
386-
(field, alias) => this.stmt_.innerJoinAndSelect(field, alias),
428+
"innerJoinAndSelect",
387429
field,
388430
...get_parametric_tuple(alias, closure),
389431
);
@@ -475,7 +517,7 @@ export class JoinQueryBuilder<Mine extends object> {
475517
) => void,
476518
): JoinQueryBuilder<Relationship.Joinable.TargetType<Mine, Field>> {
477519
return this._Join_and_select(
478-
(field, alias) => this.stmt_.leftJoinAndSelect(field, alias),
520+
"leftJoinAndSelect",
479521
field,
480522
...get_parametric_tuple(alias, closure),
481523
);
@@ -484,7 +526,7 @@ export class JoinQueryBuilder<Mine extends object> {
484526
private _Join_and_select<
485527
Field extends SpecialFields<Mine, Relationship.Joinable<any>>,
486528
>(
487-
joiner: (field: string, alias: string) => orm.SelectQueryBuilder<any>,
529+
method: "innerJoinAndSelect" | "leftJoinAndSelect",
488530
field: Field,
489531
alias: string | undefined,
490532
closure:
@@ -495,33 +537,16 @@ export class JoinQueryBuilder<Mine extends object> {
495537
) => void)
496538
| undefined,
497539
) {
498-
// PREPARE ASSET
499-
const asset: IAsset<Mine, Field> = prepare_asset(
500-
this.mine_,
501-
field,
502-
alias,
503-
);
504-
const index: string = RelationshipVariable.getter(
505-
asset.belongs ? "belongs" : "has",
506-
field,
507-
);
508-
509-
// DO JOIN
510-
joiner(`${this.alias_}.${index}`, asset.alias);
511-
512-
// CALL-BACK
513-
return call_back(
514-
this.stmt_,
515-
asset.metadata.target(),
516-
asset.alias,
517-
closure,
518-
);
540+
return this._Take_join(method, field, alias, closure, (asset) => {
541+
const index: string = RelationshipVariable.getter(
542+
asset.belongs ? "belongs" : "has",
543+
field,
544+
);
545+
this.stmt_[method](`${this.alias_}.${index}`, asset.alias);
546+
});
519547
}
520548
}
521549

522-
/* -----------------------------------------------------------
523-
BACKGROUND
524-
----------------------------------------------------------- */
525550
type IAsset<
526551
Mine extends object,
527552
Field extends SpecialFields<Mine, Relationship.Joinable<any>>,
@@ -567,21 +592,6 @@ function prepare_asset<
567592
};
568593
}
569594

570-
function call_back<Target extends object>(
571-
stmt: orm.SelectQueryBuilder<any>,
572-
target: Creator<Target>,
573-
alias: string,
574-
closure: ((builder: JoinQueryBuilder<Target>) => void) | undefined,
575-
): JoinQueryBuilder<Target> {
576-
const ret: JoinQueryBuilder<Target> = new JoinQueryBuilder(
577-
stmt,
578-
target,
579-
alias,
580-
);
581-
if (closure !== undefined) closure(ret);
582-
return ret;
583-
}
584-
585595
function get_parametric_tuple<Func extends Function>(
586596
x?: string | Func,
587597
y?: Func,
@@ -592,3 +602,16 @@ function get_parametric_tuple<Func extends Function>(
592602
function get_primary_column(creator: Creator<object>): string {
593603
return ITableInfo.get(creator).primaryColumn;
594604
}
605+
606+
interface IJoined {
607+
method: IJoined.Method;
608+
alias: string;
609+
builder: JoinQueryBuilder<any>;
610+
}
611+
namespace IJoined {
612+
export type Method =
613+
| "innerJoin"
614+
| "leftJoin"
615+
| "innerJoinAndSelect"
616+
| "leftJoinAndSelect";
617+
}

0 commit comments

Comments
 (0)