class GraphQL::Execution::Lookahead
Lookahead creates a uniform interface to inspect the forthcoming selections.
It assumes that the AST it’s working with is valid. (So, it’s safe to use during execution, but if you’re using it directly, be sure to validate first.)
A field may get access to its lookahead by adding extras: [:lookahead] to its configuration.
Examples
Example: looking ahead in a field
field :articles, [Types::Article], null: false, extras: [:lookahead] # For example, imagine a faster database call # may be issued when only some fields are requested. # # Imagine that _full_ fetch must be made to satisfy `fullContent`, # we can look ahead to see if we need that field. If we do, # we make the expensive database call instead of the cheap one. def articles(lookahead:) if lookahead.selects?(:full_content) fetch_full_articles(object) else fetch_preview_articles(object) end end
Constants
- NULL_LOOKAHEAD
-
A singleton, so that misses don’t come with overhead.
Attributes
Returns
-
Array<GraphQL::Language::Nodes::Field>
Returns
Returns
-
GraphQL::Schema::Object, GraphQL::Schema::Union, GraphQL::Schema::Interface
Public Class Methods
# File lib/graphql/execution/lookahead.rb, line 45 def initialize(query:, ast_nodes:, field: nil, root_type: nil, owner_type: nil) @ast_nodes = ast_nodes.freeze @field = field @root_type = root_type @query = query @selected_type = @field ? @field.type.unwrap : root_type @owner_type = owner_type end
Parameters
-
query(GraphQL::Query) -
ast_nodes(Array<GraphQL::Language::Nodes::Field>, Array<GraphQL::Language::Nodes::OperationDefinition>) -
field(GraphQL::Schema::Field) — ifast_nodesare fields, this is the field definition matching those nodes -
root_type(Class) — ifast_nodesare operation definition, this is the root type for that operation
Public Instance Methods
# File lib/graphql/execution/lookahead.rb, line 207 def alias_selection(alias_name, selected_type: @selected_type, arguments: nil) alias_cache_key = [alias_name, selected_type, arguments] return alias_selections[alias_cache_key] if alias_selections.key?(alias_cache_key) alias_node = lookup_alias_node(ast_nodes, alias_name) return NULL_LOOKAHEAD unless alias_node next_field_defn = @query.types.field(selected_type, alias_node.name) alias_arguments = @query.arguments_for(alias_node, next_field_defn) if alias_arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments) alias_arguments = alias_arguments.keyword_arguments end return NULL_LOOKAHEAD if arguments && arguments != alias_arguments alias_selections[alias_cache_key] = lookahead_for_selection(next_field_defn, selected_type, alias_arguments, alias_name) end
# File lib/graphql/execution/lookahead.rb, line 75 def arguments if defined?(@arguments) @arguments else @arguments = if @field @query.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args| case args when Execution::Interpreter::Arguments args.keyword_arguments when GraphQL::ExecutionError EmptyObjects::EMPTY_HASH else args end end else nil end end end
Returns
-
Hash<Symbol, Object>
Source
# File lib/graphql/execution/lookahead.rb, line 299 def inspect "#<GraphQL::Execution::Lookahead #{@field ? "@field=#{@field.path.inspect}": "@root_type=#{@root_type}"} @ast_nodes.size=#{@ast_nodes.size}>" end
Source
# File lib/graphql/execution/lookahead.rb, line 295 def name @field && @field.original_name end
The method name of the field. It returns the method_sym of the Lookahead’s field.
Examples
Example: getting the name of a selection
def articles(lookahead:) article.selection(:full_content).name # => :full_content # ... end
Returns
-
Symbol
Source
# File lib/graphql/execution/lookahead.rb, line 152 def selected? true end
Returns
-
Boolean— True if this lookahead represents a field that was requested
Source
# File lib/graphql/execution/lookahead.rb, line 169 def selection(field_name, selected_type: @selected_type, arguments: nil) next_field_defn = case field_name when String @query.types.field(selected_type, field_name) when Symbol # Try to avoid the `.to_s` below, if possible all_fields = if selected_type.kind.fields? @query.types.fields(selected_type) else # Handle unions by checking possible @query.types .possible_types(selected_type) .map { |t| @query.types.fields(t) } .tap(&:flatten!) end if (match_by_orig_name = all_fields.find { |f| f.original_name == field_name }) match_by_orig_name else # Symbol#name is only present on 3.0+ sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s guessed_name = Schema::Member::BuildType.camelize(sym_s) @query.types.field(selected_type, guessed_name) end end lookahead_for_selection(next_field_defn, selected_type, arguments) end
# File lib/graphql/execution/lookahead.rb, line 254 def selections(arguments: nil) subselections_by_type = {} subselections_on_type = subselections_by_type[@selected_type] = {} @ast_nodes.each do |node| find_selections(subselections_by_type, subselections_on_type, @selected_type, node.selections, arguments) end subselections = [] subselections_by_type.each do |type, ast_nodes_by_response_key| ast_nodes_by_response_key.each do |response_key, ast_nodes| field_defn = @query.types.field(type, ast_nodes.first.name) lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type) subselections.push(lookahead) end end subselections end
Like selection, but for all nodes. It returns a list of Lookaheads for all Selections
If arguments: is provided, each provided key/value will be matched against the arguments in each selection. This method will filter the selections if any of the given arguments: do not match the given selection.
Examples
Example: getting the name of a selection
def articles(lookahead:) next_lookaheads = lookahead.selections # => [#<GraphQL::Execution::Lookahead ...>, ...] next_lookaheads.map(&:name) #=> [:full_content, :title] end
Parameters
-
arguments(Hash) — Arguments which must match in the selection
Returns
-
Array<GraphQL::Execution::Lookahead>
# File lib/graphql/execution/lookahead.rb, line 117 def selects?(field_name, selected_type: @selected_type, arguments: nil) selection(field_name, selected_type: selected_type, arguments: arguments).selected? end
True if this node has a selection on field_name. If field_name is a String, it is treated as a GraphQL-style (camelized) field name and used verbatim. If field_name is a Symbol, it is treated as a Ruby-style (underscored) name and camelized before comparing.
If arguments: is provided, each provided key/value will be matched against the arguments in the next selection. This method will return false if any of the given arguments: are not present and matching in the next selection. (But, the next selection may contain more than the given arguments.)
Parameters
-
field_name(String, Symbol) -
arguments(Hash) — Arguments which must match in the selection
Returns
-
Boolean
# File lib/graphql/execution/lookahead.rb, line 142 def selects_alias?(alias_name, arguments: nil) alias_selection(alias_name, arguments: arguments).selected? end
True if this node has a selection with alias matching alias_name. If alias_name is a String, it is treated as a GraphQL-style (camelized) field name and used verbatim. If alias_name is a Symbol, it is treated as a Ruby-style (underscored) name and camelized before comparing.
If arguments: is provided, each provided key/value will be matched against the arguments in the next selection. This method will return false if any of the given arguments: are not present and matching in the next selection. (But, the next selection may contain more than the given arguments.)
Parameters
-
alias_name(String, Symbol) -
arguments(Hash) — Arguments which must match in the selection
Returns
-
Boolean