Class: Pipeloader::ProjectionExtension

Inherits:
GraphQL::Schema::FieldExtension
  • Object
show all
Defined in:
lib/pipeloader/field_exact.rb

Overview

Narrows a returned ActiveRecord::Relation to exactly the columns the query needs — and bails to a whole-row fetch the moment a selected field can’t be proven to read only known columns, so it can never raise MissingAttributeError.

Instance Method Summary collapse

Instance Method Details

#resolve(object:, arguments:, context:) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/pipeloader/field_exact.rb', line 42

def resolve(object:, arguments:, context:, **)
  lookahead = arguments[:lookahead]
  inner = arguments.key?(:lookahead) ? arguments.reject { |k, _| k == :lookahead } : arguments

  # A belongs_to is loaded whole-row by `object.assoc`, so resolve it via a
  # projected query instead (still pipelined). Skipped if the type defines a
  # custom resolver, or the selection is opaque (then fall through to default).
  record = object.respond_to?(:object) ? object.object : object
  if lookahead && record.is_a?(ActiveRecord::Base) &&
     !field.owner.instance_methods(false).include?(field.resolver_method) &&
     (assoc = record.class.reflect_on_association(field.method_str.to_sym))&.belongs_to?
    fk = record.public_send(assoc.foreign_key)
    return nil if fk.nil?

    cols = Pipeloader.project_columns(assoc.klass, lookahead)
    return assoc.klass.where(assoc.klass.primary_key => fk).select(*cols).first if cols
  end

  value = yield(object, inner)
  return value unless lookahead && value.is_a?(ActiveRecord::Relation)

  cols = Pipeloader.project_columns(value.klass, lookahead)
  return value unless cols # opaque field selected -> fetch whole rows

  # Keep a has_many's foreign key so AR can still group / wire the inverse.
  if value.respond_to?(:proxy_association) && (proxy = value.proxy_association)
    cols += Array(proxy.reflection.foreign_key)
  end
  value.select(*cols.uniq)
end