Reputation: 23
I use Ruby on Rails 5.2.3, Mongoid, Attachinary and Cloudinary for images.
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
Images are saved in pic field as links. Now I use this code, the problem is that this takes a very long time and not all images are saved.
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>
Upvotes: 0
Views: 573
Reputation: 165366
User.where(:pic.exists => true).all.each &:update
This is slow because .all.each
loads all matching Users into memory, find_each
is a bit more efficient on memory as it will load in batches, but it's still a waste of time and memory to load each object into memory and turn it into an object to copy one attribute. Then it runs an update
on each individual one.
Instead, you can do this entirely in the database in a single query.
If the intent is to copy from User.pic
to User.image_url
, you can do this in a single statement.
# 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")
This will run a single query:
update users
set image_url = pic
where image_url is null
There's no need to also check for users who lack a pic
because there's no harm in setting nil to nil, and a simpler search might be faster. But if you like check you can use where.not
. Users.where(image_url: nil).where.not(pic: nil)
Upvotes: 1