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