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