5
5
FindManyOptions ,
6
6
FindOneOptions ,
7
7
getMetadataArgsStorage ,
8
+ In ,
8
9
ObjectLiteral ,
9
10
Repository ,
10
11
SaveOptions ,
@@ -25,7 +26,11 @@ import { POLYMORPHIC_REPOSITORY } from './constants';
25
26
type PolymorphicHydrationType = {
26
27
key : string ;
27
28
type : 'children' | 'parent' ;
28
- values : PolymorphicChildInterface [ ] | PolymorphicChildInterface ;
29
+ hasMany : boolean ;
30
+ valueKeyMap : Record <
31
+ string ,
32
+ ( PolymorphicChildInterface | PolymorphicChildInterface [ ] ) [ ]
33
+ > ;
29
34
} ;
30
35
31
36
const entityTypeColumn = ( options : PolymorphicMetadataInterface ) : string =>
@@ -105,94 +110,142 @@ export abstract class AbstractPolymorphicRepository<
105
110
}
106
111
107
112
public async hydrateMany ( entities : E [ ] ) : Promise < E [ ] > {
108
- return Promise . all ( entities . map ( ( ent ) => this . hydrateOne ( ent ) ) ) ;
113
+ const metadata = this . getPolymorphicMetadata ( ) ;
114
+ return this . hydratePolymorphs ( entities , metadata ) ;
109
115
}
110
116
111
117
public async hydrateOne ( entity : E ) : Promise < E > {
112
- const metadata = this . getPolymorphicMetadata ( ) ;
113
-
114
- return this . hydratePolymorphs ( entity , metadata ) ;
118
+ const result = await this . hydrateMany ( [ entity ] ) ;
119
+ return result [ 0 ] ;
115
120
}
116
121
117
122
private async hydratePolymorphs (
118
- entity : E ,
123
+ entities : E [ ] ,
119
124
options : PolymorphicMetadataInterface [ ] ,
120
- ) : Promise < E > {
125
+ ) : Promise < E [ ] > {
121
126
const values = await Promise . all (
122
127
options . map ( ( option : PolymorphicMetadataInterface ) =>
123
- this . hydrateEntities ( entity , option ) ,
128
+ this . hydrateEntities ( entities , option ) ,
124
129
) ,
125
130
) ;
126
131
127
- return values . reduce < E > ( ( e : E , vals : PolymorphicHydrationType ) => {
128
- const values =
129
- vals . type === 'parent' && Array . isArray ( vals . values )
130
- ? vals . values . filter ( ( v ) => typeof v !== 'undefined' && v !== null )
131
- : vals . values ;
132
- const polys =
133
- vals . type === 'parent' && Array . isArray ( values ) ? values [ 0 ] : values ; // TODO should be condition for !hasMany
134
- type EntityKey = keyof E ;
135
- const key = vals . key as EntityKey ;
136
- e [ key ] = polys as ( typeof e ) [ typeof key ] ;
137
-
138
- return e ;
139
- } , entity ) ;
132
+ const results : E [ ] = [ ] ;
133
+ for ( let entity of entities ) {
134
+ const result = values . reduce < E > (
135
+ ( e : E , vals : PolymorphicHydrationType ) => {
136
+ const polyKey = `${ e . entityType } :${ e . entityId } ` ;
137
+ const polys = vals . hasMany
138
+ ? vals . valueKeyMap [ polyKey ]
139
+ : vals . valueKeyMap [ polyKey ] [ 0 ] ;
140
+
141
+ type EntityKey = keyof E ;
142
+ const key = vals . key as EntityKey ;
143
+ e [ key ] = polys as ( typeof e ) [ typeof key ] ;
144
+ return e ;
145
+ } ,
146
+ entity ,
147
+ ) ;
148
+
149
+ results . push ( result ) ;
150
+ }
151
+
152
+ return results ;
140
153
}
141
154
142
155
private async hydrateEntities (
143
- entity : E ,
156
+ entities : E [ ] ,
144
157
options : PolymorphicMetadataInterface ,
145
158
) : Promise < PolymorphicHydrationType > {
159
+ const typeColumn = entityTypeColumn ( options ) ;
146
160
const entityTypes : ( Function | string ) [ ] =
147
161
options . type === 'parent'
148
- ? [ entity [ entityTypeColumn ( options ) ] ]
162
+ ? [ ... new Set ( entities . map ( ( e ) => e [ typeColumn ] ) ) ]
149
163
: Array . isArray ( options . classType )
150
164
? options . classType
151
165
: [ options . classType ] ;
152
166
153
167
// TODO if not hasMany, should I return if one is found?
154
168
const results = await Promise . all (
155
169
entityTypes . map ( ( type : Function ) =>
156
- this . findPolymorphs ( entity , type , options ) ,
170
+ this . findPolymorphs ( entities , type , options ) ,
157
171
) ,
158
172
) ;
159
173
174
+ const idColumn = entityIdColumn ( options ) ;
175
+ const isParent = this . isParent ( options ) ;
176
+ const primaryColumn = PrimaryColumn ( options ) ;
177
+
178
+ const entitiesResultMap = results
179
+ // flatten all the results
180
+ . reduce < PolymorphicChildInterface [ ] > ( ( acc , val ) => {
181
+ if ( Array . isArray ( val ) ) {
182
+ acc . push ( ...val ) ;
183
+ } else {
184
+ acc . push ( val ) ;
185
+ }
186
+ return acc ;
187
+ } , [ ] )
188
+ // map the results to a keyed map by entityType & entityId
189
+ . reduce <
190
+ Record <
191
+ string ,
192
+ ( PolymorphicChildInterface | PolymorphicChildInterface [ ] ) [ ]
193
+ >
194
+ > ( ( acc , val ) => {
195
+ let key : string ;
196
+ if ( isParent ) {
197
+ const [ pColumnVal , entityType ] = Array . isArray ( val )
198
+ ? [ val [ 0 ] [ primaryColumn ] , val [ 0 ] . constructor . name ]
199
+ : [ val [ primaryColumn ] , val . constructor . name ] ;
200
+
201
+ key = `${ entityType } :${ pColumnVal } ` ;
202
+ } else {
203
+ const [ idColumnVal , typeColumnVal ] = Array . isArray ( val )
204
+ ? [ val [ 0 ] [ idColumn ] , val [ 0 ] [ typeColumn ] ]
205
+ : [ val [ idColumn ] , val [ typeColumn ] ] ;
206
+
207
+ key = `${ typeColumnVal } :${ idColumnVal } ` ;
208
+ }
209
+
210
+ acc [ key ] = acc [ key ] || [ ] ;
211
+ acc [ key ] . push ( val ) ;
212
+ return acc ;
213
+ } , { } ) ;
160
214
return {
161
215
key : options . propertyKey ,
162
216
type : options . type ,
163
- values : ( options . hasMany &&
164
- Array . isArray ( results ) &&
165
- results . length > 0 &&
166
- Array . isArray ( results [ 0 ] )
167
- ? results . reduce < PolymorphicChildInterface [ ] > (
168
- (
169
- resultEntities : PolymorphicChildInterface [ ] ,
170
- entities : PolymorphicChildInterface [ ] ,
171
- ) => entities . concat ( ...resultEntities ) ,
172
- results as PolymorphicChildInterface [ ] ,
173
- )
174
- : results ) as PolymorphicChildInterface | PolymorphicChildInterface [ ] ,
217
+ hasMany : options . hasMany ,
218
+ valueKeyMap : entitiesResultMap ,
175
219
} ;
176
220
}
177
221
178
222
private async findPolymorphs (
179
- parent : E ,
223
+ entities : E [ ] ,
180
224
entityType : Function ,
181
225
options : PolymorphicMetadataInterface ,
182
226
) : Promise < PolymorphicChildInterface [ ] | PolymorphicChildInterface | never > {
183
227
const repository = this . findRepository ( entityType ) ;
228
+ const idColumn = entityIdColumn ( options ) ;
229
+ const primaryColumn = PrimaryColumn ( options ) ;
184
230
185
- return repository [ options . hasMany ? 'find' : 'findOne' ] (
231
+ // filter out any entities that don't match the given entityType
232
+ const filteredEntities = entities . filter ( ( e ) => {
233
+ return repository . target . toString ( ) === e . entityType ;
234
+ } ) ;
235
+
236
+ const method =
237
+ options . hasMany || filteredEntities . length > 1 ? 'find' : 'findOne' ;
238
+ return repository [ method ] (
186
239
options . type === 'parent'
187
240
? {
188
241
where : {
189
242
// TODO: Not sure about this change (key was just id before)
190
- [ PrimaryColumn ( options ) ] : parent [ entityIdColumn ( options ) ] ,
243
+ [ primaryColumn ] : In ( filteredEntities . map ( ( p ) => p [ idColumn ] ) ) ,
191
244
} ,
192
245
}
193
246
: {
194
247
where : {
195
- [ entityIdColumn ( options ) ] : parent [ PrimaryColumn ( options ) ] ,
248
+ [ idColumn ] : In ( filteredEntities . map ( ( p ) => p [ primaryColumn ] ) ) ,
196
249
[ entityTypeColumn ( options ) ] : entityType ,
197
250
} ,
198
251
} ,
@@ -338,9 +391,7 @@ export abstract class AbstractPolymorphicRepository<
338
391
339
392
const metadata = this . getPolymorphicMetadata ( ) ;
340
393
341
- return Promise . all (
342
- results . map ( ( entity ) => this . hydratePolymorphs ( entity , metadata ) ) ,
343
- ) ;
394
+ return this . hydratePolymorphs ( results , metadata ) ;
344
395
}
345
396
346
397
public async findOne ( options ?: FindOneOptions < E > ) : Promise < E | null > {
@@ -356,7 +407,8 @@ export abstract class AbstractPolymorphicRepository<
356
407
return entity ;
357
408
}
358
409
359
- return this . hydratePolymorphs ( entity , polymorphicMetadata ) ;
410
+ const results = await this . hydratePolymorphs ( [ entity ] , polymorphicMetadata ) ;
411
+ return results [ 0 ] ;
360
412
}
361
413
362
414
create ( ) : E ;
0 commit comments