Commit eebc6b8badbdc7a85d499ba521a8a7aaad0b738f

Authored by Rodrigo Souto
Committed by Larissa Reis
1 parent 97e657da

tags: migration to fix tags case differences

Whenever you have 2 or more tags with the same name but different cases,
ActsAsTaggableOn returns an empty list of objects tagged with either of
the tags. To solve this problem, we must not have tags with different
cases stored.

Performance was my primal concern on this migration since we have
instances that have over 130k tags registered. So I decided to convert
every tag to lower case.  This is the fastest way I could conceive this
migration and still it might take a lot of time. Here is basic resume of
what it basically does:

  x: number of new downcased tags created.
  y: number of oddcased tags.
  z: number of tags

  1. Find all tags do not have a downcased form already created - [1 fast select query].
  2. Create a downcased version of the above queries - [x slow update queries but
  n is usually low because a minority of tags have odd case and all
  different cases of a single word generate only 1 query].
  3. Update taggings relations based on new ids - [1 slow update and 2 join queries].
  4. Updates the taggings_count of every tag - [1 slow update with z fast selects]
  5. Delete all unused tags tags - [1 slow delete query].

Signed-off-by: Larissa Reis <larissa@colivre.coop.br>
db/migrate/20150423203352_fix_tags_case_differences.rb 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +class FixTagsCaseDifferences < ActiveRecord::Migration
  2 + def up
  3 + tags = ActsAsTaggableOn::Tag.joins('LEFT JOIN tags as b on LOWER(tags.name) = b.name').where('b.id is null')
  4 + tags.find_each do |tag|
  5 + unless ActsAsTaggableOn::Tag.exists?(:name => tag.name.mb_chars.downcase)
  6 + ActsAsTaggableOn::Tag.create(:name => tag.name.mb_chars.downcase)
  7 + end
  8 + end
  9 +
  10 + execute("UPDATE taggings SET tag_id = new.id FROM taggings AS t INNER JOIN tags AS old ON t.tag_id = old.id INNER JOIN tags AS new ON LOWER(old.name) = new.name WHERE old.id != new.id AND taggings.id = t.id")
  11 +
  12 + execute("UPDATE tags SET taggings_count = (SELECT COUNT(*) FROM taggings WHERE taggings.tag_id = tags.id)")
  13 + execute("DELETE FROM tags WHERE taggings_count = 0")
  14 + end
  15 +
  16 + def down
  17 + say 'This migration is irreversible.'
  18 + end
  19 +end
... ...