@@ -490,185 +490,6 @@ BatchIncrementalResults(incrementalResults):
490
490
of {hasNext} on the final item in the list.
491
491
- Yield {batchedIncrementalResult}.
492
492
493
- ### Field Collection
494
-
495
- Before execution, the _ selection set_ is converted to a grouped field set by
496
- calling {CollectFields()}. Each entry in the grouped field set is a list of
497
- fields that share a response key (the alias if defined, otherwise the field
498
- name). This ensures all fields with the same response key (including those in
499
- referenced fragments) are executed at the same time.
500
-
501
- As an example, collecting the fields of this selection set would collect two
502
- instances of the field ` a ` and one of field ` b ` :
503
-
504
- ``` graphql example
505
- {
506
- a {
507
- subfield1
508
- }
509
- ... ExampleFragment
510
- }
511
-
512
- fragment ExampleFragment on Query {
513
- a {
514
- subfield2
515
- }
516
- b
517
- }
518
- ```
519
-
520
- The depth-first-search order of the field groups produced by {CollectFields()}
521
- is maintained through execution, ensuring that fields appear in the executed
522
- response in a stable and predictable order.
523
-
524
- CollectFields(objectType, selectionSet, variableValues, deferUsage,
525
- visitedFragments):
526
-
527
- - If {visitedFragments} is not provided, initialize it to the empty set.
528
- - Initialize {groupedFields} to an empty ordered map of lists.
529
- - Initialize {newDeferUsages} to an empty list.
530
- - For each {selection} in {selectionSet}:
531
- - If {selection} provides the directive ` @skip ` , let {skipDirective} be that
532
- directive.
533
- - If {skipDirective}'s {if} argument is {true} or is a variable in
534
- {variableValues} with the value {true}, continue with the next {selection}
535
- in {selectionSet}.
536
- - If {selection} provides the directive ` @include ` , let {includeDirective} be
537
- that directive.
538
- - If {includeDirective}'s {if} argument is not {true} and is not a variable
539
- in {variableValues} with the value {true}, continue with the next
540
- {selection} in {selectionSet}.
541
- - If {selection} is a {Field}:
542
- - Let {responseKey} be the response key of {selection} (the alias if
543
- defined, otherwise the field name).
544
- - Let {fieldDetails} be a new unordered map containing {deferUsage}.
545
- - Set the entry for {field} on {fieldDetails} to {selection}. and
546
- {deferUsage}.
547
- - Let {groupForResponseKey} be the list in {groupedFields} for
548
- {responseKey}; if no such list exists, create it as an empty list.
549
- - Append {fieldDetails} to the {groupForResponseKey}.
550
- - If {selection} is a {FragmentSpread}:
551
- - Let {fragmentSpreadName} be the name of {selection}.
552
- - If {fragmentSpreadName} provides the directive ` @defer ` and its {if}
553
- argument is not {false} and is not a variable in {variableValues} with the
554
- value {false}:
555
- - Let {deferDirective} be that directive.
556
- - If this execution is for a subscription operation, raise a _ field
557
- error_ .
558
- - If {deferDirective} is not defined:
559
- - If {fragmentSpreadName} is in {visitedFragments}, continue with the next
560
- {selection} in {selectionSet}.
561
- - Add {fragmentSpreadName} to {visitedFragments}.
562
- - Let {fragment} be the Fragment in the current Document whose name is
563
- {fragmentSpreadName}.
564
- - If no such {fragment} exists, continue with the next {selection} in
565
- {selectionSet}.
566
- - Let {fragmentType} be the type condition on {fragment}.
567
- - If {DoesFragmentTypeApply(objectType, fragmentType)} is {false}, continue
568
- with the next {selection} in {selectionSet}.
569
- - Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
570
- - If {deferDirective} is defined:
571
- - Let {path} be the corresponding entry on {deferDirective}.
572
- - Let {parentDeferUsage} be {deferUsage}.
573
- - Let {fragmentDeferUsage} be an unordered map containing {path} and
574
- {parentDeferUsage}.
575
- - Otherwise, let {fragmentDeferUsage} be {deferUsage}.
576
- - Let {fragmentGroupedFieldSet} and {fragmentNewDeferUsages} be the result
577
- of calling {CollectFields(objectType, fragmentSelectionSet,
578
- variableValues, fragmentDeferUsage, visitedFragments)}.
579
- - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
580
- - Let {responseKey} be the response key shared by all fields in
581
- {fragmentGroup}.
582
- - Let {groupForResponseKey} be the list in {groupedFields} for
583
- {responseKey}; if no such list exists, create it as an empty list.
584
- - Append all items in {fragmentGroup} to {groupForResponseKey}.
585
- - Append all items in {fragmentNewDeferUsages} to {newDeferUsages}.
586
- - If {selection} is an {InlineFragment}:
587
- - Let {fragmentType} be the type condition on {selection}.
588
- - If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
589
- fragmentType)} is {false}, continue with the next {selection} in
590
- {selectionSet}.
591
- - Let {fragmentSelectionSet} be the top-level selection set of {selection}.
592
- - If {InlineFragment} provides the directive ` @defer ` and its {if} argument
593
- is not {false} and is not a variable in {variableValues} with the value
594
- {false}:
595
- - Let {deferDirective} be that directive.
596
- - If this execution is for a subscription operation, raise a _ field
597
- error_ .
598
- - If {deferDirective} is defined:
599
- - Let {path} be the corresponding entry on {deferDirective}.
600
- - Let {parentDeferUsage} be {deferUsage}.
601
- - Let {fragmentDeferUsage} be an unordered map containing {path} and
602
- {parentDeferUsage}.
603
- - Otherwise, let {fragmentDeferUsage} be {deferUsage}.
604
- - Let {fragmentGroupedFieldSet} and {fragmentNewDeferUsages} be the result
605
- of calling {CollectFields(objectType, fragmentSelectionSet,
606
- variableValues, fragmentDeferUsage, visitedFragments)}.
607
- - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
608
- - Let {responseKey} be the response key shared by all fields in
609
- {fragmentGroup}.
610
- - Let {groupForResponseKey} be the list in {groupedFields} for
611
- {responseKey}; if no such list exists, create it as an empty list.
612
- - Append all items in {fragmentGroup} to {groupForResponseKey}.
613
- - Append all items in {fragmentNewDeferUsages} to {newDeferUsages}.
614
- - Return {groupedFields} and {newDeferUsages}.
615
-
616
- DoesFragmentTypeApply(objectType, fragmentType):
617
-
618
- - If {fragmentType} is an Object Type:
619
- - If {objectType} and {fragmentType} are the same type, return {true},
620
- otherwise return {false}.
621
- - If {fragmentType} is an Interface Type:
622
- - If {objectType} is an implementation of {fragmentType}, return {true}
623
- otherwise return {false}.
624
- - If {fragmentType} is a Union:
625
- - If {objectType} is a possible type of {fragmentType}, return {true}
626
- otherwise return {false}.
627
-
628
- Note: The steps in {CollectFields()} evaluating the ` @skip ` and ` @include `
629
- directives may be applied in either order since they apply commutatively.
630
-
631
- ### Field Plan Generation
632
-
633
- BuildFieldPlan(originalGroupedFieldSet, parentDeferUsages):
634
-
635
- - If {parentDeferUsages} is not provided, initialize it to the empty set.
636
- - Initialize {groupedFieldSet} to an empty ordered map.
637
- - Initialize {newGroupedFieldSets} to an empty unordered map.
638
- - Let {fieldPlan} be an unordered map containing {groupedFieldSet} and
639
- {newGroupedFieldSets}.
640
- - For each {responseKey} and {groupForResponseKey} of {groupedFieldSet}:
641
- - Let {filteredDeferUsageSet} be the result of
642
- {GetFilteredDeferUsageSet(groupForResponseKey)}.
643
- - If {filteredDeferUsageSet} is the equivalent set to {parentDeferUsages}:
644
- - Set the entry for {responseKey} in {groupedFieldSet} to
645
- {groupForResponseKey}.
646
- - Otherwise:
647
- - Let {newGroupedFieldSet} be the entry in {newGroupedFieldSets} for any
648
- equivalent set to {deferUsageSet}; if no such map exists, create it as an
649
- empty ordered map.
650
- - Set the entry for {responseKey} in {newGroupedFieldSet} to
651
- {groupForResponseKey}.
652
- - Return {fieldPlan}.
653
-
654
- GetFilteredDeferUsageSet(fieldGroup):
655
-
656
- - Initialize {filteredDeferUsageSet} to the empty set.
657
- - For each {fieldDetails} of {fieldGroup}:
658
- - Let {deferUsage} be the corresponding entry on {fieldDetails}.
659
- - If {deferUsage} is not defined:
660
- - Remove all entries from {filteredDeferUsageSet}.
661
- - Return {filteredDeferUsageSet}.
662
- - Add {deferUsage} to {filteredDeferUsageSet}.
663
- - For each {deferUsage} in {filteredDeferUsageSet}:
664
- - Let {parentDeferUsage} be the corresponding entry on {deferUsage}.
665
- - While {parentDeferUsage} is defined:
666
- - If {parentDeferUsage} is contained by {filteredDeferUsageSet}:
667
- - Remove {deferUsage} from {filteredDeferUsageSet}.
668
- - Continue to the next {deferUsage} in {filteredDeferUsageSet}.
669
- - Reset {parentDeferUsage} to the corresponding entry on {parentDeferUsage}.
670
- - Return {filteredDeferUsageSet}.
671
-
672
493
## Executing a Field Plan
673
494
674
495
To execute a field plan, the object value being evaluated and the object type
@@ -881,6 +702,185 @@ A correct executor must generate the following result for that _selection set_:
881
702
}
882
703
```
883
704
705
+ ### Field Collection
706
+
707
+ Before execution, the _ selection set_ is converted to a grouped field set by
708
+ calling {CollectFields()}. Each entry in the grouped field set is a list of
709
+ fields that share a response key (the alias if defined, otherwise the field
710
+ name). This ensures all fields with the same response key (including those in
711
+ referenced fragments) are executed at the same time.
712
+
713
+ As an example, collecting the fields of this selection set would collect two
714
+ instances of the field ` a ` and one of field ` b ` :
715
+
716
+ ``` graphql example
717
+ {
718
+ a {
719
+ subfield1
720
+ }
721
+ ... ExampleFragment
722
+ }
723
+
724
+ fragment ExampleFragment on Query {
725
+ a {
726
+ subfield2
727
+ }
728
+ b
729
+ }
730
+ ```
731
+
732
+ The depth-first-search order of the field groups produced by {CollectFields()}
733
+ is maintained through execution, ensuring that fields appear in the executed
734
+ response in a stable and predictable order.
735
+
736
+ CollectFields(objectType, selectionSet, variableValues, deferUsage,
737
+ visitedFragments):
738
+
739
+ - If {visitedFragments} is not provided, initialize it to the empty set.
740
+ - Initialize {groupedFields} to an empty ordered map of lists.
741
+ - Initialize {newDeferUsages} to an empty list.
742
+ - For each {selection} in {selectionSet}:
743
+ - If {selection} provides the directive ` @skip ` , let {skipDirective} be that
744
+ directive.
745
+ - If {skipDirective}'s {if} argument is {true} or is a variable in
746
+ {variableValues} with the value {true}, continue with the next {selection}
747
+ in {selectionSet}.
748
+ - If {selection} provides the directive ` @include ` , let {includeDirective} be
749
+ that directive.
750
+ - If {includeDirective}'s {if} argument is not {true} and is not a variable
751
+ in {variableValues} with the value {true}, continue with the next
752
+ {selection} in {selectionSet}.
753
+ - If {selection} is a {Field}:
754
+ - Let {responseKey} be the response key of {selection} (the alias if
755
+ defined, otherwise the field name).
756
+ - Let {fieldDetails} be a new unordered map containing {deferUsage}.
757
+ - Set the entry for {field} on {fieldDetails} to {selection}. and
758
+ {deferUsage}.
759
+ - Let {groupForResponseKey} be the list in {groupedFields} for
760
+ {responseKey}; if no such list exists, create it as an empty list.
761
+ - Append {fieldDetails} to the {groupForResponseKey}.
762
+ - If {selection} is a {FragmentSpread}:
763
+ - Let {fragmentSpreadName} be the name of {selection}.
764
+ - If {fragmentSpreadName} provides the directive ` @defer ` and its {if}
765
+ argument is not {false} and is not a variable in {variableValues} with the
766
+ value {false}:
767
+ - Let {deferDirective} be that directive.
768
+ - If this execution is for a subscription operation, raise a _ field
769
+ error_ .
770
+ - If {deferDirective} is not defined:
771
+ - If {fragmentSpreadName} is in {visitedFragments}, continue with the next
772
+ {selection} in {selectionSet}.
773
+ - Add {fragmentSpreadName} to {visitedFragments}.
774
+ - Let {fragment} be the Fragment in the current Document whose name is
775
+ {fragmentSpreadName}.
776
+ - If no such {fragment} exists, continue with the next {selection} in
777
+ {selectionSet}.
778
+ - Let {fragmentType} be the type condition on {fragment}.
779
+ - If {DoesFragmentTypeApply(objectType, fragmentType)} is {false}, continue
780
+ with the next {selection} in {selectionSet}.
781
+ - Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
782
+ - If {deferDirective} is defined:
783
+ - Let {path} be the corresponding entry on {deferDirective}.
784
+ - Let {parentDeferUsage} be {deferUsage}.
785
+ - Let {fragmentDeferUsage} be an unordered map containing {path} and
786
+ {parentDeferUsage}.
787
+ - Otherwise, let {fragmentDeferUsage} be {deferUsage}.
788
+ - Let {fragmentGroupedFieldSet} and {fragmentNewDeferUsages} be the result
789
+ of calling {CollectFields(objectType, fragmentSelectionSet,
790
+ variableValues, fragmentDeferUsage, visitedFragments)}.
791
+ - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
792
+ - Let {responseKey} be the response key shared by all fields in
793
+ {fragmentGroup}.
794
+ - Let {groupForResponseKey} be the list in {groupedFields} for
795
+ {responseKey}; if no such list exists, create it as an empty list.
796
+ - Append all items in {fragmentGroup} to {groupForResponseKey}.
797
+ - Append all items in {fragmentNewDeferUsages} to {newDeferUsages}.
798
+ - If {selection} is an {InlineFragment}:
799
+ - Let {fragmentType} be the type condition on {selection}.
800
+ - If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
801
+ fragmentType)} is {false}, continue with the next {selection} in
802
+ {selectionSet}.
803
+ - Let {fragmentSelectionSet} be the top-level selection set of {selection}.
804
+ - If {InlineFragment} provides the directive ` @defer ` and its {if} argument
805
+ is not {false} and is not a variable in {variableValues} with the value
806
+ {false}:
807
+ - Let {deferDirective} be that directive.
808
+ - If this execution is for a subscription operation, raise a _ field
809
+ error_ .
810
+ - If {deferDirective} is defined:
811
+ - Let {path} be the corresponding entry on {deferDirective}.
812
+ - Let {parentDeferUsage} be {deferUsage}.
813
+ - Let {fragmentDeferUsage} be an unordered map containing {path} and
814
+ {parentDeferUsage}.
815
+ - Otherwise, let {fragmentDeferUsage} be {deferUsage}.
816
+ - Let {fragmentGroupedFieldSet} and {fragmentNewDeferUsages} be the result
817
+ of calling {CollectFields(objectType, fragmentSelectionSet,
818
+ variableValues, fragmentDeferUsage, visitedFragments)}.
819
+ - For each {fragmentGroup} in {fragmentGroupedFieldSet}:
820
+ - Let {responseKey} be the response key shared by all fields in
821
+ {fragmentGroup}.
822
+ - Let {groupForResponseKey} be the list in {groupedFields} for
823
+ {responseKey}; if no such list exists, create it as an empty list.
824
+ - Append all items in {fragmentGroup} to {groupForResponseKey}.
825
+ - Append all items in {fragmentNewDeferUsages} to {newDeferUsages}.
826
+ - Return {groupedFields} and {newDeferUsages}.
827
+
828
+ DoesFragmentTypeApply(objectType, fragmentType):
829
+
830
+ - If {fragmentType} is an Object Type:
831
+ - If {objectType} and {fragmentType} are the same type, return {true},
832
+ otherwise return {false}.
833
+ - If {fragmentType} is an Interface Type:
834
+ - If {objectType} is an implementation of {fragmentType}, return {true}
835
+ otherwise return {false}.
836
+ - If {fragmentType} is a Union:
837
+ - If {objectType} is a possible type of {fragmentType}, return {true}
838
+ otherwise return {false}.
839
+
840
+ Note: The steps in {CollectFields()} evaluating the ` @skip ` and ` @include `
841
+ directives may be applied in either order since they apply commutatively.
842
+
843
+ ### Field Plan Generation
844
+
845
+ BuildFieldPlan(originalGroupedFieldSet, parentDeferUsages):
846
+
847
+ - If {parentDeferUsages} is not provided, initialize it to the empty set.
848
+ - Initialize {groupedFieldSet} to an empty ordered map.
849
+ - Initialize {newGroupedFieldSets} to an empty unordered map.
850
+ - Let {fieldPlan} be an unordered map containing {groupedFieldSet} and
851
+ {newGroupedFieldSets}.
852
+ - For each {responseKey} and {groupForResponseKey} of {groupedFieldSet}:
853
+ - Let {filteredDeferUsageSet} be the result of
854
+ {GetFilteredDeferUsageSet(groupForResponseKey)}.
855
+ - If {filteredDeferUsageSet} is the equivalent set to {parentDeferUsages}:
856
+ - Set the entry for {responseKey} in {groupedFieldSet} to
857
+ {groupForResponseKey}.
858
+ - Otherwise:
859
+ - Let {newGroupedFieldSet} be the entry in {newGroupedFieldSets} for any
860
+ equivalent set to {deferUsageSet}; if no such map exists, create it as an
861
+ empty ordered map.
862
+ - Set the entry for {responseKey} in {newGroupedFieldSet} to
863
+ {groupForResponseKey}.
864
+ - Return {fieldPlan}.
865
+
866
+ GetFilteredDeferUsageSet(fieldGroup):
867
+
868
+ - Initialize {filteredDeferUsageSet} to the empty set.
869
+ - For each {fieldDetails} of {fieldGroup}:
870
+ - Let {deferUsage} be the corresponding entry on {fieldDetails}.
871
+ - If {deferUsage} is not defined:
872
+ - Remove all entries from {filteredDeferUsageSet}.
873
+ - Return {filteredDeferUsageSet}.
874
+ - Add {deferUsage} to {filteredDeferUsageSet}.
875
+ - For each {deferUsage} in {filteredDeferUsageSet}:
876
+ - Let {parentDeferUsage} be the corresponding entry on {deferUsage}.
877
+ - While {parentDeferUsage} is defined:
878
+ - If {parentDeferUsage} is contained by {filteredDeferUsageSet}:
879
+ - Remove {deferUsage} from {filteredDeferUsageSet}.
880
+ - Continue to the next {deferUsage} in {filteredDeferUsageSet}.
881
+ - Reset {parentDeferUsage} to the corresponding entry on {parentDeferUsage}.
882
+ - Return {filteredDeferUsageSet}.
883
+
884
884
## Executing Fields
885
885
886
886
Each field requested in the grouped field set that is defined on the selected
0 commit comments