From b5752c2132b7568218a17232f4ca51d60c9b7929 Mon Sep 17 00:00:00 2001 From: Caio <117518+caiosba@users.noreply.github.com> Date: Fri, 22 Nov 2024 22:02:58 -0300 Subject: [PATCH] Ticket CV2-5653 [WIP]: Adding migration and rake task to delete duplicate relationships --- ...add_unique_index_to_relationships_table.rb | 33 +++++++++++++++++++ db/schema.rb | 4 +-- ...030351_delete_duplicate_relationships.rake | 10 +++--- 3 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20241123001206_add_unique_index_to_relationships_table.rb diff --git a/db/migrate/20241123001206_add_unique_index_to_relationships_table.rb b/db/migrate/20241123001206_add_unique_index_to_relationships_table.rb new file mode 100644 index 0000000000..28e1700050 --- /dev/null +++ b/db/migrate/20241123001206_add_unique_index_to_relationships_table.rb @@ -0,0 +1,33 @@ +class AddUniqueIndexToRelationshipsTable < ActiveRecord::Migration[6.1] + def change + # The code below is a copy-paste from the rake task lib/tasks/migrate/20230216030351_delete_duplicate_relationships.rake + # and it's responsible for deleting any remaining duplicate relationship, but first, before running this migration, be + # sure to run the rake task above, with: rake check:migrate:delete_duplicate_relationships + duplicates = Relationship.group(:target_id).having('COUNT(id) > 1').count + n = duplicates.size + i = 0 + duplicates.each do |pm_id, count| + i += 1 + puts "[#{Time.now}] #{i}/#{n}" + if count > 1 + relationships = Relationship.where(target_id: pm_id).order('id ASC').to_a + # Keep the confirmed relationship, or the one whose model is image, video or audio... if none, keep the first one + keep = relationships.find{ |r| r.relationship_type == Relationship.confirmed_type } || relationships.find{ |r| ['image', 'video', 'audio'].include?(r.model) } || relationships.first + raise "No relationship to keep for target_id #{pm_id}!" if keep.nil? + relationships.each do |relationship| + if relationship.id == keep.id + puts " Keeping relationship ##{r.id}" + else + puts " Deleting relationship ##{r.id}" + relationship.destroy! + end + relationship.source.clear_cached_fields + relationship.target.clear_cached_fields + end + end + end + + remove_index :relationships, name: 'index_relationships_on_target_id' + add_index :relationships, :target_id, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index dfb106d588..bc95642b32 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2024_10_15_223059) do +ActiveRecord::Schema.define(version: 2024_11_23_001206) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -596,7 +596,7 @@ t.index ["source_id", "target_id", "relationship_type"], name: "relationship_index", unique: true t.index ["source_id"], name: "index_relationships_on_source_id" t.index ["target_id", "relationship_type"], name: "index_relationships_on_target_id_and_relationship_type" - t.index ["target_id"], name: "index_relationships_on_target_id" + t.index ["target_id"], name: "index_relationships_on_target_id", unique: true t.check_constraint "source_id <> target_id", name: "source_target_must_be_different" end diff --git a/lib/tasks/migrate/20230216030351_delete_duplicate_relationships.rake b/lib/tasks/migrate/20230216030351_delete_duplicate_relationships.rake index 77c7aff648..593a2ff2d7 100644 --- a/lib/tasks/migrate/20230216030351_delete_duplicate_relationships.rake +++ b/lib/tasks/migrate/20230216030351_delete_duplicate_relationships.rake @@ -8,10 +8,10 @@ namespace :check do i += 1 puts "[#{Time.now}] #{i}/#{n}" if count > 1 - relationships = Relationship.where(target_id: pm_id).to_a - # Keep the relationship whose model is image, video or audio... if none, keep the first one - keep = relationships.find{ |r| ['image', 'video', 'audio'].include?(r.model) } || relationships.first - raise "No relationship to keeo for target_id #{pm_id}!" if keep.nil? + relationships = Relationship.where(target_id: pm_id).order('id ASC').to_a + # Keep the confirmed relationship, or the one whose model is image, video or audio... if none, keep the first one + keep = relationships.find{ |r| r.relationship_type == Relationship.confirmed_type } || relationships.find{ |r| ['image', 'video', 'audio'].include?(r.model) } || relationships.first + raise "No relationship to keep for target_id #{pm_id}!" if keep.nil? relationships.each do |relationship| if relationship.id == keep.id puts " Keeping relationship ##{r.id}" @@ -19,6 +19,8 @@ namespace :check do puts " Deleting relationship ##{r.id}" relationship.destroy! end + relationship.source.clear_cached_fields + relationship.target.clear_cached_fields end end end