class GraphQL::Analysis::QueryComplexity

Calculate the complexity of a query, using {Field#complexity} values.

@example Log the complexity of incoming queries

MySchema.query_analyzers << GraphQL::Analysis::QueryComplexity.new do |query, complexity|
  Rails.logger.info("Complexity: #{complexity}")
end

Public Class Methods

new(&block) click to toggle source

@yield [query, complexity] Called for each query analyzed by the schema, before executing it @yieldparam query [GraphQL::Query] The query that was analyzed @yieldparam complexity [Numeric] The complexity for this query

# File lib/graphql/analysis/query_complexity.rb, line 15
def initialize(&block)
  @complexity_handler = block
end

Public Instance Methods

call(memo, visit_type, irep_node) click to toggle source

Implement the query analyzer API

# File lib/graphql/analysis/query_complexity.rb, line 30
def call(memo, visit_type, irep_node)
  if irep_node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
    if visit_type == :enter
      memo[:complexities_on_type].push(TypeComplexity.new)
    else
      type_complexities = memo[:complexities_on_type].pop
      child_complexity = type_complexities.max_possible_complexity
      own_complexity = get_complexity(irep_node, child_complexity)
      memo[:complexities_on_type].last.merge(irep_node.owner_type, own_complexity)
    end
  end
  memo
end
final_value(reduced_value) click to toggle source

Send the query and complexity to the block @return [Object, GraphQL::AnalysisError] Whatever the handler returns

# File lib/graphql/analysis/query_complexity.rb, line 46
def final_value(reduced_value)
  total_complexity = reduced_value[:complexities_on_type].last.max_possible_complexity
  @complexity_handler.call(reduced_value[:target], total_complexity)
end
initial_value(target) click to toggle source

State for the query complexity calcuation:

  • `target` is passed to handler

  • `complexities_on_type` holds complexity scores for each type in an IRep node

# File lib/graphql/analysis/query_complexity.rb, line 22
def initial_value(target)
  {
    target: target,
    complexities_on_type: [TypeComplexity.new],
  }
end

Private Instance Methods

get_complexity(irep_node, child_complexity) click to toggle source

Get a complexity value for a field, by getting the number or calling its proc

# File lib/graphql/analysis/query_complexity.rb, line 55
def get_complexity(irep_node, child_complexity)
  field_defn = irep_node.definition
  defined_complexity = field_defn.complexity
  case defined_complexity
  when Proc
    defined_complexity.call(irep_node.query.context, irep_node.arguments, child_complexity)
  when Numeric
    defined_complexity + (child_complexity || 0)
  else
    raise("Invalid complexity: #{defined_complexity.inspect} on #{field_defn.name}")
  end
end