@@ -336,6 +336,112 @@ serial):
336
336
selection set.
337
337
- Return an unordered map containing {data} and {errors}.
338
338
339
+ ### Field Collection
340
+
341
+ Before execution, the selection set is converted to a grouped field set by
342
+ calling {CollectFields()}. Each entry in the grouped field set is a list of
343
+ fields that share a response key (the alias if defined, otherwise the field
344
+ name). This ensures all fields with the same response key (including those in
345
+ referenced fragments) are executed at the same time.
346
+
347
+ As an example, collecting the fields of this selection set would collect two
348
+ instances of the field ` a ` and one of field ` b ` :
349
+
350
+ ``` graphql example
351
+ {
352
+ a {
353
+ subfield1
354
+ }
355
+ ... ExampleFragment
356
+ }
357
+
358
+ fragment ExampleFragment on Query {
359
+ a {
360
+ subfield2
361
+ }
362
+ b
363
+ }
364
+ ```
365
+
366
+ The depth-first-search order of the field groups produced by {CollectFields()}
367
+ is maintained through execution, ensuring that fields appear in the executed
368
+ response in a stable and predictable order.
369
+
370
+ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
371
+
372
+ - If {visitedFragments} is not provided, initialize it to the empty set.
373
+ - Initialize {groupedFields} to an empty ordered map of lists.
374
+ - For each {selection} in {selectionSet}:
375
+ - If {selection} provides the directive ` @skip ` , let {skipDirective} be that
376
+ directive.
377
+ - If {skipDirective}'s {if} argument is {true} or is a variable in
378
+ {variableValues} with the value {true}, continue with the next {selection}
379
+ in {selectionSet}.
380
+ - If {selection} provides the directive ` @include ` , let {includeDirective} be
381
+ that directive.
382
+ - If {includeDirective}'s {if} argument is not {true} and is not a variable
383
+ in {variableValues} with the value {true}, continue with the next
384
+ {selection} in {selectionSet}.
385
+ - If {selection} is a {Field}:
386
+ - Let {responseKey} be the response key of {selection} (the alias if
387
+ defined, otherwise the field name).
388
+ - Let {groupForResponseKey} be the list in {groupedFields} for
389
+ {responseKey}; if no such list exists, create it as an empty list.
390
+ - Append {selection} to the {groupForResponseKey}.
391
+ - If {selection} is a {FragmentSpread}:
392
+ - Let {fragmentSpreadName} be the name of {selection}.
393
+ - If {fragmentSpreadName} is in {visitedFragments}, continue with the next
394
+ {selection} in {selectionSet}.
395
+ - Add {fragmentSpreadName} to {visitedFragments}.
396
+ - Let {fragment} be the Fragment in the current Document whose name is
397
+ {fragmentSpreadName}.
398
+ - If no such {fragment} exists, continue with the next {selection} in
399
+ {selectionSet}.
400
+ - Let {fragmentType} be the type condition on {fragment}.
401
+ - If {DoesFragmentTypeApply(objectType, fragmentType)} is false, continue
402
+ with the next {selection} in {selectionSet}.
403
+ - Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
404
+ - Let {fragmentGroupedFieldSet} be the result of calling
405
+ {CollectFields(objectType, fragmentSelectionSet, variableValues,
406
+ visitedFragments)}.
407
+ - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
408
+ - Let {responseKey} be the response key shared by all fields in
409
+ {fragmentGroup}.
410
+ - Let {groupForResponseKey} be the list in {groupedFields} for
411
+ {responseKey}; if no such list exists, create it as an empty list.
412
+ - Append all items in {fragmentGroup} to {groupForResponseKey}.
413
+ - If {selection} is an {InlineFragment}:
414
+ - Let {fragmentType} be the type condition on {selection}.
415
+ - If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
416
+ fragmentType)} is false, continue with the next {selection} in
417
+ {selectionSet}.
418
+ - Let {fragmentSelectionSet} be the top-level selection set of {selection}.
419
+ - Let {fragmentGroupedFieldSet} be the result of calling
420
+ {CollectFields(objectType, fragmentSelectionSet, variableValues,
421
+ visitedFragments)}.
422
+ - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
423
+ - Let {responseKey} be the response key shared by all fields in
424
+ {fragmentGroup}.
425
+ - Let {groupForResponseKey} be the list in {groupedFields} for
426
+ {responseKey}; if no such list exists, create it as an empty list.
427
+ - Append all items in {fragmentGroup} to {groupForResponseKey}.
428
+ - Return {groupedFields}.
429
+
430
+ DoesFragmentTypeApply(objectType, fragmentType):
431
+
432
+ - If {fragmentType} is an Object Type:
433
+ - if {objectType} and {fragmentType} are the same type, return {true},
434
+ otherwise return {false}.
435
+ - If {fragmentType} is an Interface Type:
436
+ - if {objectType} is an implementation of {fragmentType}, return {true}
437
+ otherwise return {false}.
438
+ - If {fragmentType} is a Union:
439
+ - if {objectType} is a possible type of {fragmentType}, return {true}
440
+ otherwise return {false}.
441
+
442
+ Note: The steps in {CollectFields()} evaluating the ` @skip ` and ` @include `
443
+ directives may be applied in either order since they apply commutatively.
444
+
339
445
## Executing a Grouped Field Set
340
446
341
447
To execute a grouped field set, the object value being evaluated and the object
@@ -471,112 +577,6 @@ A correct executor must generate the following result for that selection set:
471
577
}
472
578
```
473
579
474
- ### Field Collection
475
-
476
- Before execution, the selection set is converted to a grouped field set by
477
- calling {CollectFields()}. Each entry in the grouped field set is a list of
478
- fields that share a response key (the alias if defined, otherwise the field
479
- name). This ensures all fields with the same response key (including those in
480
- referenced fragments) are executed at the same time.
481
-
482
- As an example, collecting the fields of this selection set would collect two
483
- instances of the field ` a ` and one of field ` b ` :
484
-
485
- ``` graphql example
486
- {
487
- a {
488
- subfield1
489
- }
490
- ... ExampleFragment
491
- }
492
-
493
- fragment ExampleFragment on Query {
494
- a {
495
- subfield2
496
- }
497
- b
498
- }
499
- ```
500
-
501
- The depth-first-search order of the field groups produced by {CollectFields()}
502
- is maintained through execution, ensuring that fields appear in the executed
503
- response in a stable and predictable order.
504
-
505
- CollectFields(objectType, selectionSet, variableValues, visitedFragments):
506
-
507
- - If {visitedFragments} is not provided, initialize it to the empty set.
508
- - Initialize {groupedFields} to an empty ordered map of lists.
509
- - For each {selection} in {selectionSet}:
510
- - If {selection} provides the directive ` @skip ` , let {skipDirective} be that
511
- directive.
512
- - If {skipDirective}'s {if} argument is {true} or is a variable in
513
- {variableValues} with the value {true}, continue with the next {selection}
514
- in {selectionSet}.
515
- - If {selection} provides the directive ` @include ` , let {includeDirective} be
516
- that directive.
517
- - If {includeDirective}'s {if} argument is not {true} and is not a variable
518
- in {variableValues} with the value {true}, continue with the next
519
- {selection} in {selectionSet}.
520
- - If {selection} is a {Field}:
521
- - Let {responseKey} be the response key of {selection} (the alias if
522
- defined, otherwise the field name).
523
- - Let {groupForResponseKey} be the list in {groupedFields} for
524
- {responseKey}; if no such list exists, create it as an empty list.
525
- - Append {selection} to the {groupForResponseKey}.
526
- - If {selection} is a {FragmentSpread}:
527
- - Let {fragmentSpreadName} be the name of {selection}.
528
- - If {fragmentSpreadName} is in {visitedFragments}, continue with the next
529
- {selection} in {selectionSet}.
530
- - Add {fragmentSpreadName} to {visitedFragments}.
531
- - Let {fragment} be the Fragment in the current Document whose name is
532
- {fragmentSpreadName}.
533
- - If no such {fragment} exists, continue with the next {selection} in
534
- {selectionSet}.
535
- - Let {fragmentType} be the type condition on {fragment}.
536
- - If {DoesFragmentTypeApply(objectType, fragmentType)} is false, continue
537
- with the next {selection} in {selectionSet}.
538
- - Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
539
- - Let {fragmentGroupedFieldSet} be the result of calling
540
- {CollectFields(objectType, fragmentSelectionSet, variableValues,
541
- visitedFragments)}.
542
- - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
543
- - Let {responseKey} be the response key shared by all fields in
544
- {fragmentGroup}.
545
- - Let {groupForResponseKey} be the list in {groupedFields} for
546
- {responseKey}; if no such list exists, create it as an empty list.
547
- - Append all items in {fragmentGroup} to {groupForResponseKey}.
548
- - If {selection} is an {InlineFragment}:
549
- - Let {fragmentType} be the type condition on {selection}.
550
- - If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
551
- fragmentType)} is false, continue with the next {selection} in
552
- {selectionSet}.
553
- - Let {fragmentSelectionSet} be the top-level selection set of {selection}.
554
- - Let {fragmentGroupedFieldSet} be the result of calling
555
- {CollectFields(objectType, fragmentSelectionSet, variableValues,
556
- visitedFragments)}.
557
- - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
558
- - Let {responseKey} be the response key shared by all fields in
559
- {fragmentGroup}.
560
- - Let {groupForResponseKey} be the list in {groupedFields} for
561
- {responseKey}; if no such list exists, create it as an empty list.
562
- - Append all items in {fragmentGroup} to {groupForResponseKey}.
563
- - Return {groupedFields}.
564
-
565
- DoesFragmentTypeApply(objectType, fragmentType):
566
-
567
- - If {fragmentType} is an Object Type:
568
- - if {objectType} and {fragmentType} are the same type, return {true},
569
- otherwise return {false}.
570
- - If {fragmentType} is an Interface Type:
571
- - if {objectType} is an implementation of {fragmentType}, return {true}
572
- otherwise return {false}.
573
- - If {fragmentType} is a Union:
574
- - if {objectType} is a possible type of {fragmentType}, return {true}
575
- otherwise return {false}.
576
-
577
- Note: The steps in {CollectFields()} evaluating the ` @skip ` and ` @include `
578
- directives may be applied in either order since they apply commutatively.
579
-
580
580
## Executing Fields
581
581
582
582
Each field requested in the grouped field set that is defined on the selected
0 commit comments