From 2a047df4f1bb8aad27dedb79521241b7f770f346 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 19 Mar 2013 16:51:39 -0700 Subject: [PATCH] cache reply_to_user_id in post to eliminate a very nasty n+1 query --- app/models/post.rb | 14 ++++++++------ app/models/user.rb | 5 +++++ ...0130319122248_add_reply_to_user_id_to_post.rb | 16 ++++++++++++++++ lib/topic_view.rb | 8 +++++--- 4 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 db/migrate/20130319122248_add_reply_to_user_id_to_post.rb diff --git a/app/models/post.rb b/app/models/post.rb index 5c6ae352a6e..4408cc4910c 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -19,6 +19,7 @@ class Post < ActiveRecord::Base belongs_to :user belongs_to :topic, counter_cache: :posts_count + belongs_to :reply_to_user, class_name: "User" has_many :post_replies has_many :replies, through: :post_replies @@ -190,12 +191,6 @@ class Post < ActiveRecord::Base (quote_count == 0) && (reply_to_post_number.present?) end - # Get the post that we reply to. - def reply_to_user - return if reply_to_post_number.blank? - Post.where(topic_id: topic_id, post_number: reply_to_post_number).first.try(:user) - end - def reply_notification_target return if reply_to_post_number.blank? Post.where("topic_id = :topic_id AND post_number = :post_number AND user_id <> :user_id", @@ -268,8 +263,14 @@ class Post < ActiveRecord::Base PostRevisor.new(self).revise!(updated_by, new_raw, opts) end + + # TODO: move into PostCreator # Various callbacks before_create do + if reply_to_post_number.present? + self.reply_to_user_id ||= Post.select(:user_id).where(topic_id: topic_id, post_number: reply_to_post_number).first.try(:user_id) + end + self.post_number ||= Topic.next_post_number(topic_id, reply_to_post_number.present?) self.cooked ||= cook(raw, topic_id: topic_id) self.sort_order = post_number @@ -278,6 +279,7 @@ class Post < ActiveRecord::Base end # TODO: Move some of this into an asynchronous job? + # TODO: Move into PostCreator after_create do # Update attributes on the topic - featured users and last posted. attrs = {last_posted_at: created_at, last_post_user_id: user_id} diff --git a/app/models/user.rb b/app/models/user.rb index 4ef35bda161..d9b739d5a22 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -402,6 +402,11 @@ class User < ActiveRecord::Base admin? || TrustLevel.compare(trust_level, level) end + # a touch faster than automatic + def admin? + admin + end + def change_trust_level(level) raise "Invalid trust level #{level}" unless TrustLevel.valid_level?(level) self.trust_level = TrustLevel.levels[level] diff --git a/db/migrate/20130319122248_add_reply_to_user_id_to_post.rb b/db/migrate/20130319122248_add_reply_to_user_id_to_post.rb new file mode 100644 index 00000000000..3f537dfdc92 --- /dev/null +++ b/db/migrate/20130319122248_add_reply_to_user_id_to_post.rb @@ -0,0 +1,16 @@ +class AddReplyToUserIdToPost < ActiveRecord::Migration + def up + # caching this column makes the topic page WAY faster + add_column :posts, :reply_to_user_id, :integer + execute 'UPDATE posts p SET reply_to_user_id = ( + SELECT u.id from users u + JOIN posts p2 ON p2.user_id = u.id AND + p2.post_number = p.reply_to_post_number AND + p2.topic_id = p.topic_id + )' + end + + def down + remove_column :posts, :reply_to_user_id + end +end diff --git a/lib/topic_view.rb b/lib/topic_view.rb index 768554feb87..fc1f07ab39f 100644 --- a/lib/topic_view.rb +++ b/lib/topic_view.rb @@ -34,6 +34,7 @@ class TopicView @all_posts = @posts + filter_posts(options) @draft_key = @topic.draft_key @@ -130,7 +131,8 @@ class TopicView @max = post_number - 1 @posts = @posts.reverse_order.where("post_number < ?", post_number) - @posts = @posts.includes(:topic).joins(:user).limit(SiteSetting.posts_per_page) + + @posts = @posts.includes(:reply_to_user).includes(:topic).joins(:user).limit(SiteSetting.posts_per_page) @min = @max - @posts.size @min = 1 if @min < 1 end @@ -140,7 +142,7 @@ class TopicView @initial_load = false @min = post_number @posts = @posts.regular_order.where("post_number > ?", post_number) - @posts = @posts.includes(:topic).joins(:user).limit(SiteSetting.posts_per_page) + @posts = @posts.includes(:reply_to_user).includes(:topic).joins(:user).limit(SiteSetting.posts_per_page) @max = @min + @posts.size end @@ -275,7 +277,7 @@ class TopicView def filter_posts_in_range(min, max) @min, @max = min, max - @posts = @posts.where("post_number between ? and ?", @min, @max).includes(:user).regular_order + @posts = @posts.where("post_number between ? and ?", @min, @max).includes(:user).includes(:reply_to_user).regular_order end def find_topic(topic_id)