REFACTOR: Use less queries when moving posts

This commit is contained in:
Gerhard Schlager 2019-08-01 17:47:19 +02:00
parent f57fdee2f6
commit 4113b57cfe

View File

@ -63,6 +63,7 @@ class PostMover
moving_all_posts = (@original_topic.posts.pluck(:id).sort == @post_ids.sort) moving_all_posts = (@original_topic.posts.pluck(:id).sort == @post_ids.sort)
create_temp_table
move_each_post move_each_post
notify_users_that_posts_have_moved notify_users_that_posts_have_moved
update_statistics update_statistics
@ -75,6 +76,26 @@ class PostMover
destination_topic.reload destination_topic.reload
destination_topic destination_topic
ensure
drop_temp_table
end
def create_temp_table
DB.exec <<~SQL
CREATE TEMPORARY TABLE moved_posts (
old_topic_id INTEGER,
old_post_id INTEGER,
old_post_number INTEGER,
new_topic_id INTEGER,
new_topic_title VARCHAR,
new_post_id INTEGER,
new_post_number INTEGER
)
SQL
end
def drop_temp_table
DB.exec("DROP TABLE IF EXISTS moved_posts")
end end
def move_each_post def move_each_post
@ -95,29 +116,23 @@ class PostMover
end end
posts.each do |post| posts.each do |post|
post.is_first_post? ? create_first_post(post) : move(post) metadata = movement_metadata(post)
new_post = post.is_first_post? ? create_first_post(post) : move(post)
store_movement(metadata, new_post)
if @move_to_pm && !destination_topic.topic_allowed_users.exists?(user_id: post.user_id) if @move_to_pm && !destination_topic.topic_allowed_users.exists?(user_id: post.user_id)
destination_topic.topic_allowed_users.create!(user_id: post.user_id) destination_topic.topic_allowed_users.create!(user_id: post.user_id)
end end
end end
PostReply.where("reply_id IN (:post_ids) OR post_id IN (:post_ids)", post_ids: post_ids).each do |post_reply| move_incoming_emails
if post_reply.post && post_reply.reply && post_reply.reply.topic_id != post_reply.post.topic_id move_notifications
Post update_reply_counts
.where("id = ? AND reply_count > 0", post_reply.post.id) delete_post_replies
.update_all("reply_count = reply_count - 1")
PostReply
.where(reply_id: post_reply.reply.id, post_id: post_reply.post.id)
.delete_all
end
end
end end
def create_first_post(post) def create_first_post(post)
old_post_attributes = post_attributes(post)
@post_creator = PostCreator.new( @post_creator = PostCreator.new(
post.user, post.user,
raw: post.raw, raw: post.raw,
@ -133,9 +148,7 @@ class PostMover
) )
new_post = @post_creator.create new_post = @post_creator.create
move_incoming_emails(post, new_post)
move_email_logs(post, new_post) move_email_logs(post, new_post)
move_notifications(old_post_attributes, new_post)
PostAction.copy(post, new_post) PostAction.copy(post, new_post)
new_post.update_column(:reply_count, @reply_count[1] || 0) new_post.update_column(:reply_count, @reply_count[1] || 0)
@ -162,25 +175,45 @@ class PostMover
update[:reply_to_user_id] = nil update[:reply_to_user_id] = nil
end end
old_post_attributes = post_attributes(post)
post.attributes = update post.attributes = update
post.save(validate: false) post.save(validate: false)
move_incoming_emails(post, post)
move_notifications(old_post_attributes, post)
DiscourseEvent.trigger(:post_moved, post, original_topic.id) DiscourseEvent.trigger(:post_moved, post, original_topic.id)
# Move any links from the post to the new topic # Move any links from the post to the new topic
post.topic_links.update_all(topic_id: destination_topic.id) post.topic_links.update_all(topic_id: destination_topic.id)
post
end end
def move_incoming_emails(old_post, new_post) def movement_metadata(post)
return if old_post.incoming_email.nil? {
old_topic_id: post.topic_id,
old_post_id: post.id,
old_post_number: post.post_number,
new_topic_id: destination_topic.id,
new_post_number: @move_map[post.post_number],
new_topic_title: destination_topic.title
}
end
email = old_post.incoming_email def store_movement(metadata, new_post)
email.update_columns(topic_id: new_post.topic_id, post_id: new_post.id) metadata[:new_post_id] = new_post.id
new_post.incoming_email = email
DB.exec(<<~SQL, metadata)
INSERT INTO moved_posts(old_topic_id, old_post_id, old_post_number, new_topic_id, new_topic_title, new_post_id, new_post_number)
VALUES (:old_topic_id, :old_post_id, :old_post_number, :new_topic_id, :new_topic_title, :new_post_id, :new_post_number)
SQL
end
def move_incoming_emails
DB.exec <<~SQL
UPDATE incoming_emails ie
SET topic_id = mp.new_topic_id,
post_id = mp.new_post_id
FROM moved_posts mp
WHERE ie.topic_id = mp.old_topic_id AND ie.post_id = mp.old_post_id
SQL
end end
def move_email_logs(old_post, new_post) def move_email_logs(old_post, new_post)
@ -189,28 +222,44 @@ class PostMover
.update_all(post_id: new_post.id) .update_all(post_id: new_post.id)
end end
def move_notifications(old_post_attributes, new_post) def move_notifications
params = { DB.exec <<~SQL
old_topic_id: old_post_attributes[:topic_id], UPDATE notifications n
old_post_number: old_post_attributes[:post_number], SET topic_id = mp.new_topic_id,
new_topic_id: new_post.topic_id, post_number = mp.new_post_number,
new_post_number: new_post.post_number,
new_topic_title: new_post.topic.title
}
DB.exec(<<~SQL, params)
UPDATE notifications
SET topic_id = :new_topic_id,
post_number = :new_post_number,
data = (data :: JSONB || data = (data :: JSONB ||
jsonb_strip_nulls( jsonb_strip_nulls(
jsonb_build_object( jsonb_build_object(
'topic_title', CASE WHEN data :: JSONB ->> 'topic_title' IS NULL 'topic_title', CASE WHEN data :: JSONB ->> 'topic_title' IS NULL
THEN NULL THEN NULL
ELSE :new_topic_title END ELSE mp.new_topic_title END
) )
)) :: JSON )) :: JSON
WHERE topic_id = :old_topic_id AND post_number = :old_post_number FROM moved_posts mp
WHERE n.topic_id = mp.old_topic_id AND n.post_number = mp.old_post_number
SQL
end
def update_reply_counts
DB.exec <<~SQL
UPDATE posts p
SET reply_count = GREATEST(0, reply_count - x.moved_reply_count)
FROM (
SELECT r.post_id, mp.new_topic_id, COUNT(1) AS moved_reply_count
FROM moved_posts mp
JOIN post_replies r ON (mp.old_post_id = r.reply_id)
GROUP BY r.post_id, mp.new_topic_id
) x
WHERE x.post_id = p.id AND x.new_topic_id <> p.topic_id
SQL
end
def delete_post_replies
DB.exec <<~SQL
DELETE
FROM post_replies pr USING moved_posts mp, posts p, posts r
WHERE (pr.reply_id = mp.old_post_id OR pr.post_id = mp.old_post_id) AND
p.id = pr.post_id AND r.id = pr.reply_id AND p.topic_id <> r.topic_id
SQL SQL
end end
@ -316,13 +365,6 @@ class PostMover
destination_topic.save! destination_topic.save!
end end
def post_attributes(post)
{
topic_id: post.topic_id,
post_number: post.post_number
}
end
def enqueue_jobs(topic) def enqueue_jobs(topic)
@post_creator.enqueue_jobs if @post_creator @post_creator.enqueue_jobs if @post_creator