Skip to content

Commit 16171e9

Browse files
committed
introduce CollectRootFields and CollectSubfields
Rather than merging subSelectionSets of a field set using MergeSelectionSets and then calling CollectFields, introducing CollectSubfields allows the field set's groupedSubfieldSet to be calculated directly. This may be helpful if the specification were ever to be altered such that additional state beyond the current selection set were to be required to calculate the response, i.e. if it were to be required to know the originating selectionSet of a given field within the fieldSet for determining when to communicate a reference signal. See #998 (comment) # Conflicts: # spec/Section 6 -- Execution.md
1 parent 1b56d86 commit 16171e9

File tree

2 files changed

+68
-38
lines changed

2 files changed

+68
-38
lines changed

spec/Section 5 -- Validation.md

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

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

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

spec/Section 6 -- Execution.md

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,11 @@ ExecuteQuery(query, schema, variableValues, initialValue):
132132
- Let {queryType} be the root Query type in {schema}.
133133
- Assert: {queryType} is an Object type.
134134
- Let {selectionSet} be the top level Selection Set in {query}.
135+
- Let {groupedFieldSet} be the result of {CollectRootFields(queryType,
136+
selectionSet, variableValues)}.
135137
- Let {data}, {defers} and {streams} be the result of running
136-
{ExecuteSelectionSet(selectionSet, queryType, initialValue, variableValues)}
137-
_normally_ (allowing parallelization).
138+
{ExecuteGroupedFieldSet(groupedFieldSet, queryType, initialValue,
139+
variableValues)} _normally_ (allowing parallelization).
138140
- Let {errors} be the list of all _field error_ raised while executing the
139141
selection set.
140142
- If {defers} is an empty map and {streams} is an empty list:
@@ -219,7 +221,7 @@ variableValues):
219221
entries of {fieldDetails}.
220222
- Let {selectionSet} be a new selection set consisting of {fields}.
221223
- Let {data}, {childDefers} and {childStreams} be the result of running
222-
{ExecuteSelectionSet(selectionSet, objectType, objectValue,
224+
{ExecuteGroupedFieldSet(selectionSet, objectType, objectValue,
223225
variableValues, path)}.
224226
- Let {childErrors} be the list of all _field error_ raised while
225227
executing the selection set.
@@ -336,8 +338,10 @@ ExecuteMutation(mutation, schema, variableValues, initialValue):
336338
- Let {mutationType} be the root Mutation type in {schema}.
337339
- Assert: {mutationType} is an Object type.
338340
- Let {selectionSet} be the top level Selection Set in {mutation}.
341+
- Let {groupedFieldSet} be the result of {CollectRootFields(mutationType,
342+
selectionSet, variableValues)}.
339343
- Let {data}, {defers} and {streams} be the result of running
340-
{ExecuteSelectionSet(selectionSet, mutationType, initialValue,
344+
{ExecuteGroupedFieldSet(groupedFieldSet, mutationType, initialValue,
341345
variableValues)} _serially_.
342346
- Let {errors} be the list of all _field error_ raised while executing the
343347
selection set.
@@ -446,8 +450,8 @@ CreateSourceEventStream(subscription, schema, variableValues, initialValue):
446450
- Let {subscriptionType} be the root Subscription type in {schema}.
447451
- Assert: {subscriptionType} is an Object type.
448452
- Let {selectionSet} be the top level Selection Set in {subscription}.
449-
- Let {groupedFieldSet} be the result of running
450-
{CollectFields(subscriptionType, selectionSet, variableValues)}.
453+
- Let {groupedFieldSet} be the result of {CollectRootFields(subscriptionType,
454+
selectionSet, variableValues)}.
451455
- If {groupedFieldSet} does not have exactly one entry, raise a _request error_.
452456
- Let {fieldDetails} be the value of the first entry in {groupedFieldSet}.
453457
- Let {fieldDetail} be the first entry in {fieldDetails}.
@@ -496,8 +500,10 @@ ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue):
496500
- Let {subscriptionType} be the root Subscription type in {schema}.
497501
- Assert: {subscriptionType} is an Object type.
498502
- Let {selectionSet} be the top level Selection Set in {subscription}.
503+
- Let {groupedFieldSet} be the result of {CollectRootFields(subscriptionType,
504+
selectionSet, variableValues)}.
499505
- Let {data}, {defers} and {streams} be the result of running
500-
{ExecuteSelectionSet(selectionSet, subscriptionType, initialValue,
506+
{ExecuteGroupedFieldSet(groupedFieldSet, subscriptionType, initialValue,
501507
variableValues)} _normally_ (allowing parallelization).
502508
- Let {errors} be the list of all _field error_ raised while executing the
503509
selection set.
@@ -523,22 +529,19 @@ Unsubscribe(responseStream):
523529

524530
- Cancel {responseStream}
525531

526-
## Executing Selection Sets
532+
## Executing Grouped Field Sets
527533

528-
To execute a selection set, the object value being evaluated and the object type
529-
need to be known, as well as whether it must be executed serially, or may be
530-
executed in parallel.
534+
To execute a grouped field set, the object value being evaluated and the object
535+
type need to be known, as well as whether it must be executed serially, or may
536+
be executed in parallel.
531537

532-
First, the selection set is turned into a grouped field set; then, each
533-
represented field in the grouped field set produces an entry into a response
534-
map.
538+
Each represented field in the grouped field set produces an entry into a
539+
response map.
535540

536-
ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues,
541+
ExecuteGroupedFieldSet(groupedFieldSet, objectType, objectValue, variableValues,
537542
parentPath):
538543

539544
- If {parentPath} is not provided, initialize it to an empty list.
540-
- Let {groupedFieldSet} be the result of running {CollectFields(objectType,
541-
selectionSet, variableValues)}.
542545
- Initialize {resultMap} to an empty ordered map.
543546
- Let {defers} be an empty unordered map.
544547
- Let {streams} be an empty list.
@@ -607,8 +610,8 @@ is explained in greater detail in the Field Collection section below.
607610

608611
**Errors and Non-Null Fields**
609612

610-
If during {ExecuteSelectionSet()} a field with a non-null {fieldType} raises a
611-
_field error_ then that error must propagate to this entire selection set,
613+
If during {ExecuteGroupedFieldSet()} a field with a non-null {fieldType} raises
614+
a _field error_ then that error must propagate to this entire selection set,
612615
either resolving to {null} if allowed or further propagated to a parent field.
613616

614617
If this occurs, any sibling fields which have not yet executed or have not yet
@@ -747,6 +750,7 @@ response in a stable and predictable order.
747750
CollectFields(objectType, selectionSet, variableValues, isDeferred,
748751
visitedFragments):
749752

753+
- Initialize {groupedFieldSet} to an empty ordered map of lists.
750754
- If {isDeferred} is not provided, initialize it to {false}.
751755
- If {visitedFragments} is not provided, initialize it to the empty set.
752756
- Initialize {groupedFields} to an empty ordered map of lists.
@@ -765,7 +769,7 @@ visitedFragments):
765769
- Let {field} be {selection}.
766770
- Let {responseKey} be the response key of {field} (the alias if defined,
767771
otherwise the field name).
768-
- Let {groupForResponseKey} be the list in {groupedFields} for
772+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
769773
{responseKey}; if no such list exists, create it as an empty list.
770774
- Let {fieldDetail} be an unordered map containing {field} and {isDeferred}.
771775
- Append {fieldDetail} to the {groupForResponseKey}.
@@ -794,7 +798,7 @@ visitedFragments):
794798
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
795799
- Let {responseKey} be the response key shared by all fields in
796800
{fragmentGroup}.
797-
- Let {groupForResponseKey} be the list in {groupedFields} for
801+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
798802
{responseKey}; if no such list exists, create it as an empty list.
799803
- Append all items in {fragmentGroup} to {groupForResponseKey}.
800804
- If {selection} is an {InlineFragment}:
@@ -815,10 +819,10 @@ visitedFragments):
815819
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
816820
- Let {responseKey} be the response key shared by all fields in
817821
{fragmentGroup}.
818-
- Let {groupForResponseKey} be the list in {groupedFields} for
822+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
819823
{responseKey}; if no such list exists, create it as an empty list.
820824
- Append all items in {fragmentGroup} to {groupForResponseKey}.
821-
- Return {groupedFields}.
825+
- Return {groupedFields} and {visitedFragments}.
822826

823827
DoesFragmentTypeApply(objectType, fragmentType):
824828

@@ -835,6 +839,39 @@ DoesFragmentTypeApply(objectType, fragmentType):
835839
Note: The steps in {CollectFields()} evaluating the `@skip` and `@include`
836840
directives may be applied in either order since they apply commutatively.
837841

842+
### Root Field Collection
843+
844+
Root field collection processes the operation's top-level selection set:
845+
846+
CollectRootFields(rootType, operationSelectionSet, variableValues):
847+
848+
- Initialize {visitedFragments} to the empty set.
849+
- Let {groupedFieldSet} be the result of calling {CollectFields(rootType,
850+
operationSelectionSet, variableValues, visitedFragments)}.
851+
- Return {groupedFieldSet}.
852+
853+
### Object Subfield Collection
854+
855+
Object subfield collection processes a field's sub-selection sets:
856+
857+
CollectSubfields(objectType, fields, variableValues):
858+
859+
- Initialize {visitedFragments} to the empty set.
860+
- Initialize {groupedSubfieldSet} to an empty ordered map of lists.
861+
- For each {field} in {fields}:
862+
- Let {fieldSelectionSet} be the selection set of {field}.
863+
- If {fieldSelectionSet} is null or empty, continue to the next field.
864+
- Let {fieldGroupedFieldSet} be the result of calling
865+
{CollectFields(objectType, fragmentSelectionSet, variableValues,
866+
visitedFragments)}.
867+
- For each {fieldGroup} in {fieldGroupedFieldSet}:
868+
- Let {responseKey} be the response key shared by all fields in
869+
{fragmentGroup}.
870+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
871+
{responseKey}; if no such list exists, create it as an empty list.
872+
- Append all items in {fieldGroup} to {groupForResponseKey}.
873+
- Return {groupedSubfieldSet}.
874+
838875
## Executing Fields
839876

840877
Each field requested in the grouped field set that is defined on the selected
@@ -980,10 +1017,11 @@ CompleteValue(fieldType, fields, result, variableValues, path):
9801017
- Otherwise if {fieldType} is an Interface or Union type.
9811018
- Let {objectType} be the result of running {ResolveAbstractType(fieldType,
9821019
result)}.
983-
- Let {subSelectionSet} be the result of running {MergeSelectionSets(fields)}.
1020+
- Let {groupedSubfieldSet} be the result of calling
1021+
{CollectSubfields(objectType, fields, variableValues)}.
9841022
- Let {completedResult}, {defers} and {streams} be the result of running
985-
{ExecuteSelectionSet(subSelectionSet, objectType, result, variableValues,
986-
path)} _normally_ (allowing for parallelization).
1023+
{ExecuteGroupedFieldSet(groupedSubfieldSet, objectType, result,
1024+
variableValues, path)} _normally_ (allowing for parallelization).
9871025
- Return {completedResult}, {defers} and {streams}.
9881026

9891027
**Coercing Results**
@@ -1047,17 +1085,9 @@ sub-selections.
10471085
}
10481086
```
10491087

1050-
After resolving the value for `me`, the selection sets are merged together so
1051-
`firstName` and `lastName` can be resolved for one value.
1052-
1053-
MergeSelectionSets(fields):
1054-
1055-
- Let {selectionSet} be an empty list.
1056-
- For each {field} in {fields}:
1057-
- Let {fieldSelectionSet} be the selection set of {field}.
1058-
- If {fieldSelectionSet} is null or empty, continue to the next field.
1059-
- Append all selections in {fieldSelectionSet} to {selectionSet}.
1060-
- Return {selectionSet}.
1088+
After resolving the value for `me`, the selection sets are merged together by
1089+
calling {CollectSubfields()} so `firstName` and `lastName` can be resolved for
1090+
one value.
10611091

10621092
### Handling Field Errors
10631093

0 commit comments

Comments
 (0)