Module: Pipeloader::Batch::BatchLoader

Defined in:
lib/pipeloader/batch/batch_loader.rb

Overview

Runs ONE query for a BatchProxy’s accumulated scope across every live sibling of the owner, partitions rows by foreign key, applies per-group limit/offset, and caches each sibling’s slice (keyed by the scope) so the other siblings’ identical access is free.

Class Method Summary collapse

Class Method Details

.apply_window!(grouped, proxy) ⇒ Object

limit/offset are per group: the batched query is ordered but unlimited, and each owner’s slice is taken in Ruby (so “5 per author”, not 5 overall).



52
53
54
55
56
57
58
59
60
# File 'lib/pipeloader/batch/batch_loader.rb', line 52

def apply_window!(grouped, proxy)
  offset = proxy.offset_value || 0
  limit = proxy.limit_value
  grouped.each_key do |key|
    windowed = grouped[key].drop(offset)
    windowed = windowed.first(limit) if limit
    grouped[key] = windowed
  end
end

.load(proxy) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/pipeloader/batch/batch_loader.rb', line 12

def load(proxy)
  owner = proxy.owner
  cache_key = [proxy.name, proxy.cache_signature]
  cache = owner._pipeloader_batch_scope_cache
  return cache[cache_key] if cache.key?(cache_key)

  siblings = relevant_siblings(proxy)
  grouped = run(proxy, siblings)

  siblings.each do |sibling|
    sibling._pipeloader_batch_scope_cache[cache_key] = grouped[sibling.send(proxy.owner_key)]
  end
  grouped[owner.send(proxy.owner_key)]
end

.relevant_siblings(proxy) ⇒ Object



27
28
29
30
31
32
# File 'lib/pipeloader/batch/batch_loader.rb', line 27

def relevant_siblings(proxy)
  owner = proxy.owner
  siblings = owner._pipeloader_batch_context.all(owner.class).select(&:persisted?)
  siblings << owner if owner.persisted? && siblings.none? { |sibling| sibling.equal?(owner) }
  siblings
end

.run(proxy, siblings) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/pipeloader/batch/batch_loader.rb', line 34

def run(proxy, siblings)
  foreign_key = proxy.foreign_key
  ids = siblings.map { |sibling| sibling.send(proxy.owner_key) }.compact.uniq

  grouped = Hash.new { |hash, key| hash[key] = [] }
  return grouped if ids.empty?

  scope = proxy.relation.where(foreign_key => ids)
  # A custom .select must still include the FK so we can partition the rows.
  scope = scope.select(foreign_key) if scope.select_values.any?
  scope.to_a.each { |record| grouped[record[foreign_key]] << record }

  apply_window!(grouped, proxy) if proxy.limit_value || proxy.offset_value
  grouped
end