Skip to content

Commit 4fc5b32

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 3aa021f commit 4fc5b32

File tree

2 files changed

+54
-48
lines changed

2 files changed

+54
-48
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: 53 additions & 47 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 {ExecuteFieldSet(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 {ExecuteFieldSet(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 {ExecuteFieldSet(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,17 @@ Unsubscribe(responseStream):
322328

323329
- Cancel {responseStream}
324330

325-
## Executing Selection Sets
331+
## Executing 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+
ExecuteFieldSet(groupedFieldSet, objectType, objectValue, variableValues):
336341

337-
- Let {groupedFieldSet} be the result of {CollectFields(objectType,
338-
selectionSet, variableValues)}.
339342
- Initialize {resultMap} to an empty ordered map.
340343
- For each {groupedFieldSet} as {responseKey} and {fields}:
341344
- Let {fieldName} be the name of the first entry in {fields}. Note: This value
@@ -490,10 +493,12 @@ The depth-first-search order of the field groups produced by {CollectFields()}
490493
is maintained through execution, ensuring that fields appear in the executed
491494
response in a stable and predictable order.
492495

493-
CollectFields(objectType, selectionSet, variableValues, visitedFragments):
496+
CollectFields(objectType, selectionSet, variableValues, groupedFieldSet,
497+
visitedFragments):
494498

499+
- If {groupedFieldSet} is not provided, initialize it an empty ordered map of
500+
lists.
495501
- If {visitedFragments} is not provided, initialize it to the empty set.
496-
- Initialize {groupedFields} to an empty ordered map of lists.
497502
- For each {selection} in {selectionSet}:
498503
- If {selection} provides the directive `@skip`, let {skipDirective} be that
499504
directive.
@@ -508,7 +513,7 @@ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
508513
- If {selection} is a {Field}:
509514
- Let {responseKey} be the response key of {selection} (the alias if
510515
defined, otherwise the field name).
511-
- Let {groupForResponseKey} be the list in {groupedFields} for
516+
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
512517
{responseKey}; if no such list exists, create it as an empty list.
513518
- Append {selection} to the {groupForResponseKey}.
514519
- If {selection} is a {FragmentSpread}:
@@ -524,31 +529,16 @@ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
524529
- If {DoesFragmentTypeApply(objectType, fragmentType)} is false, continue
525530
with the next {selection} in {selectionSet}.
526531
- Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
527-
- Let {fragmentGroupedFieldSet} be the result of calling
528-
{CollectFields(objectType, fragmentSelectionSet, variableValues,
529-
visitedFragments)}.
530-
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
531-
- Let {responseKey} be the response key shared by all fields in
532-
{fragmentGroup}.
533-
- Let {groupForResponseKey} be the list in {groupedFields} for
534-
{responseKey}; if no such list exists, create it as an empty list.
535-
- Append all items in {fragmentGroup} to {groupForResponseKey}.
532+
- Call {CollectFields(objectType, fragmentSelectionSet, variableValues,
533+
groupedFieldSet, visitedFragments)}.
536534
- If {selection} is an {InlineFragment}:
537535
- Let {fragmentType} be the type condition on {selection}.
538536
- If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
539537
fragmentType)} is false, continue with the next {selection} in
540538
{selectionSet}.
541539
- Let {fragmentSelectionSet} be the top-level selection set of {selection}.
542-
- Let {fragmentGroupedFieldSet} be the result of calling
543-
{CollectFields(objectType, fragmentSelectionSet, variableValues,
544-
visitedFragments)}.
545-
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
546-
- Let {responseKey} be the response key shared by all fields in
547-
{fragmentGroup}.
548-
- Let {groupForResponseKey} be the list in {groupedFields} for
549-
{responseKey}; if no such list exists, create it as an empty list.
550-
- Append all items in {fragmentGroup} to {groupForResponseKey}.
551-
- Return {groupedFields}.
540+
- Call {CollectFields(objectType, fragmentSelectionSet, variableValues,
541+
groupedFieldSet, visitedFragments)}.
552542

553543
DoesFragmentTypeApply(objectType, fragmentType):
554544

@@ -565,6 +555,30 @@ DoesFragmentTypeApply(objectType, fragmentType):
565555
Note: The steps in {CollectFields()} evaluating the `@skip` and `@include`
566556
directives may be applied in either order since they apply commutatively.
567557

558+
### Root Field Collection
559+
560+
Root field collection processes the operation's top-level selection set:
561+
562+
CollectRootFields(rootType, operationSelectionSet, variableValues):
563+
564+
- Initialize {groupedFieldSet} to an empty ordered map of lists.
565+
- Call {CollectFields(rootType, operationSelectionSet, groupedFieldSet)}.
566+
- Return {groupedFieldSet}.
567+
568+
### Object Subfield Collection
569+
570+
Root field collection processes the operation's top-level selection set:
571+
572+
CollectSubfields(objectType, fields, variableValues):
573+
574+
- Initialize {groupedSubfieldSet} to an empty ordered map of lists.
575+
- For each {field} in {fields}:
576+
- Let {fieldSelectionSet} be the selection set of {field}.
577+
- If {fieldSelectionSet} is null or empty, continue to the next field.
578+
- Call {CollectFields(objectType, fieldSelectionSet, variableValues,
579+
groupedSubfieldSet)}.
580+
- Return {groupedSubfieldSet}.
581+
568582
## Executing Fields
569583

570584
Each field requested in the grouped field set that is defined on the selected
@@ -692,8 +706,9 @@ CompleteValue(fieldType, fields, result, variableValues):
692706
- Let {objectType} be {fieldType}.
693707
- Otherwise if {fieldType} is an Interface or Union type.
694708
- Let {objectType} be {ResolveAbstractType(fieldType, result)}.
695-
- Let {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}.
696-
- Return the result of evaluating {ExecuteSelectionSet(subSelectionSet,
709+
- Let {groupedSubfieldSet} be the result of calling
710+
{CollectSubfields(objectType, fields, variableValues)}.
711+
- Return the result of evaluating {ExecuteFieldSet(groupedSubfieldSet,
697712
objectType, result, variableValues)} _normally_ (allowing for
698713
parallelization).
699714

@@ -761,15 +776,6 @@ sub-selections.
761776
After resolving the value for `me`, the selection sets are merged together so
762777
`firstName` and `lastName` can be resolved for one value.
763778

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}.
772-
773779
### Handling Field Errors
774780

775781
A _field error_ is an error raised from a particular field during value

0 commit comments

Comments
 (0)