Fix #4901: Duplicate disapprovals
* Add uniqueness constraint on post_disapprovals (user_id, post_id). * Add fix script to remove existing duplicates.
This commit is contained in:
@@ -145,6 +145,29 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
def update!(*args)
|
||||
all.each { |record| record.update!(*args) }
|
||||
end
|
||||
|
||||
def each_duplicate(*columns)
|
||||
return enum_for(:each_duplicate, *columns) unless block_given?
|
||||
|
||||
group(columns).having("count(*) > 1").count.each do |values, count|
|
||||
hash = columns.zip(Array.wrap(values)).to_h
|
||||
yield count: count, **hash
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_duplicates!(*columns, log: true)
|
||||
each_duplicate(*columns) do |count:, **columns_with_values|
|
||||
records = where(columns_with_values).order(:id)
|
||||
dupes = records.drop(1)
|
||||
|
||||
if log
|
||||
data = { keep: records.first.id, destroy: dupes.map(&:id), count: count, **columns_with_values }
|
||||
DanbooruLogger.info("Destroying duplicate #{self.name} #{dupes.map(&:id).join(", ")}", data)
|
||||
end
|
||||
|
||||
dupes.each(&:destroy!)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddUniqueUserIdAndPostIdIndexToPostDisapprovals < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_index :post_disapprovals, [:user_id, :post_id], unique: true
|
||||
end
|
||||
end
|
||||
@@ -4096,6 +4096,13 @@ CREATE INDEX index_post_disapprovals_on_post_id ON public.post_disapprovals USIN
|
||||
CREATE INDEX index_post_disapprovals_on_user_id ON public.post_disapprovals USING btree (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_post_disapprovals_on_user_id_and_post_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE UNIQUE INDEX index_post_disapprovals_on_user_id_and_post_id ON public.post_disapprovals USING btree (user_id, post_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_post_flags_on_creator_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
@@ -5060,6 +5067,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20210926125826'),
|
||||
('20211008091234'),
|
||||
('20211010181657'),
|
||||
('20211011044400');
|
||||
('20211011044400'),
|
||||
('20211013011619');
|
||||
|
||||
|
||||
|
||||
7
script/fixes/081_delete_duplicate_post_disapprovals.rb
Executable file
7
script/fixes/081_delete_duplicate_post_disapprovals.rb
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require_relative "../../config/environment"
|
||||
|
||||
PostDisapproval.transaction do
|
||||
PostDisapproval.destroy_duplicates!(:user_id, :post_id)
|
||||
end
|
||||
@@ -45,4 +45,20 @@ class ApplicationRecordTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "ApplicationRecord#destroy_duplicates!" do
|
||||
should "destroy all duplicates" do
|
||||
@post1 = create(:post, score: 42)
|
||||
@post2 = create(:post, score: 42)
|
||||
@post3 = create(:post, score: 42)
|
||||
@post4 = create(:post, score: 23)
|
||||
|
||||
Post.destroy_duplicates!(:score)
|
||||
|
||||
assert_equal(true, Post.exists?(@post1.id))
|
||||
assert_equal(false, Post.exists?(@post2.id))
|
||||
assert_equal(false, Post.exists?(@post3.id))
|
||||
assert_equal(true, Post.exists?(@post4.id))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user