PERF: Optimize topic query for many categories (#20743)

Sites with many categories and many of them in muted by default (see
`default_categories_muted`) reported bad performance when requesting
the homepage as an anonymous user. This was the case because of the
long query that iterated over topics and categories trying to remove
those from the muted categories.
This commit is contained in:
Bianca Nenciu 2023-03-22 23:31:33 +02:00 committed by GitHub
parent 32aa821f12
commit fb3c610f09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 16 additions and 14 deletions

View File

@ -288,20 +288,18 @@ module TopicGuardian
topic&.access_topic_via_group.present? && authenticated?
end
def filter_allowed_categories(records)
def filter_allowed_categories(records, category_id_column: "topics.category_id")
return records if is_admin? && !SiteSetting.suppress_secured_categories_from_admin
records =
(
if allowed_category_ids.size == 0
records.where("topics.category_id IS NULL")
else
records.where(
"topics.category_id IS NULL or topics.category_id IN (?)",
allowed_category_ids,
)
end
)
if allowed_category_ids.size == 0
records.where("#{category_id_column} IS NULL")
else
records.where(
"#{category_id_column} IS NULL or #{category_id_column} IN (?)",
allowed_category_ids,
)
end
records.references(:categories)
end

View File

@ -776,7 +776,11 @@ class TopicQuery
result = apply_ordering(result, options)
all_listable_topics = @guardian.filter_allowed_categories(Topic.unscoped.listable_topics)
all_listable_topics =
@guardian.filter_allowed_categories(
Topic.unscoped.listable_topics,
category_id_column: "categories.id",
)
if options[:include_pms] || options[:include_all_pms]
all_pm_topics =
@ -937,12 +941,12 @@ class TopicQuery
].flatten.map(&:to_i)
category_ids << category_id if category_id.present? && category_ids.exclude?(category_id)
list = list.where("topics.category_id IN (?)", category_ids) if category_ids.present?
list = list.where("categories.id IN (?)", category_ids) if category_ids.present?
else
category_ids = SiteSetting.default_categories_muted.split("|").map(&:to_i)
category_ids -= [category_id] if category_id.present? && category_ids.include?(category_id)
list = list.where("topics.category_id NOT IN (?)", category_ids) if category_ids.present?
list = list.where("categories.id NOT IN (?)", category_ids) if category_ids.present?
end
list