@@ -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
@@ -474,112 +580,6 @@ A correct executor must generate the following result for that _selection set_:
474
580
}
475
581
```
476
582
477
- ### Field Collection
478
-
479
- Before execution, the _ selection set_ is converted to a grouped field set by
480
- calling {CollectFields()}. Each entry in the grouped field set is a list of
481
- fields that share a response key (the alias if defined, otherwise the field
482
- name). This ensures all fields with the same response key (including those in
483
- referenced fragments) are executed at the same time.
484
-
485
- As an example, collecting the fields of this selection set would collect two
486
- instances of the field ` a ` and one of field ` b ` :
487
-
488
- ``` graphql example
489
- {
490
- a {
491
- subfield1
492
- }
493
- ... ExampleFragment
494
- }
495
-
496
- fragment ExampleFragment on Query {
497
- a {
498
- subfield2
499
- }
500
- b
501
- }
502
- ```
503
-
504
- The depth-first-search order of the field groups produced by {CollectFields()}
505
- is maintained through execution, ensuring that fields appear in the executed
506
- response in a stable and predictable order.
507
-
508
- CollectFields(objectType, selectionSet, variableValues, visitedFragments):
509
-
510
- - If {visitedFragments} is not provided, initialize it to the empty set.
511
- - Initialize {groupedFields} to an empty ordered map of lists.
512
- - For each {selection} in {selectionSet}:
513
- - If {selection} provides the directive ` @skip ` , let {skipDirective} be that
514
- directive.
515
- - If {skipDirective}'s {if} argument is {true} or is a variable in
516
- {variableValues} with the value {true}, continue with the next {selection}
517
- in {selectionSet}.
518
- - If {selection} provides the directive ` @include ` , let {includeDirective} be
519
- that directive.
520
- - If {includeDirective}'s {if} argument is not {true} and is not a variable
521
- in {variableValues} with the value {true}, continue with the next
522
- {selection} in {selectionSet}.
523
- - If {selection} is a {Field}:
524
- - Let {responseKey} be the response key of {selection} (the alias if
525
- defined, otherwise the field name).
526
- - Let {groupForResponseKey} be the list in {groupedFields} for
527
- {responseKey}; if no such list exists, create it as an empty list.
528
- - Append {selection} to the {groupForResponseKey}.
529
- - If {selection} is a {FragmentSpread}:
530
- - Let {fragmentSpreadName} be the name of {selection}.
531
- - If {fragmentSpreadName} is in {visitedFragments}, continue with the next
532
- {selection} in {selectionSet}.
533
- - Add {fragmentSpreadName} to {visitedFragments}.
534
- - Let {fragment} be the Fragment in the current Document whose name is
535
- {fragmentSpreadName}.
536
- - If no such {fragment} exists, continue with the next {selection} in
537
- {selectionSet}.
538
- - Let {fragmentType} be the type condition on {fragment}.
539
- - If {DoesFragmentTypeApply(objectType, fragmentType)} is {false}, continue
540
- with the next {selection} in {selectionSet}.
541
- - Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
542
- - Let {fragmentGroupedFieldSet} be the result of calling
543
- {CollectFields(objectType, fragmentSelectionSet, variableValues,
544
- visitedFragments)}.
545
- - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
546
- - Let {responseKey} be the response key shared by all fields in
547
- {fragmentGroup}.
548
- - Let {groupForResponseKey} be the list in {groupedFields} for
549
- {responseKey}; if no such list exists, create it as an empty list.
550
- - Append all items in {fragmentGroup} to {groupForResponseKey}.
551
- - If {selection} is an {InlineFragment}:
552
- - Let {fragmentType} be the type condition on {selection}.
553
- - If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
554
- fragmentType)} is {false}, continue with the next {selection} in
555
- {selectionSet}.
556
- - Let {fragmentSelectionSet} be the top-level selection set of {selection}.
557
- - Let {fragmentGroupedFieldSet} be the result of calling
558
- {CollectFields(objectType, fragmentSelectionSet, variableValues,
559
- visitedFragments)}.
560
- - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
561
- - Let {responseKey} be the response key shared by all fields in
562
- {fragmentGroup}.
563
- - Let {groupForResponseKey} be the list in {groupedFields} for
564
- {responseKey}; if no such list exists, create it as an empty list.
565
- - Append all items in {fragmentGroup} to {groupForResponseKey}.
566
- - Return {groupedFields}.
567
-
568
- DoesFragmentTypeApply(objectType, fragmentType):
569
-
570
- - If {fragmentType} is an Object Type:
571
- - If {objectType} and {fragmentType} are the same type, return {true},
572
- otherwise return {false}.
573
- - If {fragmentType} is an Interface Type:
574
- - If {objectType} is an implementation of {fragmentType}, return {true}
575
- otherwise return {false}.
576
- - If {fragmentType} is a Union:
577
- - If {objectType} is a possible type of {fragmentType}, return {true}
578
- otherwise return {false}.
579
-
580
- Note: The steps in {CollectFields()} evaluating the ` @skip ` and ` @include `
581
- directives may be applied in either order since they apply commutatively.
582
-
583
583
## Executing Fields
584
584
585
585
Each field requested in the grouped field set that is defined on the selected
0 commit comments