如何将图像迁移到 Rails 中的新字段?

How to migrate images to new field in Rails?

我在 Rails 5.2.3 上使用 Ruby,用于图像的 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

图像作为链接保存在 pic 字段中。 现在我使用这个代码,问题是这需要很长时间并且不是所有的图像都被保存。

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

日志

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>
User.where(:pic.exists => true).all.each &:update

这很慢,因为 .all.each 将所有匹配的用户加载到内存中,find_each 在内存上更有效一些,因为它会分批加载,但仍然浪费时间和内存将每个对象加载到内存中并将其变成一个对象以复制一个属性。然后它 运行 是每个单独的 update

相反,您可以在单个查询中完全在数据库中执行此操作。


如果打算从 User.pic 复制到 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.notUsers.where(image_url: nil).where.not(pic: nil)