Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace ExecuteSelectionSet with ExecuteGroupedFieldSet #1039

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion spec/Section 5 -- Validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ unambiguous. Therefore any two field selections which might both be encountered
for the same object are only valid if they are equivalent.

During execution, the simultaneous execution of fields with the same response
name is accomplished by {MergeSelectionSets()} and {CollectFields()}.
name is accomplished by {CollectSubfields()}.

For simple hand-written GraphQL, this rule is obviously a clear developer error,
however nested fragments can make this difficult to detect manually.
Expand Down
91 changes: 55 additions & 36 deletions spec/Section 6 -- Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,8 @@ ExecuteQuery(query, schema, variableValues, initialValue):
- Let {queryType} be the root Query type in {schema}.
- Assert: {queryType} is an Object type.
- Let {selectionSet} be the top level Selection Set in {query}.
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
queryType, initialValue, variableValues)} _normally_ (allowing
parallelization).
- Let {errors} be the list of all _field error_ raised while executing the
selection set.
- Return an unordered map containing {data} and {errors}.
- Return {ExecuteRootSelectionSet(variableValues, initialValue, queryType,
selectionSet)}.

### Mutation

Expand All @@ -153,11 +149,8 @@ ExecuteMutation(mutation, schema, variableValues, initialValue):
- Let {mutationType} be the root Mutation type in {schema}.
- Assert: {mutationType} is an Object type.
- Let {selectionSet} be the top level Selection Set in {mutation}.
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
mutationType, initialValue, variableValues)} _serially_.
- Let {errors} be the list of all _field error_ raised while executing the
selection set.
- Return an unordered map containing {data} and {errors}.
- Return {ExecuteRootSelectionSet(variableValues, initialValue, mutationType,
selectionSet, true)}.

### Subscription

Expand Down Expand Up @@ -300,12 +293,8 @@ ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue):
- Let {subscriptionType} be the root Subscription type in {schema}.
- Assert: {subscriptionType} is an Object type.
- Let {selectionSet} be the top level Selection Set in {subscription}.
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
subscriptionType, initialValue, variableValues)} _normally_ (allowing
parallelization).
- Let {errors} be the list of all _field error_ raised while executing the
selection set.
- Return an unordered map containing {data} and {errors}.
- Return {ExecuteRootSelectionSet(variableValues, initialValue,
subscriptionType, selectionSet)}.

Note: The {ExecuteSubscriptionEvent()} algorithm is intentionally similar to
{ExecuteQuery()} since this is how each event result is produced.
Expand All @@ -321,20 +310,44 @@ Unsubscribe(responseStream):

- Cancel {responseStream}

## Executing Selection Sets
## Executing the Root Selection Set

To execute the root selection set, the object value being evaluated and the
object type need to be known, as well as whether it must be executed serially,
or may be executed in parallel.

To execute a selection set, the object value being evaluated and the object type
need to be known, as well as whether it must be executed serially, or may be
executed in parallel.
Executing the root selection set works similarly for queries (parallel),
mutations (serial), and subscriptions (where it is executed for each event in
the underlying Source Stream).

First, the selection set is turned into a grouped field set; then, each
represented field in the grouped field set produces an entry into a response
map.
First, the selection set is turned into a grouped field set; then, we execute
this grouped field set and return the resulting {data} and {errors}.

ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues):
ExecuteRootSelectionSet(variableValues, initialValue, objectType, selectionSet,
serial):

- If {serial} is not provided, initialize it to {false}.
- Let {groupedFieldSet} be the result of {CollectFields(objectType,
selectionSet, variableValues)}.
- Let {data} be the result of running {ExecuteGroupedFieldSet(groupedFieldSet,
objectType, initialValue, variableValues)} _serially_ if {serial} is {true},
_normally_ (allowing parallelization) otherwise.
- Let {errors} be the list of all _field error_ raised while executing the
selection set.
- Return an unordered map containing {data} and {errors}.

## Executing a Grouped Field Set

To execute a grouped field set, the object value being evaluated and the object
type need to be known, as well as whether it must be executed serially, or may
be executed in parallel.

Each represented field in the grouped field set produces an entry into a
response map.

ExecuteGroupedFieldSet(groupedFieldSet, objectType, objectValue,
variableValues):

- Initialize {resultMap} to an empty ordered map.
- For each {groupedFieldSet} as {responseKey} and {fields}:
- Let {fieldName} be the name of the first entry in {fields}. Note: This value
Expand All @@ -352,8 +365,8 @@ is explained in greater detail in the Field Collection section below.

**Errors and Non-Null Fields**

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

If this occurs, any sibling fields which have not yet executed or have not yet
Expand Down Expand Up @@ -691,8 +704,9 @@ CompleteValue(fieldType, fields, result, variableValues):
- Let {objectType} be {fieldType}.
- Otherwise if {fieldType} is an Interface or Union type.
- Let {objectType} be {ResolveAbstractType(fieldType, result)}.
- Let {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}.
- Return the result of evaluating {ExecuteSelectionSet(subSelectionSet,
- Let {groupedFieldSet} be the result of calling {CollectSubfields(objectType,
fields, variableValues)}.
- Return the result of evaluating {ExecuteGroupedFieldSet(groupedFieldSet,
objectType, result, variableValues)} _normally_ (allowing for
parallelization).

Expand Down Expand Up @@ -739,9 +753,9 @@ ResolveAbstractType(abstractType, objectValue):

**Merging Selection Sets**

When more than one field of the same name is executed in parallel, their
selection sets are merged together when completing the value in order to
continue execution of the sub-selection sets.
When more than one field of the same name is executed in parallel, during value
completion their selection sets are collected together to produce a single
grouped field set in order to continue execution of the sub-selection sets.

An example operation illustrating parallel fields with the same name with
sub-selections.
Expand All @@ -760,14 +774,19 @@ sub-selections.
After resolving the value for `me`, the selection sets are merged together so
`firstName` and `lastName` can be resolved for one value.

MergeSelectionSets(fields):
CollectSubfields(objectType, fields, variableValues):

- Let {selectionSet} be an empty list.
- Let {groupedFieldSet} be an empty map.
- For each {field} in {fields}:
- Let {fieldSelectionSet} be the selection set of {field}.
- If {fieldSelectionSet} is null or empty, continue to the next field.
- Append all selections in {fieldSelectionSet} to {selectionSet}.
- Return {selectionSet}.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should call this fieldGroupedFieldSet rather than subGroupedFieldSet as when comparing here between the non prefixed groupedFieldSet and this prefixed grouped field set, they both refer to subfields, just one is from a single node and one is the merged result.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps childGroupedFieldSet? fieldGroupedFieldSet sounds to me like a "field-grouped field set" rather than a "field's grouped-field-set".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, child has the same problem, but maybe if we change fieldSelectionSet in tandem that would work anyway through some context signaling

- Let {subGroupedFieldSet} be the result of {CollectFields(objectType,
fieldSelectionSet, variableValues)}.
- For each {subGroupedFieldSet} as {responseKey} and {subfields}:
- Let {groupForResponseKey} be the list in {groupedFieldSet} for
{responseKey}; if no such list exists, create it as an empty list.
- Append all fields in {subfields} to {groupForResponseKey}.
- Return {groupedFieldSet}.

### Handling Field Errors

Expand Down
Loading