PERF: Use mini_sql for the query

On sites with many flages, it could take quite a long time for
ActiveRecord to return all the joined data.

It's now 3 queries instead of one, but significantly faster, especially
if you have a minimum threshold set.
This commit is contained in:
Robin Ward 2018-11-05 14:43:33 -05:00
parent afbdf9c2d2
commit 71460fc203
1 changed files with 42 additions and 33 deletions

View File

@ -202,52 +202,61 @@ module FlagQuery
end end
def self.flagged_topics def self.flagged_topics
results = DB.query(<<~SQL)
results = PostAction SELECT pa.post_action_type_id,
.flags pa.post_id,
.active p.topic_id,
.includes(post: [{ user: :user_stat }, :topic]) pa.created_at AS last_flag_at,
.references(:post) p.user_id
.where("posts.user_id > 0") FROM post_actions AS pa
.order('post_actions.created_at DESC') INNER JOIN posts AS p ON pa.post_id = p.id
INNER JOIN topics AS t ON t.id = p.topic_id
WHERE pa.post_action_type_id IN (#{PostActionType.notify_flag_type_ids.join(',')})
AND pa.disagreed_at IS NULL
AND pa.deferred_at IS NULL
AND pa.agreed_at IS NULL
AND pa.deleted_at IS NULL
AND p.user_id > 0
AND p.deleted_at IS NULL
AND t.deleted_at IS NULL
ORDER BY pa.created_at DESC
SQL
ft_by_id = {} ft_by_id = {}
users_by_id = {}
topics_by_id = {}
counts_by_post = {} counts_by_post = {}
user_ids = Set.new
results.each do |pa| results.each do |pa|
if pa.post.present? && pa.post.topic.present?
topic_id = pa.post.topic.id
ft = ft_by_id[topic_id] ||= OpenStruct.new( ft = ft_by_id[pa.topic_id] ||= OpenStruct.new(
topic: pa.post.topic, topic_id: pa.topic_id,
flag_counts: {}, flag_counts: {},
user_ids: [], user_ids: Set.new,
last_flag_at: pa.created_at, last_flag_at: pa.last_flag_at,
meets_minimum: false meets_minimum: false
) )
counts_by_post[pa.post.id] ||= 0 counts_by_post[pa.post_id] ||= 0
sum = counts_by_post[pa.post.id] += 1 sum = counts_by_post[pa.post_id] += 1
ft.meets_minimum = true if sum >= SiteSetting.min_flags_staff_visibility ft.meets_minimum = true if sum >= SiteSetting.min_flags_staff_visibility
topics_by_id[topic_id] = pa.post.topic ft.flag_counts[pa.post_action_type_id] ||= 0
ft.flag_counts[pa.post_action_type_id] ||= 0 ft.flag_counts[pa.post_action_type_id] += 1
ft.flag_counts[pa.post_action_type_id] += 1
ft.user_ids << pa.post.user_id ft.user_ids << pa.user_id
ft.user_ids.uniq! user_ids << pa.user_id
users_by_id[pa.post.user_id] ||= pa.post.user
end
end end
all_topics = Topic.where(id: ft_by_id.keys).to_a
all_topics.each { |t| ft_by_id[t.id].topic = t }
flagged_topics = ft_by_id.values.select { |ft| ft.meets_minimum } flagged_topics = ft_by_id.values.select { |ft| ft.meets_minimum }
Topic.preload_custom_fields(all_topics, TopicList.preloaded_custom_fields)
Topic.preload_custom_fields(topics_by_id.values, TopicList.preloaded_custom_fields) {
flagged_topics: flagged_topics,
{ flagged_topics: flagged_topics, users: users_by_id.values } users: User.where(id: user_ids)
}
end end
private private