Skip to content

Commit

Permalink
moves Field Collection section earlier
Browse files Browse the repository at this point in the history
  • Loading branch information
yaacovCR committed Feb 15, 2024
1 parent 145c560 commit 61bad40
Showing 1 changed file with 106 additions and 106 deletions.
212 changes: 106 additions & 106 deletions spec/Section 6 -- Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,112 @@ serial):
selection set.
- Return an unordered map containing {data} and {errors}.

### Field Collection

Before execution, the selection set is converted to a grouped field set by
calling {CollectFields()}. Each entry in the grouped field set is a list of
fields that share a response key (the alias if defined, otherwise the field
name). This ensures all fields with the same response key (including those in
referenced fragments) are executed at the same time.

As an example, collecting the fields of this selection set would collect two
instances of the field `a` and one of field `b`:

```graphql example
{
a {
subfield1
}
...ExampleFragment
}

fragment ExampleFragment on Query {
a {
subfield2
}
b
}
```

The depth-first-search order of the field groups produced by {CollectFields()}
is maintained through execution, ensuring that fields appear in the executed
response in a stable and predictable order.

CollectFields(objectType, selectionSet, variableValues, visitedFragments):

- If {visitedFragments} is not provided, initialize it to the empty set.
- Initialize {groupedFields} to an empty ordered map of lists.
- For each {selection} in {selectionSet}:
- If {selection} provides the directive `@skip`, let {skipDirective} be that
directive.
- If {skipDirective}'s {if} argument is {true} or is a variable in
{variableValues} with the value {true}, continue with the next {selection}
in {selectionSet}.
- If {selection} provides the directive `@include`, let {includeDirective} be
that directive.
- If {includeDirective}'s {if} argument is not {true} and is not a variable
in {variableValues} with the value {true}, continue with the next
{selection} in {selectionSet}.
- If {selection} is a {Field}:
- Let {responseKey} be the response key of {selection} (the alias if
defined, otherwise the field name).
- Let {groupForResponseKey} be the list in {groupedFields} for
{responseKey}; if no such list exists, create it as an empty list.
- Append {selection} to the {groupForResponseKey}.
- If {selection} is a {FragmentSpread}:
- Let {fragmentSpreadName} be the name of {selection}.
- If {fragmentSpreadName} is in {visitedFragments}, continue with the next
{selection} in {selectionSet}.
- Add {fragmentSpreadName} to {visitedFragments}.
- Let {fragment} be the Fragment in the current Document whose name is
{fragmentSpreadName}.
- If no such {fragment} exists, continue with the next {selection} in
{selectionSet}.
- Let {fragmentType} be the type condition on {fragment}.
- If {DoesFragmentTypeApply(objectType, fragmentType)} is false, continue
with the next {selection} in {selectionSet}.
- Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
- Let {fragmentGroupedFieldSet} be the result of calling
{CollectFields(objectType, fragmentSelectionSet, variableValues,
visitedFragments)}.
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
- Let {responseKey} be the response key shared by all fields in
{fragmentGroup}.
- Let {groupForResponseKey} be the list in {groupedFields} for
{responseKey}; if no such list exists, create it as an empty list.
- Append all items in {fragmentGroup} to {groupForResponseKey}.
- If {selection} is an {InlineFragment}:
- Let {fragmentType} be the type condition on {selection}.
- If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
fragmentType)} is false, continue with the next {selection} in
{selectionSet}.
- Let {fragmentSelectionSet} be the top-level selection set of {selection}.
- Let {fragmentGroupedFieldSet} be the result of calling
{CollectFields(objectType, fragmentSelectionSet, variableValues,
visitedFragments)}.
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
- Let {responseKey} be the response key shared by all fields in
{fragmentGroup}.
- Let {groupForResponseKey} be the list in {groupedFields} for
{responseKey}; if no such list exists, create it as an empty list.
- Append all items in {fragmentGroup} to {groupForResponseKey}.
- Return {groupedFields}.

DoesFragmentTypeApply(objectType, fragmentType):

- If {fragmentType} is an Object Type:
- if {objectType} and {fragmentType} are the same type, return {true},
otherwise return {false}.
- If {fragmentType} is an Interface Type:
- if {objectType} is an implementation of {fragmentType}, return {true}
otherwise return {false}.
- If {fragmentType} is a Union:
- if {objectType} is a possible type of {fragmentType}, return {true}
otherwise return {false}.

Note: The steps in {CollectFields()} evaluating the `@skip` and `@include`
directives may be applied in either order since they apply commutatively.

