PERF: topic_view participant post count: don't send back ID list (#10210)

PERF: topic_view participant post count: don’t send back ID list (#10210)

On large topics, the cost of sending the entire post ID list back over to the database is signficant. Just have the DB recalculate the list of visible posts instead.

diff --git a/lib/guardian/topic_guardian.rb b/lib/guardian/topic_guardian.rb
index 842f44c..387e7c8 100644
--- a/lib/guardian/topic_guardian.rb
+++ b/lib/guardian/topic_guardian.rb
@@ -31,6 +31,10 @@ module TopicGuardian
     is_staff? && SiteSetting.enable_whispers?
   end
 
+  def can_see_whispers?(_topic)
+    is_staff?
+  end
+
   def can_publish_topic?(topic, category)
     is_staff? && can_see?(topic) && can_create_topic?(category)
   end
diff --git a/lib/topic_view.rb b/lib/topic_view.rb
index aa0c46a..81b4449 100644
--- a/lib/topic_view.rb
+++ b/lib/topic_view.rb
@@ -366,21 +366,24 @@ class TopicView
       if is_mega_topic?
         {}
       else
-        post_ids = unfiltered_post_ids
-
-        return {} if post_ids.blank?
+        post_types = [Post.types[:regular], Post.types[:moderator_action]]
+        if @guardian.can_see_whispers?(@topic)
+          post_types << Post.types[:whisper]
+        end
 
         sql = <<~SQL
             SELECT user_id, count(*) AS count_all
               FROM posts
-             WHERE id in (:post_ids)
+             WHERE topic_id = :topic_id
+               AND post_type IN (:post_types)
                AND user_id IS NOT NULL
+               AND posts.deleted_at IS NULL
           GROUP BY user_id
           ORDER BY count_all DESC
              LIMIT #{MAX_PARTICIPANTS}
         SQL
 
-        Hash[*DB.query_single(sql, post_ids: post_ids)]
+        Hash[*DB.query_single(sql, topic_id: @topic.id, post_types: post_types)]
       end
     end
   end
diff --git a/spec/components/topic_view_spec.rb b/spec/components/topic_view_spec.rb
index 624e9a3..0d51b1e 100644
--- a/spec/components/topic_view_spec.rb
+++ b/spec/components/topic_view_spec.rb
@@ -287,7 +287,7 @@ describe TopicView do
       it 'returns the two posters with their appropriate counts' do
         Fabricate(:post, topic: topic, user: evil_trout, post_type: Post.types[:whisper])
 
-        expect(topic_view.post_counts_by_user.to_a).to match_array([[first_poster.id, 2], [evil_trout.id, 2]])
+        expect(TopicView.new(topic.id, admin).post_counts_by_user.to_a).to match_array([[first_poster.id, 2], [evil_trout.id, 2]])
 
         expect(TopicView.new(topic.id, first_poster).post_counts_by_user.to_a).to match_array([[first_poster.id, 2], [evil_trout.id, 1]])
       end

GitHub sha: 8ddd45d5

This commit appears in #10210 which was merged by riking.