PERF: Don't load all poll_votes for a poll

PERF: Don’t load all poll_votes for a poll

diff --git a/plugins/poll/app/models/poll.rb b/plugins/poll/app/models/poll.rb
index 44ce17f..2105338 100644
--- a/plugins/poll/app/models/poll.rb
+++ b/plugins/poll/app/models/poll.rb
@@ -55,7 +55,7 @@ class Poll < ActiveRecord::Base
   end
 
   def has_voted?(user)
-    user&.id && poll_votes.any? { |v| v.user_id == user.id }
+    user&.id && poll_votes.where(user_id: user.id).exists?
   end
 
   def can_see_voters?(user)
diff --git a/plugins/poll/app/serializers/poll_serializer.rb b/plugins/poll/app/serializers/poll_serializer.rb
index c540d7b..171eb95 100644
--- a/plugins/poll/app/serializers/poll_serializer.rb
+++ b/plugins/poll/app/serializers/poll_serializer.rb
@@ -45,7 +45,7 @@ class PollSerializer < ApplicationSerializer
   end
 
   def voters
-    object.poll_votes.map { |v| v.user_id }.uniq.count + object.anonymous_voters.to_i
+    object.poll_votes.count('DISTINCT user_id') + object.anonymous_voters.to_i
   end
 
   def close
diff --git a/plugins/poll/plugin.rb b/plugins/poll/plugin.rb
index 1dee522..391610c 100644
--- a/plugins/poll/plugin.rb
+++ b/plugins/poll/plugin.rb
@@ -68,7 +68,7 @@ after_initialize do
             raise StandardError.new I18n.t("poll.user_cant_post_in_topic")
           end
 
-          poll = Poll.includes(poll_options: :poll_votes).find_by(post_id: post_id, name: poll_name)
+          poll = Poll.includes(:poll_options).find_by(post_id: post_id, name: poll_name)
 
           raise StandardError.new I18n.t("poll.no_poll_with_this_name", name: poll_name) unless poll
           raise StandardError.new I18n.t("poll.poll_must_be_open_to_vote") if poll.is_closed?
@@ -91,18 +91,18 @@ after_initialize do
             obj << option.id if options.include?(option.digest)
           end
 
+          old_option_ids = poll.poll_options.each_with_object([]) do |option, obj|
+            if option.poll_votes.where(user_id: user.id).exists?
+              obj << option.id
+            end
+          end
+
           # remove non-selected votes
           PollVote
             .where(poll: poll, user: user)
             .where.not(poll_option_id: new_option_ids)
             .delete_all
 
-          old_option_ids = poll.poll_options.each_with_object([]) do |option, obj|
-            if option.poll_votes.any? { |v| v.user_id == user.id }
-              obj << option.id
-            end
-          end
-
           # create missing votes
           (new_option_ids - old_option_ids).each do |option_id|
             PollVote.create!(poll: poll, user: user, poll_option_id: option_id)
@@ -575,7 +575,6 @@ after_initialize do
 
       if post_with_polls.present?
         Poll
-          .includes(poll_options: :poll_votes, poll_votes: :poll_option)
           .where(post_id: post_with_polls)
           .each do |p|
             polls[p.post_id] ||= []
@@ -591,7 +590,7 @@ after_initialize do
     @preloaded_polls ||= if @topic_view.present?
       @topic_view.polls[object.id]
     else
-      Poll.includes(poll_options: :poll_votes).where(post: object)
+      Poll.includes(:poll_options).where(post: object)
     end
   end
 
@@ -609,11 +608,12 @@ after_initialize do
 
   add_to_serializer(:post, :polls_votes, false) do
     preloaded_polls.map do |poll|
-      user_poll_votes = poll.poll_votes.each_with_object([]) do |vote, obj|
-        if vote.user_id == scope.user.id
-          obj << vote.poll_option.digest
-        end
-      end
+      user_poll_votes =
+        poll
+          .poll_votes
+          .where(user_id: scope.user.id)
+          .joins(:poll_option)
+          .pluck("poll_options.digest")
 
       [poll.name, user_poll_votes]
     end.to_h

GitHub sha: f2842490

1 Like

This commit appears in #10608 which was merged by danielwaterworth.

Small tip is that you can pass conditions to exists? too. poll_votes.exists?(user_id: user.id)

1 Like