## Executing a Grouped Field Set

To execute a grouped field set, the object value being evaluated and the object
Expand Down Expand Up @@ -471,112 +577,6 @@ A correct executor must generate the following result for that selection set:
}
```

### Field Collection

Before execution, the selection set is converted to a grouped field set by
calling {CollectFields()}. Each entry in the grouped field set is a list of
fields that share a response key (the alias if defined, otherwise the field
name). This ensures all fields with the same response key (including those in
referenced fragments) are executed at the same time.

As an example, collecting the fields of this selection set would collect two
instances of the field `a` and one of field `b`:

```graphql example
{
a {
subfield1
}
...ExampleFragment
}

fragment ExampleFragment on Query {
a {
subfield2
}
b
}
```

The depth-first-search order of the field groups produced by {CollectFields()}
is maintained through execution, ensuring that fields appear in the executed
response in a stable and predictable order.

CollectFields(objectType, selectionSet, variableValues, visitedFragments):

- If {visitedFragments} is not provided, initialize it to the empty set.
- Initialize {groupedFields} to an empty ordered map of lists.
- For each {selection} in {selectionSet}:
- If {selection} provides the directive `@skip`, let {skipDirective} be that
directive.
- If {skipDirective}'s {if} argument is {true} or is a variable in
{variableValues} with the value {true}, continue with the next {selection}
in {selectionSet}.
- If {selection} provides the directive `@include`, let {includeDirective} be
that directive.
- If {includeDirective}'s {if} argument is not {true} and is not a variable
in {variableValues} with the value {true}, continue with the next
{selection} in {selectionSet}.
- If {selection} is a {Field}:
- Let {responseKey} be the response key of {selection} (the alias if
defined, otherwise the field name).
- Let {groupForResponseKey} be the list in {groupedFields} for
{responseKey}; if no such list exists, create it as an empty list.
- Append {selection} to the {groupForResponseKey}.
- If {selection} is a {FragmentSpread}:
- Let {fragmentSpreadName} be the name of {selection}.
- If {fragmentSpreadName} is in {visitedFragments}, continue with the next
{selection} in {selectionSet}.
- Add {fragmentSpreadName} to {visitedFragments}.
- Let {fragment} be the Fragment in the current Document whose name is
{fragmentSpreadName}.
- If no such {fragment} exists, continue with the next {selection} in
{selectionSet}.
- Let {fragmentType} be the type condition on {fragment}.
- If {DoesFragmentTypeApply(objectType, fragmentType)} is false, continue
with the next {selection} in {selectionSet}.
- Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
- Let {fragmentGroupedFieldSet} be the result of calling
{CollectFields(objectType, fragmentSelectionSet, variableValues,
visitedFragments)}.
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
- Let {responseKey} be the response key shared by all fields in
{fragmentGroup}.
- Let {groupForResponseKey} be the list in {groupedFields} for
{responseKey}; if no such list exists, create it as an empty list.
- Append all items in {fragmentGroup} to {groupForResponseKey}.
- If {selection} is an {InlineFragment}:
- Let {fragmentType} be the type condition on {selection}.
- If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
fragmentType)} is false, continue with the next {selection} in
{selectionSet}.
- Let {fragmentSelectionSet} be the top-level selection set of {selection}.
- Let {fragmentGroupedFieldSet} be the result of calling
{CollectFields(objectType, fragmentSelectionSet, variableValues,
visitedFragments)}.
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
- Let {responseKey} be the response key shared by all fields in
{fragmentGroup}.
- Let {groupForResponseKey} be the list in {groupedFields} for
{responseKey}; if no such list exists, create it as an empty list.
- Append all items in {fragmentGroup} to {groupForResponseKey}.
- Return {groupedFields}.

DoesFragmentTypeApply(objectType, fragmentType):

- If {fragmentType} is an Object Type:
- if {objectType} and {fragmentType} are the same type, return {true},
otherwise return {false}.
- If {fragmentType} is an Interface Type:
- if {objectType} is an implementation of {fragmentType}, return {true}
otherwise return {false}.
- If {fragmentType} is a Union:
- if {objectType} is a possible type of {fragmentType}, return {true}
otherwise return {false}.

Note: The steps in {CollectFields()} evaluating the `@skip` and `@include`
directives may be applied in either order since they apply commutatively.

## Executing Fields

Each field requested in the grouped field set that is defined on the selected
Expand Down

0 comments on commit 61bad40

Please sign in to comment.