@@ -34,7 +34,7 @@ import { EventToken } from '@deepkit/event';
34
34
export type SORT_ORDER = 'asc' | 'desc' | any ;
35
35
export type Sort < T extends OrmEntity , ORDER extends SORT_ORDER = SORT_ORDER > = { [ P in keyof T & string ] ?: ORDER } ;
36
36
37
- export interface DatabaseJoinModel < T extends OrmEntity , PARENT extends BaseQuery < any > > {
37
+ export interface DatabaseJoinModel < T extends OrmEntity > {
38
38
//this is the parent classSchema, the foreign classSchema is stored in `query`
39
39
classSchema : ReflectionClass < T > ,
40
40
propertySchema : ReflectionProperty ,
@@ -43,7 +43,7 @@ export interface DatabaseJoinModel<T extends OrmEntity, PARENT extends BaseQuery
43
43
//defines the field name under which the database engine populated the results.
44
44
//necessary for the formatter to pick it up, convert and set correctly the real field name
45
45
as ?: string ,
46
- query : JoinDatabaseQuery < T , PARENT > ,
46
+ query : BaseQuery < T > ,
47
47
foreignPrimaryKey : ReflectionProperty ,
48
48
}
49
49
@@ -94,7 +94,7 @@ export class DatabaseQueryModel<T extends OrmEntity, FILTER extends FilterQuery<
94
94
public aggregate = new Map < string , { property : ReflectionProperty , func : string } > ( ) ;
95
95
public select : Set < string > = new Set < string > ( ) ;
96
96
public lazyLoad : Set < string > = new Set < string > ( ) ;
97
- public joins : DatabaseJoinModel < any , any > [ ] = [ ] ;
97
+ public joins : DatabaseJoinModel < any > [ ] = [ ] ;
98
98
public skip ?: number ;
99
99
public itemsPerPage : number = 50 ;
100
100
public limit ?: number ;
@@ -151,12 +151,8 @@ export class DatabaseQueryModel<T extends OrmEntity, FILTER extends FilterQuery<
151
151
152
152
m . joins = this . joins . map ( ( v ) => {
153
153
return {
154
- classSchema : v . classSchema ,
155
- propertySchema : v . propertySchema ,
156
- type : v . type ,
157
- populate : v . populate ,
158
- query : v . query . clone ( parentQuery ) ,
159
- foreignPrimaryKey : v . foreignPrimaryKey ,
154
+ ...v ,
155
+ query : v . query . clone ( ) ,
160
156
} ;
161
157
} ) ;
162
158
@@ -213,6 +209,8 @@ export interface QueryClassType<T> {
213
209
create ( query : BaseQuery < any > ) : QueryClassType < T > ;
214
210
}
215
211
212
+ export type Configure < T extends OrmEntity > = ( query : BaseQuery < T > ) => BaseQuery < T > | void ;
213
+
216
214
export class BaseQuery < T extends OrmEntity > {
217
215
//for higher kinded type for selected fields
218
216
_ ! : ( ) => T ;
@@ -235,11 +233,11 @@ export class BaseQuery<T extends OrmEntity> {
235
233
*
236
234
* This allows to use more dynamic query composition functions.
237
235
*
238
- * To support joins queries `AnyQuery ` is necessary as query type.
236
+ * To support joins queries `BaseQuery ` is necessary as query type.
239
237
*
240
238
* @example
241
239
* ```typescript
242
- * function joinFrontendData(query: AnyQuery <Product>) {
240
+ * function joinFrontendData(query: BaseQuery <Product>) {
243
241
* return query
244
242
* .useJoinWith('images').select('sort').end()
245
243
* .useJoinWith('brand').select('id', 'name', 'website').end()
@@ -249,7 +247,8 @@ export class BaseQuery<T extends OrmEntity> {
249
247
* ```
250
248
* @reflection never
251
249
*/
252
- use < Q , R , A extends any [ ] > ( modifier : ( query : Q , ...args : A ) => R , ...args : A ) : this extends JoinDatabaseQuery < any , any > ? this : Exclude < R , JoinDatabaseQuery < any , any > > {
250
+ use < Q , R , A extends any [ ] > ( modifier : ( query : Q , ...args : A ) => R , ...args : A ) : this
251
+ {
253
252
return modifier ( this as any , ...args ) as any ;
254
253
}
255
254
@@ -536,23 +535,38 @@ export class BaseQuery<T extends OrmEntity> {
536
535
* Adds a left join in the filter. Does NOT populate the reference with values.
537
536
* Accessing `field` in the entity (if not optional field) results in an error.
538
537
*/
539
- join < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K , type : 'left' | 'inner' = 'left' , populate : boolean = false ) : this {
538
+ join < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > (
539
+ field : K , type : 'left' | 'inner' = 'left' , populate : boolean = false ,
540
+ configure ?: Configure < ENTITY >
541
+ ) : this {
542
+ return this . addJoin ( field , type , populate , configure ) [ 0 ] ;
543
+ }
544
+
545
+ /**
546
+ * Adds a left join in the filter and returns new this query and the join query.
547
+ */
548
+ protected addJoin < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > (
549
+ field : K , type : 'left' | 'inner' = 'left' , populate : boolean = false ,
550
+ configure ?: Configure < ENTITY >
551
+ ) : [ thisQuery : this, joinQuery : BaseQuery < ENTITY > ] {
540
552
const propertySchema = this . classSchema . getProperty ( field as string ) ;
541
553
if ( ! propertySchema . isReference ( ) && ! propertySchema . isBackReference ( ) ) {
542
554
throw new Error ( `Field ${ String ( field ) } is not marked as reference. Use Reference type` ) ;
543
555
}
544
556
const c = this . clone ( ) ;
545
557
546
558
const foreignReflectionClass = resolveForeignReflectionClass ( propertySchema ) ;
547
- const query = new JoinDatabaseQuery < ENTITY , this > ( foreignReflectionClass , c , field as string ) ;
559
+ let query = new BaseQuery < ENTITY > ( foreignReflectionClass ) ;
548
560
query . model . parameters = c . model . parameters ;
561
+ if ( configure ) query = configure ( query ) || query ;
549
562
550
563
c . model . joins . push ( {
551
564
propertySchema, query, populate, type,
552
565
foreignPrimaryKey : foreignReflectionClass . getPrimary ( ) ,
553
566
classSchema : this . classSchema ,
554
567
} ) ;
555
- return c ;
568
+
569
+ return [ c , query ] ;
556
570
}
557
571
558
572
/**
@@ -561,65 +575,65 @@ export class BaseQuery<T extends OrmEntity> {
561
575
* Returns JoinDatabaseQuery to further specify the join, which you need to `.end()`
562
576
*/
563
577
useJoin < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K ) : JoinDatabaseQuery < ENTITY , this> {
564
- const c = this . join ( field , 'left' ) ;
565
- return c . model . joins [ c . model . joins . length - 1 ] . query ;
578
+ const c = this . addJoin ( field , 'left' ) ;
579
+ return new JoinDatabaseQuery ( c [ 1 ] . classSchema , c [ 1 ] , c [ 0 ] ) ;
566
580
}
567
581
568
582
/**
569
583
* Adds a left join in the filter and populates the result set WITH reference field accordingly.
570
584
*/
571
- joinWith < K extends keyof ReferenceFields < T > > ( field : K ) : this {
572
- return this . join ( field , 'left' , true ) ;
585
+ joinWith < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K , configure ?: Configure < ENTITY > ) : this {
586
+ return this . addJoin ( field , 'left' , true , configure ) [ 0 ] ;
573
587
}
574
588
575
589
/**
576
590
* Adds a left join in the filter and populates the result set WITH reference field accordingly.
577
591
* Returns JoinDatabaseQuery to further specify the join, which you need to `.end()`
578
592
*/
579
593
useJoinWith < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K ) : JoinDatabaseQuery < ENTITY , this> {
580
- const c = this . join ( field , 'left' , true ) ;
581
- return c . model . joins [ c . model . joins . length - 1 ] . query ;
594
+ const c = this . addJoin ( field , 'left' , true ) ;
595
+ return new JoinDatabaseQuery ( c [ 1 ] . classSchema , c [ 1 ] , c [ 0 ] ) ;
582
596
}
583
597
584
- getJoin < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K ) : JoinDatabaseQuery < ENTITY , this > {
598
+ getJoin < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K ) : BaseQuery < ENTITY > {
585
599
for ( const join of this . model . joins ) {
586
600
if ( join . propertySchema . name === field ) return join . query ;
587
601
}
588
602
throw new Error ( `No join fo reference ${ String ( field ) } added.` ) ;
589
603
}
590
604
591
605
/**
592
- * Adds a inner join in the filter and populates the result set WITH reference field accordingly.
606
+ * Adds an inner join in the filter and populates the result set WITH reference field accordingly.
593
607
*/
594
- innerJoinWith < K extends keyof ReferenceFields < T > > ( field : K ) : this {
595
- return this . join ( field , 'inner' , true ) ;
608
+ innerJoinWith < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K , configure ?: Configure < ENTITY > ) : this {
609
+ return this . addJoin ( field , 'inner' , true , configure ) [ 0 ] ;
596
610
}
597
611
598
612
/**
599
- * Adds a inner join in the filter and populates the result set WITH reference field accordingly.
613
+ * Adds an inner join in the filter and populates the result set WITH reference field accordingly.
600
614
* Returns JoinDatabaseQuery to further specify the join, which you need to `.end()`
601
615
*/
602
616
useInnerJoinWith < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K ) : JoinDatabaseQuery < ENTITY , this> {
603
- const c = this . join ( field , 'inner' , true ) ;
604
- return c . model . joins [ c . model . joins . length - 1 ] . query ;
617
+ const c = this . addJoin ( field , 'inner' , true ) ;
618
+ return new JoinDatabaseQuery ( c [ 1 ] . classSchema , c [ 1 ] , c [ 0 ] ) ;
605
619
}
606
620
607
621
/**
608
- * Adds a inner join in the filter. Does NOT populate the reference with values.
622
+ * Adds an inner join in the filter. Does NOT populate the reference with values.
609
623
* Accessing `field` in the entity (if not optional field) results in an error.
610
624
*/
611
- innerJoin < K extends keyof ReferenceFields < T > > ( field : K ) : this {
612
- return this . join ( field , 'inner' ) ;
625
+ innerJoin < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K , configure ?: Configure < ENTITY > ) : this {
626
+ return this . addJoin ( field , 'inner' , false , configure ) [ 0 ] ;
613
627
}
614
628
615
629
/**
616
- * Adds a inner join in the filter. Does NOT populate the reference with values.
630
+ * Adds an inner join in the filter. Does NOT populate the reference with values.
617
631
* Accessing `field` in the entity (if not optional field) results in an error.
618
632
* Returns JoinDatabaseQuery to further specify the join, which you need to `.end()`
619
633
*/
620
634
useInnerJoin < K extends keyof ReferenceFields < T > , ENTITY extends OrmEntity = FindEntity < T [ K ] > > ( field : K ) : JoinDatabaseQuery < ENTITY , this> {
621
- const c = this . join ( field , 'inner' ) ;
622
- return c . model . joins [ c . model . joins . length - 1 ] . query ;
635
+ const c = this . addJoin ( field , 'inner' ) ;
636
+ return new JoinDatabaseQuery ( c [ 1 ] . classSchema , c [ 1 ] , c [ 0 ] ) ;
623
637
}
624
638
}
625
639
@@ -1002,27 +1016,27 @@ export class Query<T extends OrmEntity> extends BaseQuery<T> {
1002
1016
1003
1017
export class JoinDatabaseQuery < T extends OrmEntity , PARENT extends BaseQuery < any > > extends BaseQuery < T > {
1004
1018
constructor (
1005
- public readonly foreignClassSchema : ReflectionClass < T > ,
1006
- public parentQuery ?: PARENT ,
1007
- public field ?: string ,
1019
+ // important to have this as first argument, since clone() uses it
1020
+ classSchema : ReflectionClass < any > ,
1021
+ public query : BaseQuery < any > ,
1022
+ public parentQuery ?: PARENT
1008
1023
) {
1009
- super ( foreignClassSchema ) ;
1024
+ super ( classSchema ) ;
1010
1025
}
1011
1026
1012
1027
clone ( parentQuery ?: PARENT ) : this {
1013
1028
const c = super . clone ( ) ;
1014
1029
c . parentQuery = parentQuery || this . parentQuery ;
1015
- c . field = this . field ;
1030
+ c . query = this . query ;
1016
1031
return c ;
1017
1032
}
1018
1033
1019
1034
end ( ) : PARENT {
1020
1035
if ( ! this . parentQuery ) throw new Error ( 'Join has no parent query' ) ;
1021
- if ( ! this . field ) throw new Error ( 'Join has no field' ) ;
1022
1036
//the parentQuery has not the updated JoinDatabaseQuery stuff, we need to move it now to there
1023
- this . parentQuery . getJoin ( this . field ) . model = this . model ;
1037
+ this . query . model = this . model ;
1024
1038
return this . parentQuery ;
1025
1039
}
1026
1040
}
1027
1041
1028
- export type AnyQuery < T extends OrmEntity > = JoinDatabaseQuery < T , any > | Query < T > ;
1042
+ export type AnyQuery < T extends OrmEntity > = BaseQuery < T > ;
0 commit comments