Skip to content

Commit 1a4164c

Browse files
committed
introduce CollectRootFields and CollectSubfields
Rather than merging subSelectionSets of a field group using MergeSelectionSets and then calling CollectFields, introducing CollectSubfields allows the field group's groupedSubfieldSet to be calculated directly.
1 parent 8df13da commit 1a4164c

File tree

2 files changed

+63
-35
lines changed

2 files changed

+63
-35
lines changed

spec/Section 5 -- Validation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ unambiguous. Therefore any two field selections which might both be encountered
474474
for the same object are only valid if they are equivalent.
475475

476476
During execution, the simultaneous execution of fields with the same response
477-
name is accomplished by {MergeSelectionSets()} and {CollectFields()}.
477+
name is accomplished by {CollectFields()}.
478478

479479
For simple hand-written GraphQL, this rule is obviously a clear developer error,
480480
however nested fragments can make this difficult to detect manually.

spec/Section 6 -- Execution.md

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ ExecuteQuery(query, schema, variableValues, initialValue):
136136
- Let {queryType} be the root Query type in {schema}.
137137
- Assert: {queryType} is an Object type.
138138
- Let {selectionSet} be the top level Selection Set in {query}.
139-
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
139+
- Let {groupedFieldSet} and {deferredGroupedFieldsList} be the result of
140+
{CollectRootFields(queryType, selectionSet, variableValues)}.
141+
- Let {data} be the result of running {ExecuteGroupedFieldSet(groupedFieldSet,
140142
queryType, initialValue, variableValues, subsequentPayloads)} _normally_
141143
(allowing parallelization).
142144
- Let {errors} be the list of all _field error_ raised while executing the
@@ -171,7 +173,9 @@ ExecuteMutation(mutation, schema, variableValues, initialValue):
171173
- Let {mutationType} be the root Mutation type in {schema}.
172174
- Assert: {mutationType} is an Object type.
173175
- Let {selectionSet} be the top level Selection Set in {mutation}.
174-
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
176+
- Let {groupedFieldSet} and {deferredGroupedFieldsList} be the result of
177+
{CollectRootFields(queryType, selectionSet, variableValues)}.
178+
- Let {data} be the result of running {ExecuteGroupedFieldSet(groupedFieldSet,
175179
mutationType, initialValue, variableValues, subsequentPayloads)} _serially_.
176180
- Let {errors} be the list of all _field error_ raised while executing the
177181
selection set.
@@ -286,7 +290,7 @@ CreateSourceEventStream(subscription, schema, variableValues, initialValue):
286290
- Let {subscriptionType} be the root Subscription type in {schema}.
287291
- Assert: {subscriptionType} is an Object type.
288292
- Let {selectionSet} be the top level Selection Set in {subscription}.
289-
- Let {groupedFieldSet} be the result of {CollectFields(subscriptionType,
293+
- Let {groupedFieldSet} be the result of {CollectRootFields(subscriptionType,
290294
selectionSet, variableValues)}.
291295
- If {groupedFieldSet} does not have exactly one entry, raise a _request error_.
292296
- Let {fields} be the value of the first entry in {groupedFieldSet}.
@@ -332,7 +336,9 @@ ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue):
332336
- Let {subscriptionType} be the root Subscription type in {schema}.
333337
- Assert: {subscriptionType} is an Object type.
334338
- Let {selectionSet} be the top level Selection Set in {subscription}.
335-
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
339+
- Let {groupedFieldSet} be the result of {CollectRootFields(subscriptionType,
340+
selectionSet, variableValues)}.
341+
- Let {data} be the result of running {ExecuteGroupedFieldSet(groupedFieldSet,
336342
subscriptionType, initialValue, variableValues)} _normally_ (allowing
337343
parallelization).
338344
- Let {errors} be the list of all _field error_ raised while executing the
@@ -400,23 +406,20 @@ YieldSubsequentPayloads(initialResponse, subsequentPayloads):
400406
{true}.
401407
- Yield {subsequentResponse}
402408

403-
## Executing Selection Sets
409+
## Executing Grouped Field Sets
404410

405-
To execute a selection set, the object value being evaluated and the object type
406-
need to be known, as well as whether it must be executed serially, or may be
407-
executed in parallel.
411+
To execute a grouped field set, the object value being evaluated and the object
412+
type need to be known, as well as whether it must be executed serially, or may
413+
be executed in parallel.
408414

409-
First, the selection set is turned into a grouped field set; then, each
410-
represented field in the grouped field set produces an entry into a response
411-
map.
415+
Each represented field in the grouped field set produces an entry into a
416+
response map.
412417

413-
ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues, path,
414-
subsequentPayloads, asyncRecord):
418+
ExecuteGroupedFieldSet(groupedFieldSet, deferredGroupedFieldsList, objectType,
419+
objectValue, variableValues, path, subsequentPayloads, asyncRecord):
415420

416421
- If {path} is not provided, initialize it to an empty list.
417422
- If {subsequentPayloads} is not provided, initialize it to the empty set.
418-
- Let {groupedFieldSet} and {deferredGroupedFieldsList} be the result of
419-
{CollectFields(objectType, selectionSet, variableValues, path, asyncRecord)}.
420423
- Initialize {resultMap} to an empty ordered map.
421424
- For each {groupedFieldSet} as {responseKey} and {fields}:
422425
- Let {fieldName} be the name of the first entry in {fields}. Note: This value
@@ -651,8 +654,7 @@ response in a stable and predictable order.
651654
CollectFields(objectType, selectionSet, variableValues, path, asyncRecord,
652655
visitedFragments, deferredGroupedFieldsList):
653656

