Skip to content

Commit 1645856

Browse files
committed
move Field Collection back to where it was
mostly to reduce the diff.
1 parent 14b107f commit 1645856

File tree

1 file changed

+179
-179
lines changed

1 file changed

+179
-179
lines changed

spec/Section 6 -- Execution.md

Lines changed: 179 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -493,185 +493,6 @@ BatchIncrementalResults(incrementalResults):
493493
of {hasNext} on the final item in the list.
494494
- Yield {batchedIncrementalResult}.
495495

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-
675496
## Executing a Field Plan
676497

677498
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_:
884705
}
885706
```
886707

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+
887887
## Executing Fields
888888

889889
Each field requested in the grouped field set that is defined on the selected

0 commit comments

Comments
 (0)