/moderator/dashboard: optimize sql queries

* Converts queries to use active record instead of raw sql. This ensures
  that user objects are loaded by rails in the join, so that we
  don't have to issue `User.find` calls to load users one-by-one.

* Use `.includes` to preload associations used in the view, to avoid
  additional N+1 query problems (primarily, calls to link_to_user
  also causing users to be loaded one-by-one).
This commit is contained in:
evazion
2017-03-18 18:21:13 -05:00
parent b3e3012a9c
commit d1d0fe9bc5
10 changed files with 62 additions and 144 deletions

View File

@@ -1,28 +1,16 @@
module Moderator module Moderator
module Dashboard module Dashboard
module Queries module Queries
class Artist class Artist < ::Struct.new(:user, :count)
attr_reader :user, :count
def self.all(min_date, max_level) def self.all(min_date, max_level)
sql = <<-EOS ::ArtistVersion.joins(:updater)
SELECT artist_versions.updater_id AS updater_id, count(*) .where("artist_versions.created_at > ?", min_date)
FROM artist_versions .where("users.level <= ?", max_level)
JOIN users ON users.id = artist_versions.updater_id .group(:updater)
WHERE .order("count(*) desc")
artist_versions.created_at > ? .limit(10)
AND users.level <= ? .count
GROUP BY artist_versions.updater_id .map { |user, count| new(user, count) }
ORDER BY count(*) DESC
LIMIT 10
EOS
ActiveRecord::Base.select_all_sql(sql, min_date, max_level).map {|x| new(x)}
end
def initialize(hash)
@user = ::User.find(hash["updater_id"])
@count = hash["count"]
end end
end end
end end

View File

@@ -1,31 +1,18 @@
module Moderator module Moderator
module Dashboard module Dashboard
module Queries module Queries
class Comment class Comment < ::Struct.new(:comment, :count)
attr_reader :comment, :count
def self.all(min_date, max_level) def self.all(min_date, max_level)
sql = <<-EOS ::CommentVote.joins(comment: [:creator])
SELECT comment_votes.comment_id, count(*) .where("comments.score < 0")
FROM comment_votes .where("comment_votes.created_at > ?", min_date)
JOIN comments ON comments.id = comment_id .where("users.level <= ?", max_level)
JOIN users ON users.id = comments.creator_id .group(:comment)
WHERE .having("count(*) >= 3")
comment_votes.created_at > ? .order("count(*) desc")
AND comments.score < 0 .limit(10)
AND users.level <= ? .count
GROUP BY comment_votes.comment_id .map { |comment, count| new(comment, count) }
HAVING count(*) >= 3
ORDER BY count(*) DESC
LIMIT 10
EOS
ActiveRecord::Base.select_all_sql(sql, min_date, max_level).map {|x| new(x)}
end
def initialize(hash)
@comment = ::Comment.find(hash["comment_id"])
@count = hash["count"]
end end
end end
end end

View File

@@ -3,7 +3,7 @@ module Moderator
module Queries module Queries
class ModAction class ModAction
def self.all def self.all
::ModAction.order("id desc").limit(10) ::ModAction.includes(:creator).order("id desc").limit(10)
end end
end end
end end

View File

@@ -1,28 +1,16 @@
module Moderator module Moderator
module Dashboard module Dashboard
module Queries module Queries
class Note class Note < ::Struct.new(:user, :count)
attr_reader :user, :count
def self.all(min_date, max_level) def self.all(min_date, max_level)
sql = <<-EOS ::NoteVersion.joins(:updater)
SELECT note_versions.updater_id, count(*) .where("note_versions.created_at > ?", min_date)
FROM note_versions .where("users.level <= ?", max_level)
JOIN users ON users.id = note_versions.updater_id .group(:updater)
WHERE .order("count(*) desc")
note_versions.created_at > ? .limit(10)
AND users.level <= ? .count
GROUP BY note_versions.updater_id .map { |user, count| new(user, count) }
ORDER BY count(*) DESC
LIMIT 10
EOS
ActiveRecord::Base.select_all_sql(sql, min_date, max_level).map {|x| new(x)}
end
def initialize(hash)
@user = ::User.find(hash["updater_id"])
@count = hash["count"]
end end
end end
end end

View File

