55 FindManyOptions ,
66 FindOneOptions ,
77 getMetadataArgsStorage ,
8+ In ,
89 ObjectLiteral ,
910 Repository ,
1011 SaveOptions ,
@@ -25,7 +26,11 @@ import { POLYMORPHIC_REPOSITORY } from './constants';
2526type PolymorphicHydrationType = {
2627 key : string ;
2728 type : 'children' | 'parent' ;
28- values : PolymorphicChildInterface [ ] | PolymorphicChildInterface ;
29+ hasMany : boolean ;
30+ valueKeyMap : Record <
31+ string ,
32+ ( PolymorphicChildInterface | PolymorphicChildInterface [ ] ) [ ]
33+ > ;
2934} ;
3035
3136const entityTypeColumn = ( options : PolymorphicMetadataInterface ) : string =>
@@ -105,94 +110,142 @@ export abstract class AbstractPolymorphicRepository<
105110 }
106111
107112 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 ) ;
109115 }
110116
111117 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 ] ;
115120 }
116121
117122 private async hydratePolymorphs (
118- entity : E ,
123+ entities : E [ ] ,
119124 options : PolymorphicMetadataInterface [ ] ,
120- ) : Promise < E > {
125+ ) : Promise < E [ ] > {
121126 const values = await Promise . all (
122127 options . map ( ( option : PolymorphicMetadataInterface ) =>
123- this . hydrateEntities ( entity , option ) ,
128+ this . hydrateEntities ( entities , option ) ,
124129 ) ,
125130 ) ;
126131
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 ;
140153 }
141154
142155 private async hydrateEntities (
143- entity : E ,
156+ entities : E [ ] ,
144157 options : PolymorphicMetadataInterface ,
145158 ) : Promise < PolymorphicHydrationType > {
159+ const typeColumn = entityTypeColumn ( options ) ;
146160 const entityTypes : ( Function | string ) [ ] =
147161 options . type === 'parent'
148- ? [ entity [ entityTypeColumn ( options ) ] ]
162+ ? [ ... new Set ( entities . map ( ( e ) => e [ typeColumn ] ) ) ]
149163 : Array . isArray ( options . classType )
150164 ? options . classType
151165 : [ options . classType ] ;
152166
153167 // TODO if not hasMany, should I return if one is found?
154168 const results = await Promise . all (
155169 entityTypes . map ( ( type : Function ) =>
156- this . findPolymorphs ( entity , type , options ) ,
170+ this . findPolymorphs ( entities , type , options ) ,
157171 ) ,
158172 ) ;
159173
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+ } , { } ) ;
160214 return {
161215 key : options . propertyKey ,
162216 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 ,
175219 } ;
176220 }
177221
178222 private async findPolymorphs (
179- parent : E ,
223+ entities : E [ ] ,
180224 entityType : Function ,
181225 options : PolymorphicMetadataInterface ,
182226 ) : Promise < PolymorphicChildInterface [ ] | PolymorphicChildInterface | never > {
183227 const repository = this . findRepository ( entityType ) ;
228+ const idColumn = entityIdColumn ( options ) ;
229+ const primaryColumn = PrimaryColumn ( options ) ;
184230
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 ] (
186239 options . type === 'parent'
187240 ? {
188241 where : {
189242 // 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 ] ) ) ,
191244 } ,
192245 }
193246 : {
194247 where : {
195- [ entityIdColumn ( options ) ] : parent [ PrimaryColumn ( options ) ] ,
248+ [ idColumn ] : In ( filteredEntities . map ( ( p ) => p [ primaryColumn ] ) ) ,
196249 [ entityTypeColumn ( options ) ] : entityType ,
197250 } ,
198251 } ,
@@ -338,9 +391,7 @@ export abstract class AbstractPolymorphicRepository<
338391
339392 const metadata = this . getPolymorphicMetadata ( ) ;
340393
341- return Promise . all (
342- results . map ( ( entity ) => this . hydratePolymorphs ( entity , metadata ) ) ,
343- ) ;
394+ return this . hydratePolymorphs ( results , metadata ) ;
344395 }
345396
346397 public async findOne ( options ?: FindOneOptions < E > ) : Promise < E | null > {
@@ -356,7 +407,8 @@ export abstract class AbstractPolymorphicRepository<
356407 return entity ;
357408 }
358409
359- return this . hydratePolymorphs ( entity , polymorphicMetadata ) ;
410+ const results = await this . hydratePolymorphs ( [ entity ] , polymorphicMetadata ) ;
411+ return results [ 0 ] ;
360412 }
361413
362414 create ( ) : E ;
0 commit comments