Skip to content

Commit b94f733

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)
1 parent 45ffddb commit b94f733

File tree

2 files changed

+64
-35
lines changed

2 files changed

+64
-35
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: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ ExecuteQuery(query, schema, variableValues, initialValue):
131131
- Let {queryType} be the root Query type in {schema}.
132132
- Assert: {queryType} is an Object type.
133133
- Let {selectionSet} be the top level Selection Set in {query}.
134-
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
134+
- Let {groupedFieldSet} be the result of {CollectRootFields(queryType,
135+
selectionSet, variableValues)}.
136+
- Let {data} be the result of running {ExecuteGroupedFieldSet(groupedFieldSet,
135137
queryType, initialValue, variableValues)} _normally_ (allowing
136138
parallelization).
137139
- Let {errors} be the list of all _field error_ raised while executing the
@@ -153,7 +155,9 @@ ExecuteMutation(mutation, schema, variableValues, initialValue):
153155
- Let {mutationType} be the root Mutation type in {schema}.
154156
- Assert: {mutationType} is an Object type.
155157
- Let {selectionSet} be the top level Selection Set in {mutation}.
156-
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
158+
- Let {groupedFieldSet} be the result of {CollectRootFields(mutationType,
159+
selectionSet, variableValues)}.
160+
- Let {data} be the result of running {ExecuteGroupedFieldSet(groupedFieldSet,
157161
mutationType, initialValue, variableValues)} _serially_.
158162
- Let {errors} be the list of all _field error_ raised while executing the
159163
selection set.
@@ -256,7 +260,7 @@ CreateSourceEventStream(subscription, schema, variableValues, initialValue):
256260
- Let {subscriptionType} be the root Subscription type in {schema}.
257261
- Assert: {subscriptionType} is an Object type.
258262
- Let {selectionSet} be the top level Selection Set in {subscription}.
259-
- Let {groupedFieldSet} be the result of {CollectFields(subscriptionType,
263+
- Let {groupedFieldSet} be the result of {CollectRootFields(subscriptionType,
260264
selectionSet, variableValues)}.
261265
- If {groupedFieldSet} does not have exactly one entry, raise a _request error_.
262266
- Let {fields} be the value of the first entry in {groupedFieldSet}.
@@ -301,7 +305,9 @@ ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue):
301305
- Let {subscriptionType} be the root Subscription type in {schema}.
302306
- Assert: {subscriptionType} is an Object type.
303307
- Let {selectionSet} be the top level Selection Set in {subscription}.
304-
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
308+
- Let {groupedFieldSet} be the result of {CollectRootFields(subscriptionType,
309+
selectionSet, variableValues)}.
310+
- Let {data} be the result of running {ExecuteGroupedFieldSet(groupedFieldSet,
305311
subscriptionType, initialValue, variableValues)} _normally_ (allowing
306312
parallelization).
307313
- Let {errors} be the list of all _field error_ raised while executing the
@@ -322,20 +328,18 @@ Unsubscribe(responseStream):
322328

323329
- Cancel {responseStream}
324330

325-
## Executing Selection Sets
331+
## Executing Grouped Field Sets
326332

327-
To execute a selection set, the object value being evaluated and the object type
328-
need to be known, as well as whether it must be executed serially, or may be
329-
executed in parallel.
333+
To execute a grouped field set, the object value being evaluated and the object
334+
type need to be known, as well as whether it must be executed serially, or may
335+
be executed in parallel.
330336

331-
First, the selection set is turned into a grouped field set; then, each
332-
represented field in the grouped field set produces an entry into a response
333-
map.
337+
Each represented field in the grouped field set produces an entry into a
338+
response map.
334339

335-
ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues):
340+
ExecuteGroupedFieldSet(groupedFieldSet, objectType, objectValue,
341+
variableValues):
336342

337-
- Let {groupedFieldSet} be the result of {CollectFields(objectType,
338-
selectionSet, variableValues)}.
339343
- Initialize {resultMap} to an empty ordered map.
340344
- For each {groupedFieldSet} as {responseKey} and {fields}:
341345
- Let {fieldName} be the name of the first entry in {fields}. Note: This value
@@ -492,8 +496,7 @@ response in a stable and predictable order.
492496

493497
CollectFields(objectType, selectionSet, variableValues, visitedFragments):
494498

