Module: BetterRecord::Relation

Defined in:
lib/better_record/relation.rb

Instance Method Summary collapse

Instance Method Details

#pluck_in_batches(*columns, batch_size: 1000) ⇒ Object

pluck_in_batches: yields an array of *columns that is at least size

batch_size to a block.

Special case: if there is only one column selected than each batch
              will yield an array of columns like [:column, :column, ...]
              rather than [[:column], [:column], ...]

Arguments

columns      ->  an arbitrary selection of columns found on the table.
batch_size   ->  How many items to pluck at a time
&block       ->  A block that processes an array of returned columns.
                 Array is, at most, size batch_size

Returns

nothing is returned from the function


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/better_record/relation.rb', line 19

def pluck_in_batches(*columns, batch_size: 1000)
  if columns.empty?
    raise "There must be at least one column to pluck"
  end

  # the :id to start the query at
  batch_start = nil

  # It's cool. We're only taking in symbols
  # no deep clone needed
  select_columns = columns.dup

  # Find index of :id in the array
  remove_id_from_results = false
  id_index = columns.index(primary_key.to_sym)

  # :id is still needed to calculate offsets
  # add it to the front of the array and remove it when yielding
  if id_index.nil?
    id_index = 0
    select_columns.unshift(primary_key)

    remove_id_from_results = true
  end

  loop do
    relation = self.reorder(table[primary_key].asc).limit(batch_size)
    relation = relation.where(table[primary_key].gt(batch_start)) if batch_start
    items = relation.pluck(*select_columns)

    break if items.empty?

    # Use the last id to calculate where to offset queries
    last_item = items.last
    batch_start = last_item.is_a?(Array) ? last_item[id_index] : last_item

    # Remove :id column if not in *columns
    items.map! { |row| row[1..-1] } if remove_id_from_results

    yield items

    break if items.size < batch_size
  end
end