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)
|
def update!(*args)
|
||||||
all.each { |record| record.update!(*args) }
|
all.each { |record| record.update!(*args) }
|
||||||
end
|
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
|
||||||
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);
|
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: -
|
-- Name: index_post_flags_on_creator_id; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@@ -5060,6 +5067,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||||||
('20210926125826'),
|
('20210926125826'),
|
||||||
('20211008091234'),
|
('20211008091234'),
|
||||||
('20211010181657'),
|
('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
|
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
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user