495-
- If {visitedFragments} is not provided, initialize it to the empty set.
496-
- Initialize {groupedFields} to an empty ordered map of lists.
499+
- Initialize {groupedFieldSet} to an empty ordered map of lists.
497500
- For each {selection} in {selectionSet}:
498501
- If {selection} provides the directive `@skip`, let {skipDirective} be that
499502
directive.
@@ -508,7 +511,7 @@ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
508511
- If {selection} is a {Field}:
509512
- Let {responseKey} be the response key of {selection} (the alias if
510513
defined, otherwise the field name).
511-
- Let {groupForResponseKey} be the list in {groupedFields} for
514+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
512515
{responseKey}; if no such list exists, create it as an empty list.
513516
- Append {selection} to the {groupForResponseKey}.
514517
- If {selection} is a {FragmentSpread}:
@@ -525,12 +528,12 @@ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
525528
with the next {selection} in {selectionSet}.
526529
- Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
527530
- Let {fragmentGroupedFieldSet} be the result of calling
528-
{CollectFields(objectType, fragmentSelectionSet, variableValues,
531+
{CollectFields(objectType, fieldSelectionSet, variableValues,
529532
visitedFragments)}.
530533
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
531534
- Let {responseKey} be the response key shared by all fields in
532535
{fragmentGroup}.
533-
- Let {groupForResponseKey} be the list in {groupedFields} for
536+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
534537
{responseKey}; if no such list exists, create it as an empty list.
535538
- Append all items in {fragmentGroup} to {groupForResponseKey}.
536539
- If {selection} is an {InlineFragment}:
@@ -545,10 +548,10 @@ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
545548
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
546549
- Let {responseKey} be the response key shared by all fields in
547550
{fragmentGroup}.
548-
- Let {groupForResponseKey} be the list in {groupedFields} for
551+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
549552
{responseKey}; if no such list exists, create it as an empty list.
550553
- Append all items in {fragmentGroup} to {groupForResponseKey}.
551-
- Return {groupedFields}.
554+
- Return {groupedFields} and {visitedFragments}.
552555

553556
DoesFragmentTypeApply(objectType, fragmentType):
554557

@@ -565,6 +568,39 @@ DoesFragmentTypeApply(objectType, fragmentType):
565568
Note: The steps in {CollectFields()} evaluating the `@skip` and `@include`
566569
directives may be applied in either order since they apply commutatively.
567570

571+
### Root Field Collection
572+
573+
Root field collection processes the operation's top-level selection set:
574+
575+
CollectRootFields(rootType, operationSelectionSet, variableValues):
576+
577+
- Initialize {visitedFragments} to the empty set.
578+
- Let {groupedFieldSet} be the result of calling {CollectFields(rootType,
579+
operationSelectionSet, variableValues, visitedFragments)}.
580+
- Return {groupedFieldSet}.
581+
582+
### Object Subfield Collection
583+
584+
Object subfield collection processes a field's sub-selection sets:
585+
586+
CollectSubfields(objectType, fields, variableValues):
587+
588+
- Initialize {visitedFragments} to the empty set.
589+
- Initialize {groupedSubfieldSet} to an empty ordered map of lists.
590+
- For each {field} in {fields}:
591+
- Let {fieldSelectionSet} be the selection set of {field}.
592+
- If {fieldSelectionSet} is null or empty, continue to the next field.
593+
- Let {fieldGroupedFieldSet} be the result of calling
594+
{CollectFields(objectType, fragmentSelectionSet, variableValues,
595+
visitedFragments)}.
596+
- For each {fieldGroup} in {fieldGroupedFieldSet}:
597+
- Let {responseKey} be the response key shared by all fields in
598+
{fragmentGroup}.
599+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
600+
{responseKey}; if no such list exists, create it as an empty list.
601+
- Append all items in {fieldGroup} to {groupForResponseKey}.
602+
- Return {groupedSubfieldSet}.
603+
568604
## Executing Fields
569605

570606
Each field requested in the grouped field set that is defined on the selected
@@ -692,8 +728,9 @@ CompleteValue(fieldType, fields, result, variableValues):
692728
- Let {objectType} be {fieldType}.
693729
- Otherwise if {fieldType} is an Interface or Union type.
694730
- Let {objectType} be {ResolveAbstractType(fieldType, result)}.
695-
- Let {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}.
696-
- Return the result of evaluating {ExecuteSelectionSet(subSelectionSet,
731+
- Let {groupedSubfieldSet} be the result of calling
732+
{CollectSubfields(objectType, fields, variableValues)}.
733+
- Return the result of evaluating {ExecuteGroupedFieldSet(groupedSubfieldSet,
697734
objectType, result, variableValues)} _normally_ (allowing for
698735
parallelization).
699736

@@ -758,17 +795,9 @@ sub-selections.
758795
}
759796
```
760797

761-
After resolving the value for `me`, the selection sets are merged together so
762-
`firstName` and `lastName` can be resolved for one value.
763-
764-
MergeSelectionSets(fields):
765-
766-
- Let {selectionSet} be an empty list.
767-
- For each {field} in {fields}:
768-
- Let {fieldSelectionSet} be the selection set of {field}.
769-
- If {fieldSelectionSet} is null or empty, continue to the next field.
770-
- Append all selections in {fieldSelectionSet} to {selectionSet}.
771-
- Return {selectionSet}.
798+
After resolving the value for `me`, the selection sets are merged together by
799+
calling {CollectSubfields()} so `firstName` and `lastName` can be resolved for
800+
one value.
772801

773802
### Handling Field Errors
774803

0 commit comments

Comments
 (0)