@@ -180,47 +180,102 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
180
180
return ;
181
181
}
182
182
183
- for ( const kind of [ 'select' , 'include' ] as const ) {
184
- if ( args [ kind ] && typeof args [ kind ] === 'object' ) {
185
- for ( const [ field , value ] of Object . entries < any > ( args [ kind ] ) ) {
186
- const fieldInfo = resolveField ( this . options . modelMeta , model , field ) ;
187
- if ( ! fieldInfo ) {
188
- continue ;
189
- }
183
+ // there're two cases where we need to inject polymorphic base hierarchy for fields
184
+ // defined in base models
185
+ // 1. base fields mentioned in select/include clause
186
+ // { select: { fieldFromBase: true } } => { select: { delegate_aux_[Base]: { fieldFromBase: true } } }
187
+ // 2. base fields mentioned in _count select/include clause
188
+ // { select: { _count: { select: { fieldFromBase: true } } } } => { select: { delegate_aux_[Base]: { select: { _count: { select: { fieldFromBase: true } } } } } }
189
+ //
190
+ // Note that although structurally similar, we need to correctly deal with different injection location of the "delegate_aux" hierarchy
191
+
192
+ // selectors for the above two cases
193
+ const selectors = [
194
+ // regular select: { select: { field: true } }
195
+ ( payload : any ) => ( { data : payload . select , kind : 'select' as const , isCount : false } ) ,
196
+ // regular include: { include: { field: true } }
197
+ ( payload : any ) => ( { data : payload . include , kind : 'include' as const , isCount : false } ) ,
198
+ // select _count: { select: { _count: { select: { field: true } } } }
199
+ ( payload : any ) => ( {
200
+ data : payload . select ?. _count ?. select ,
201
+ kind : 'select' as const ,
202
+ isCount : true ,
203
+ } ) ,
204
+ // include _count: { include: { _count: { select: { field: true } } } }
205
+ ( payload : any ) => ( {
206
+ data : payload . include ?. _count ?. select ,
207
+ kind : 'include' as const ,
208
+ isCount : true ,
209
+ } ) ,
210
+ ] ;
211
+
212
+ for ( const selector of selectors ) {
213
+ const { data, kind, isCount } = selector ( args ) ;
214
+ if ( ! data || typeof data !== 'object' ) {
215
+ continue ;
216
+ }
190
217
191
- if ( this . isDelegateOrDescendantOfDelegate ( fieldInfo ?. type ) && value ) {
192
- // delegate model, recursively inject hierarchy
193
- if ( args [ kind ] [ field ] ) {
194
- if ( args [ kind ] [ field ] === true ) {
195
- // make sure the payload is an object
196
- args [ kind ] [ field ] = { } ;
197
- }
198
- await this . injectSelectIncludeHierarchy ( fieldInfo . type , args [ kind ] [ field ] ) ;
218
+ for ( const [ field , value ] of Object . entries < any > ( data ) ) {
219
+ const fieldInfo = resolveField ( this . options . modelMeta , model , field ) ;
220
+ if ( ! fieldInfo ) {
221
+ continue ;
222
+ }
223
+
224
+ if ( this . isDelegateOrDescendantOfDelegate ( fieldInfo ?. type ) && value ) {
225
+ // delegate model, recursively inject hierarchy
226
+ if ( data [ field ] ) {
227
+ if ( data [ field ] === true ) {
228
+ // make sure the payload is an object
229
+ data [ field ] = { } ;
199
230
}
231
+ await this . injectSelectIncludeHierarchy ( fieldInfo . type , data [ field ] ) ;
200
232
}
233
+ }
201
234
202
- // refetch the field select/include value because it may have been
203
- // updated during injection
204
- const fieldValue = args [ kind ] [ field ] ;
235
+ // refetch the field select/include value because it may have been
236
+ // updated during injection
237
+ const fieldValue = data [ field ] ;
205
238
206
- if ( fieldValue !== undefined ) {
207
- if ( fieldValue . orderBy ) {
208
- // `orderBy` may contain fields from base types
209
- enumerate ( fieldValue . orderBy ) . forEach ( ( item ) =>
210
- this . injectWhereHierarchy ( fieldInfo . type , item )
211
- ) ;
212
- }
239
+ if ( fieldValue !== undefined ) {
240
+ if ( fieldValue . orderBy ) {
241
+ // `orderBy` may contain fields from base types
242
+ enumerate ( fieldValue . orderBy ) . forEach ( ( item ) =>
243
+ this . injectWhereHierarchy ( fieldInfo . type , item )
244
+ ) ;
245
+ }
213
246
214
- if ( this . injectBaseFieldSelect ( model , field , fieldValue , args , kind ) ) {
215
- delete args [ kind ] [ field ] ;
216
- } else if ( fieldInfo . isDataModel ) {
217
- let nextValue = fieldValue ;
218
- if ( nextValue === true ) {
219
- // make sure the payload is an object
220
- args [ kind ] [ field ] = nextValue = { } ;
247
+ let injected = false ;
248
+ if ( ! isCount ) {
249
+ // regular select/include injection
250
+ injected = await this . injectBaseFieldSelect ( model , field , fieldValue , args , kind ) ;
251
+ if ( injected ) {
252
+ // if injected, remove the field from the original payload
253
+ delete data [ field ] ;
254
+ }
255
+ } else {
256
+ // _count select/include injection, inject into an empty payload and then merge to the proper location
257
+ const injectTarget = { [ kind ] : { } } ;
258
+ injected = await this . injectBaseFieldSelect ( model , field , fieldValue , injectTarget , kind , true ) ;
259
+ if ( injected ) {
260
+ // if injected, remove the field from the original payload
261
+ delete data [ field ] ;
262
+ if ( Object . keys ( data ) . length === 0 ) {
263
+ // if the original "_count" payload becomes empty, remove it
264
+ delete args [ kind ] [ '_count' ] ;
221
265
}
222
- await this . injectSelectIncludeHierarchy ( fieldInfo . type , nextValue ) ;
266
+ // finally merge the injection into the original payload
267
+ const merged = deepmerge ( args [ kind ] , injectTarget [ kind ] ) ;
268
+ args [ kind ] = merged ;
269
+ }
270
+ }
271
+
272
+ if ( ! injected && fieldInfo . isDataModel ) {
273
+ let nextValue = fieldValue ;
274
+ if ( nextValue === true ) {
275
+ // make sure the payload is an object
276
+ data [ field ] = nextValue = { } ;
223
277
}
278
+ await this . injectSelectIncludeHierarchy ( fieldInfo . type , nextValue ) ;
224
279
}
225
280
}
226
281
}
@@ -272,7 +327,8 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
272
327
field : string ,
273
328
value : any ,
274
329
selectInclude : any ,
275
- context : 'select' | 'include'
330
+ context : 'select' | 'include' ,
331
+ forCount = false // if the injection is for a "{ _count: { select: { field: true } } }" payload
276
332
) {
277
333
const fieldInfo = resolveField ( this . options . modelMeta , model , field ) ;
278
334
if ( ! fieldInfo ?. inheritedFrom ) {
@@ -286,24 +342,35 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
286
342
const baseRelationName = this . makeAuxRelationName ( base ) ;
287
343
288
344
// prepare base layer select/include
289
- // let selectOrInclude = 'select';
290
345
let thisLayer : any ;
291
346
if ( target . include ) {
292
- // selectOrInclude = 'include';
293
347
thisLayer = target . include ;
294
348
} else if ( target . select ) {
295
- // selectOrInclude = 'select';
296
349
thisLayer = target . select ;
297
350
} else {
298
- // selectInclude = 'include';
299
351
thisLayer = target . select = { } ;
300
352
}
301
353
302
354
if ( base . name === fieldInfo . inheritedFrom ) {
303
355
if ( ! thisLayer [ baseRelationName ] ) {
304
356
thisLayer [ baseRelationName ] = { [ context ] : { } } ;
305
357
}
306
- thisLayer [ baseRelationName ] [ context ] [ field ] = value ;
358
+ if ( forCount ) {
359
+ // { _count: { select: { field: true } } } => { delegate_aux_[Base]: { select: { _count: { select: { field: true } } } } }
360
+ if (
361
+ ! thisLayer [ baseRelationName ] [ context ] [ '_count' ] ||
362
+ typeof thisLayer [ baseRelationName ] [ context ] !== 'object'
363
+ ) {
364
+ thisLayer [ baseRelationName ] [ context ] [ '_count' ] = { } ;
365
+ }
366
+ thisLayer [ baseRelationName ] [ context ] [ '_count' ] = deepmerge (
367
+ thisLayer [ baseRelationName ] [ context ] [ '_count' ] ,
368
+ { select : { [ field ] : value } }
369
+ ) ;
370
+ } else {
371
+ // { select: { field: true } } => { delegate_aux_[Base]: { select: { field: true } } }
372
+ thisLayer [ baseRelationName ] [ context ] [ field ] = value ;
373
+ }
307
374
break ;
308
375
} else {
309
376
if ( ! thisLayer [ baseRelationName ] ) {
0 commit comments