Spec-Zone.ru › Ruby on Rails 4.1

модуль ActiveRecord::Batches

Методы публичного экземпляра

find_each(options = {}) { |record| ... } Показать исходный код

Итерация по коллекции записей из базы данных (например, с помощью метода all ) очень неэффективна, так как она попытается создать все объекты сразу.

В этом случае методы пакетной обработки позволяют работать с записями по частям, тем самым значительно уменьшая потребление памяти.

Метод find_each использует метод find_in_batches с размером пакета 1000 (или как указано в параметре :batch_size).

Person.find_each do |person|
  person.do_awesome_stuff
end

Person.where("age > 21").find_each do |person|
  person.party_all_night!
end

Если вы не предоставите блок для find_each, он вернет перечислитель для цепочки с другими методами:

Person.find_each.with_index do |person, index|
  person.award_trophy(index + 1)
end

Параметры

  • :batch_size - Указывает размер пакета. По умолчанию 1000.

  • :start - Указывает стартовую точку для пакетной обработки.

Это особенно полезно, если вы хотите, чтобы несколько рабочих процессов обрабатывали одну и ту же очередь обработки. Вы можете сделать так, чтобы рабочий процесс 1 обрабатывал все записи с id от 0 до 10 000, а рабочий процесс 2 — от 10 000 и далее (установив параметр :start для этого рабочего процесса).

# Let's process for a batch of 2000 records, skipping the first 2000 rows
Person.find_each(start: 2000, batch_size: 2000) do |person|
  person.party_all_night!
end

ПРИМЕЧАНИЕ: Установить порядок невозможно. Он автоматически устанавливается в возрастающем порядке по первичному ключу («id ASC»), чтобы обеспечить правильный порядок пакетной обработки. Это также означает, что этот метод работает только с первичными ключами на основе целых чисел.

ПРИМЕЧАНИЕ: Также нельзя установить ограничение, оно используется для управления размерами пакетов.

# File activerecord/lib/active_record/relation/batches.rb, line 49
def find_each(options = {})
  if block_given?
    find_in_batches(options) do |records|
      records.each { |record| yield record }
    end
  else
    enum_for :find_each, options do
      options[:start] ? where(table[primary_key].gteq(options[:start])).size : size
    end
  end
end
find_in_batches(options = {}) { |records| ... } Показать исходный код

Возвращает каждый пакет записей, найденных с помощью поиска options в виде массива.

Person.where("age > 21").find_in_batches do |group|
  sleep(50) # Make sure it doesn't get too crowded in there!
  group.each { |person| person.party_all_night! }
end

Если вы не предоставите блок для find_in_batches, он вернет перечислитель для цепочки с другими методами:

Person.find_in_batches.with_index do |group, batch|
  puts "Processing group ##{batch}"
  group.each(&:recover_from_last_night!)
end

Чтобы получать каждую запись по отдельности, используйте find_each.

Параметры

  • :batch_size - Указывает размер пакета. По умолчанию 1000.

  • :start - Указывает стартовую точку для пакетной обработки.

Это особенно полезно, если вы хотите, чтобы несколько рабочих процессов обрабатывали одну и ту же очередь обработки. Вы можете сделать так, чтобы рабочий процесс 1 обрабатывал все записи с id от 0 до 10 000, а рабочий процесс 2 — от 10 000 и далее (установив параметр :start для этого рабочего процесса).

# Let's process the next 2000 records
Person.find_in_batches(start: 2000, batch_size: 2000) do |group|
  group.each { |person| person.party_all_night! }
end

ПРИМЕЧАНИЕ: Установить порядок невозможно. Он автоматически устанавливается в возрастающем порядке по первичному ключу («id ASC»), чтобы обеспечить правильный порядок пакетной обработки. Это также означает, что этот метод работает только с первичными ключами на основе целых чисел.

ПРИМЕЧАНИЕ: Также нельзя установить ограничение, оно используется для управления размерами пакетов.

# File activerecord/lib/active_record/relation/batches.rb, line 99
def find_in_batches(options = {})
  options.assert_valid_keys(:start, :batch_size)

  relation = self
  start = options[:start]
  batch_size = options[:batch_size] || 1000

  unless block_given?
    return to_enum(:find_in_batches, options) do
      total = start ? where(table[primary_key].gteq(start)).size : size
      (total - 1).div(batch_size) + 1
    end
  end

  if logger && (arel.orders.present? || arel.taken.present?)
    logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
  end

  relation = relation.reorder(batch_order).limit(batch_size)
  relation.reverse_order_value = false
  records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a

  while records.any?
    records_size = records.size
    primary_key_offset = records.last.id
    raise "Primary key not included in the custom select clause" unless primary_key_offset

    yield records

    break if records_size < batch_size

    records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
  end
end

© 2004–2016 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API