@@ -2,28 +2,13 @@ module Moderator
module Dashboard module Dashboard
module Queries module Queries
class PostAppeal class PostAppeal
attr_reader :post, :count
def self.all(min_date) def self.all(min_date)
sql = <<-EOS ::Post.joins(:appeals).includes(:uploader, :flags, appeals: [:creator])
SELECT post_appeals.post_id, count(*) .deleted
FROM post_appeals .where("post_appeals.created_at > ?", min_date)
JOIN posts ON posts.id = post_appeals.post_id .group(:id)
WHERE .order("count(*) desc")
post_appeals.created_at > ? .limit(10)
and posts.is_deleted = true
and posts.is_pending = false
GROUP BY post_appeals.post_id
ORDER BY count(*) DESC
LIMIT 10
EOS
ActiveRecord::Base.select_all_sql(sql, min_date).map {|x| new(x)}
end
def initialize(hash)
@post = ::Post.find(hash["post_id"])
@count = hash["count"]
end end
end end
end end

View File

@@ -1,9 +1,7 @@
module Moderator module Moderator
module Dashboard module Dashboard
module Queries module Queries
class Tag class Tag < ::Struct.new(:user, :count)
attr_reader :user, :count
def self.all(min_date, max_level) def self.all(min_date, max_level)
return [] unless PostArchive.enabled? return [] unless PostArchive.enabled?
@@ -13,10 +11,6 @@ module Moderator
records.select { |rec| rec.user.level <= max_level }.sort_by(&:count).reverse.take(10) records.select { |rec| rec.user.level <= max_level }.sort_by(&:count).reverse.take(10)
end end
def initialize(user, count)
@user, @count = user, count
end
end end
end end
end end

View File

@@ -1,28 +1,16 @@
module Moderator module Moderator
module Dashboard module Dashboard
module Queries module Queries
class Upload class Upload < ::Struct.new(:user, :count)
attr_reader :user, :count
def self.all(min_date, max_level) def self.all(min_date, max_level)
sql = <<-EOS ::Post.joins(:uploader)
select uploader_id, count(*) .where("posts.created_at > ?", min_date)
from posts .where("users.level <= ?", max_level)
join users on uploader_id = users.id .group(:uploader)
where .order("count(*) desc")
posts.created_at > ? .limit(10)
and level <= ? .count
group by posts.uploader_id .map { |user, count| new(user, count) }
order by count(*) desc
limit 10
EOS
ActiveRecord::Base.select_all_sql(sql, min_date, max_level).map {|x| new(x)}
end
def initialize(hash)
@user = ::User.find(hash["uploader_id"])
@count = hash["count"]
end end
end end
end end

View File

@@ -3,7 +3,7 @@ module Moderator
module Queries module Queries
class UserFeedback class UserFeedback
def self.all def self.all
::UserFeedback.order("id desc").limit(10) ::UserFeedback.includes(:user).order("id desc").limit(10)
end end
end end
end end

View File

@@ -1,28 +1,16 @@
module Moderator module Moderator
module Dashboard module Dashboard
module Queries module Queries
class WikiPage class WikiPage < ::Struct.new(:user, :count)
attr_reader :user, :count
def self.all(min_date, max_level) def self.all(min_date, max_level)
sql = <<-EOS ::WikiPageVersion.joins(:updater)
SELECT wiki_page_versions.updater_id, count(*) .where("wiki_page_versions.created_at > ?", min_date)
FROM wiki_page_versions .where("users.level <= ?", max_level)
JOIN users ON users.id = wiki_page_versions.updater_id .group(:updater)
WHERE .order("count(*) desc")
wiki_page_versions.created_at > ? .limit(10)
AND users.level <= ? .count
GROUP BY wiki_page_versions.updater_id .map { |user, count| new(user, count) }
ORDER BY count(*) DESC
LIMIT 10
EOS
ActiveRecord::Base.select_all_sql(sql, min_date, max_level).map {|x| new(x)}
end
def initialize(hash)
@user = ::User.find(hash["updater_id"])
@count = hash["count"]
end end
end end
end end

View File

@@ -10,13 +10,13 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<% @dashboard.appeals.each do |appeal| %> <% @dashboard.appeals.each do |post| %>
<tr> <tr>
<td><%= link_to image_tag(appeal.post.preview_file_url), post_path(appeal.post) %></td> <td><%= link_to image_tag(post.preview_file_url), post_path(post) %></td>
<td><%= mod_link_to_user appeal.post.uploader, :negative %></td> <td><%= mod_link_to_user post.uploader, :negative %></td>
<td><%= post_flag_reasons(appeal.post) %></td> <td><%= post_flag_reasons(post) %></td>
<td><%= post_appeal_reasons(appeal.post) %></td> <td><%= post_appeal_reasons(post) %></td>
<td><%= appeal.post.score %></td> <td><%= post.score %></td>
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>