brendon / ranked-model

An acts_as_sortable/acts_as_list replacement built for Rails 4+
https://github.com/mixonic/ranked-model
MIT License
1.09k stars 134 forks source link

Migration docs #158

Closed janklimo closed 4 years ago

janklimo commented 5 years ago

I'm evaluating implementing this gem on a project with lots of existing records.

I couldn't find any info in the docs about how to correctly migrate data.

Here's my model:

module Properties
  class Image < ApplicationRecord
    include RankedModel
    ranks :sort_order, with_same: :property_id
  end
end

there is a unique index on these columns (I'm using Postgres):

add_column :properties_images, :sort_order, :integer
add_index :properties_images, [:property_id, :sort_order], unique: true

Here's the migration script that keeps failing due to a violated uniqueness constraint:

ActiveRecord::Base.transaction do
  Property.all.find_each do |property|
    Properties::Image.where(property: property).order(created_at: :desc).each_with_index do |image, index|
      image.update(sort_order_position: index)
    end
  end
end

What's the recommended way to populate the new column once this gem is installed?

brendon commented 5 years ago

Hi @janklimo, lol, working both sides of the equation eh? :D

I use this gem in some apps and acts_as_list in others and have ended up as the maintainer of both!

Regarding your migration, try not specifying the position. As long as you're updating them in the correct order, ranked-model should assign the positions correctly. (This is just a guess)

I don't think there's much in the way of instructions on how to populate an existing list unfortunately. If the above doesn't work for you then we'd have to dig into the code and see why the uniqueness constraint is being violated. It's probably in the rebalancing routine I'd guess.

janklimo commented 5 years ago

@brendon I tried doing image.touch in the correct order but sort_order still remained nil. If the only thing I want to update is sort_order, what'd you recommend to do?

Edit for posterity: I ended up spacing the items out manually, like so:

ActiveRecord::Base.transaction do
  Property.all.find_each do |property|
    Properties::ImagesQuery.new(property).all.order(created_at: :desc).each_with_index do |image, index|
      image.update(sort_order: index * 1_000)
    end
  end
end
brendon commented 5 years ago

That is certainly one way around it, and perfectly legit (making sure to disable the ranked-model callback as I don't think it copes well with setting the raw column value.

If you'd be keen, did you want to do up a PR with a failing test showing the scenario? I could take a look and see what I can find out :)

oyeanuj commented 4 years ago

@janklimo How did you end up migrating here?

janklimo commented 4 years ago

@oyeanuj like I mentioned above:

ActiveRecord::Base.transaction do
  Property.all.find_each do |property|
    Properties::ImagesQuery.new(property).all.order(created_at: :desc).each_with_index do |image, index|
      image.update(sort_order: index * 1_000)
    end
  end
end

i.e. setting the column value (not position) and keeping the records spaced out as you can see above.

brendon commented 4 years ago

@janklimo, that's a good way to get set up. In hindsight, one could probably do something like:

property.update(sort_order_position: :last) for each item in order and that would append each to the bottom of the list.