class OnlineMigrations::BatchIterator
@private
Attributes
relation[R]
Public Class Methods
new(relation)
click to toggle source
# File lib/online_migrations/batch_iterator.rb, line 8 def initialize(relation) if !relation.is_a?(ActiveRecord::Relation) raise ArgumentError, "relation is not an ActiveRecord::Relation" end @relation = relation end
Public Instance Methods
each_batch(of: 1000, column: relation.primary_key, start: nil, finish: nil, order: :asc) { |batch_relation, index| ... }
click to toggle source
# File lib/online_migrations/batch_iterator.rb, line 16 def each_batch(of: 1000, column: relation.primary_key, start: nil, finish: nil, order: :asc) unless [:asc, :desc].include?(order) raise ArgumentError, ":order must be :asc or :desc, got #{order.inspect}" end relation = apply_limits(self.relation, column, start, finish, order) base_relation = relation.except(:select) .select(column) .reorder(column => order) start_row = base_relation.uncached { base_relation.first } return unless start_row start_id = start_row[column] arel_table = relation.arel_table 0.step do |index| if order == :asc start_cond = arel_table[column].gteq(start_id) else start_cond = arel_table[column].lteq(start_id) end stop_row = base_relation.uncached do base_relation .where(start_cond) .offset(of) .first end batch_relation = relation.where(start_cond) if stop_row stop_id = stop_row[column] start_id = stop_id if order == :asc stop_cond = arel_table[column].lt(stop_id) else stop_cond = arel_table[column].gt(stop_id) end batch_relation = batch_relation.where(stop_cond) end # Any ORDER BYs are useless for this relation and can lead to less # efficient UPDATE queries, hence we get rid of it. batch_relation = batch_relation.except(:order) # Retaining the results in the query cache would undermine the point of batching. batch_relation.uncached { yield batch_relation, index } break unless stop_row end end
Private Instance Methods
apply_limits(relation, column, start, finish, order)
click to toggle source
# File lib/online_migrations/batch_iterator.rb, line 75 def apply_limits(relation, column, start, finish, order) if start relation = relation.where(relation.arel_table[column].public_send((order == :asc ? :gteq : :lteq), start)) end if finish relation = relation.where(relation.arel_table[column].public_send((order == :asc ? :lteq : :gteq), finish)) end relation end