654-
- If {visitedFragments} is not provided, initialize it to the empty set.
655-
- Initialize {groupedFields} to an empty ordered map of lists.
657+
- Initialize {groupedFieldSet} to an empty ordered map of lists.
656658
- If {deferredGroupedFieldsList} is not provided, initialize it to an empty
657659
list.
658660
- For each {selection} in {selectionSet}:
@@ -669,7 +671,7 @@ visitedFragments, deferredGroupedFieldsList):
669671
- If {selection} is a {Field}:
670672
- Let {responseKey} be the response key of {selection} (the alias if
671673
defined, otherwise the field name).
672-
- Let {groupForResponseKey} be the list in {groupedFields} for
674+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
673675
{responseKey}; if no such list exists, create it as an empty list.
674676
- Append {selection} to the {groupForResponseKey}.
675677
- If {selection} is a {FragmentSpread}:
@@ -707,7 +709,7 @@ visitedFragments, deferredGroupedFieldsList):
707709
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
708710
- Let {responseKey} be the response key shared by all fields in
709711
{fragmentGroup}.
710-
- Let {groupForResponseKey} be the list in {groupedFields} for
712+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
711713
{responseKey}; if no such list exists, create it as an empty list.
712714
- Append all items in {fragmentGroup} to {groupForResponseKey}.
713715
- If {selection} is an {InlineFragment}:
@@ -737,10 +739,10 @@ visitedFragments, deferredGroupedFieldsList):
737739
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
738740
- Let {responseKey} be the response key shared by all fields in
739741
{fragmentGroup}.
740-
- Let {groupForResponseKey} be the list in {groupedFields} for
742+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
741743
{responseKey}; if no such list exists, create it as an empty list.
742744
- Append all items in {fragmentGroup} to {groupForResponseKey}.
743-
- Return {groupedFields} and {deferredGroupedFieldsList}.
745+
- Return {groupedFields}, {deferredGroupedFieldsList}, and {visitedFragments}.
744746

745747
Note: The steps in {CollectFields()} evaluating the `@skip` and `@include`
746748
directives may be applied in either order since they apply commutatively.
@@ -808,6 +810,39 @@ variableValues, parentRecord, subsequentPayloads):
808810
- Set {dataExecution} on {deferredFragmentRecord}.
809811
- Append {deferRecord} to {subsequentPayloads}.
810812

813+
### Root Field Collection
814+
815+
Root field collection processes the operation's top-level selection set:
816+
817+
CollectRootFields(rootType, operationSelectionSet, variableValues):
818+
819+
- Initialize {visitedFragments} to the empty set.
820+
- Let {groupedFieldSet} be the result of calling {CollectFields(rootType,
821+
operationSelectionSet, variableValues, visitedFragments)}.
822+
- Return {groupedFieldSet}.
823+
824+
### Object Subfield Collection
825+
826+
Object subfield collection processes a field's sub-selection sets:
827+
828+
CollectSubfields(objectType, fields, variableValues):
829+
830+
- Initialize {visitedFragments} to the empty set.
831+
- Initialize {groupedSubfieldSet} to an empty ordered map of lists.
832+
- For each {field} in {fields}:
833+
- Let {fieldSelectionSet} be the selection set of {field}.
834+
- If {fieldSelectionSet} is null or empty, continue to the next field.
835+
- Let {fieldGroupedFieldSet} be the result of calling
836+
{CollectFields(objectType, fragmentSelectionSet, variableValues,
837+
visitedFragments)}.
838+
- For each {fieldGroup} in {fieldGroupedFieldSet}:
839+
- Let {responseKey} be the response key shared by all fields in
840+
{fragmentGroup}.
841+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
842+
{responseKey}; if no such list exists, create it as an empty list.
843+
- Append all items in {fieldGroup} to {groupForResponseKey}.
844+
- Return {groupedSubfieldSet}.
845+
811846
## Executing Fields
812847

813848
Each field requested in the grouped field set that is defined on the selected
@@ -1024,8 +1059,9 @@ subsequentPayloads, asyncRecord):
10241059
- Let {objectType} be {fieldType}.
10251060
- Otherwise if {fieldType} is an Interface or Union type.
10261061
- Let {objectType} be {ResolveAbstractType(fieldType, result)}.
1027-
- Let {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}.
1028-
- Return the result of evaluating {ExecuteSelectionSet(subSelectionSet,
1062+
- Let {groupedSubfieldSet} and {deferredGroupedFieldsList} be the result of
1063+
calling {CollectSubfields(objectType, fields, variableValues)}.
1064+
- Return the result of evaluating {ExecuteGroupedFieldSet(groupedSubfieldSet,
10291065
objectType, result, variableValues, path, subsequentPayloads, asyncRecord)}
10301066
_normally_ (allowing for parallelization).
10311067

@@ -1090,17 +1126,9 @@ sub-selections.
10901126
}
10911127
```
10921128

1093-
After resolving the value for `me`, the selection sets are merged together so
1094-
`firstName` and `lastName` can be resolved for one value.
1095-
1096-
MergeSelectionSets(fields):
1097-
1098-
- Let {selectionSet} be an empty list.
1099-
- For each {field} in {fields}:
1100-
- Let {fieldSelectionSet} be the selection set of {field}.
1101-
- If {fieldSelectionSet} is null or empty, continue to the next field.
1102-
- Append all selections in {fieldSelectionSet} to {selectionSet}.
1103-
- Return {selectionSet}.
1129+
After resolving the value for `me`, the selection sets are merged together by
1130+
calling {CollectSubfields()} so `firstName` and `lastName` can be resolved for
1131+
one value.
11041132

11051133
### Handling Field Errors
11061134

0 commit comments

Comments
 (0)