DEV: Properly support composing multiple category filters on `/filter` (#20953)

Before this commit, composing multiple category filters with a query such as category:category1 and category:category2 would not return any results. This is because we were filtering for topics that belonged to both category1 and category2, which is impossible since a topic can only belong to a single category.

With this commit, specifying a query like category:category1 category:category2 will now translate to filtering for topics that belong to either the category1 or category2 category.
This commit is contained in:
Alan Guo Xiang Tan 2023-04-05 07:16:37 +08:00 committed by GitHub
parent 6f54fffe82
commit 62696b9ee7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 2 deletions

View File

@ -8,6 +8,7 @@ class TopicsFilter
def filter_from_query_string(query_string)
return @scope if query_string.blank?
category_or_clause = false
query_string.scan(
/(?<key_prefix>[-=])?(?<key>\w+):(?<value>[^\s]+)/,
@ -45,7 +46,10 @@ class TopicsFilter
filter_categories(
category_slugs: category_slugs.split(delimiter),
exclude_subcategories: key_prefix.presence,
or_clause: category_or_clause,
)
category_or_clause = true
end
end
end
@ -80,7 +84,7 @@ class TopicsFilter
private
def filter_categories(category_slugs:, exclude_subcategories: false)
def filter_categories(category_slugs:, exclude_subcategories: false, or_clause: false)
category_ids = Category.ids_from_slugs(category_slugs)
category_ids =
@ -96,7 +100,11 @@ class TopicsFilter
category_ids = category_ids.flat_map { |category_id| Category.subcategory_ids(category_id) }
end
@scope = @scope.joins(:category).where("categories.id IN (?)", category_ids)
if or_clause
@scope.or(Topic.where("categories.id IN (?)", category_ids))
else
@scope.joins(:category).where("categories.id IN (?)", category_ids)
end
end
def filter_tags(tag_names:, match_all: true, exclude: false)

View File

@ -138,6 +138,37 @@ RSpec.describe TopicsFilter do
end
end
describe "when query string is `category:category category:category2`" do
it "should return topics from any of the specified categories and its subcategories" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("category:category category:category2")
.pluck(:id),
).to contain_exactly(
topic_in_category.id,
topic_in_category_subcategory.id,
topic_in_category2.id,
topic_in_category2_subcategory.id,
)
end
end
describe "when query string is `category:category =category:category2`" do
it "should return topics and subcategory topics from category but only topics from category2" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("category:category =category:category2")
.pluck(:id),
).to contain_exactly(
topic_in_category.id,
topic_in_category_subcategory.id,
topic_in_category2.id,
)
end
end
describe "when query string is `=category:category`" do
it "should not return topics from subcategories`" do
expect(