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