Как перенести изображения в новое поле в Rails? - PullRequest
0 голосов
/ 09 мая 2020

Я использую Ruby в Rails 5.2.3, Mongoid, Attachinary и Cloudinary для изображений.

class User
  include Mongoid::Document

  has_attachment :image, accept: [:jpg, :png, :gif]

  field :pic, type: String

  before_update :migrate_images

  def migrate_images
    self.image_url = self.pic
  end
end

Изображения сохраняются в поле pi c как ссылки. Сейчас я использую этот код, проблема в том, что это занимает очень много времени и не все изображения сохраняются.

User.where(:pic.exists => true).all.each &:update

log

irb(main):001:0> User.where(:pic.exists => true).all.each &:update
=> #<Mongoid::Contextual::Mongo:0x00007ffe5a3f98e0 @cache=nil, @klass=User, @criteria=#<Mongoid::Criteria
  selector: {"pic"=>{"$exists"=>true}}
  options:  {}
  class:    User
  embedded: false>
, @collection=#<Mongo::Collection:0x70365213493680 namespace=link_development.users>, @view=#<Mongo::Collection::View:0x70365213493380 namespace='link_development.users' @filter={"pic"=>{"$exists"=>true}} @options={"session"=>nil}>, @cache_loaded=true>

1 Ответ

1 голос
/ 09 мая 2020
User.where(:pic.exists => true).all.each &:update

Это медленно, потому что .all.each загружает всех подходящих пользователей в память, find_each немного более эффективно использует память, так как загружается партиями, но это все равно пустая трата времени и память для загрузки каждого объекта в память и превращения его в объект для копирования одного атрибута. Затем он запускает update для каждого отдельного.

Вместо этого вы можете сделать это полностью в базе данных в одном запросе.


Если намерение состоит в копировании из * От 1012 * до User.image_url, вы можете сделать это одним оператором.

# Find all the users who do not already have an image_url set
User.where(image_url: nil)
    # Set their image_url to be their pic.
    .update_all("image_url = pic")

Это запустит один запрос:

update users
  set image_url = pic
  where image_url is null

Нет необходимости также проверять пользователей, которые не хватает pic, потому что установка nil на nil не повредит, а более простой поиск может быть быстрее. Но если вам нравится чек, вы можете использовать where.not. Users.where(image_url: nil).where.not(pic: nil)

...