Skip to content

Commit a2516e2

Browse files
committed
move Field Collection back to where it was
mostly to reduce the diff.
1 parent db54ad8 commit a2516e2

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
@@ -490,185 +490,6 @@ BatchIncrementalResults(incrementalResults):
490490
of {hasNext} on the final item in the list.
491491
- Yield {batchedIncrementalResult}.
492492

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-
672493
## Executing a Field Plan
673494

674495
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_:
881702
}
882703
```
883704

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+
884884
## Executing Fields
885885

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

0 commit comments

Comments
 (0)