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:
parent
6f54fffe82
commit
62696b9ee7
|
@ -8,6 +8,7 @@ class TopicsFilter
|
||||||
|
|
||||||
def filter_from_query_string(query_string)
|
def filter_from_query_string(query_string)
|
||||||
return @scope if query_string.blank?
|
return @scope if query_string.blank?
|
||||||
|
category_or_clause = false
|
||||||
|
|
||||||
query_string.scan(
|
query_string.scan(
|
||||||
/(?<key_prefix>[-=])?(?<key>\w+):(?<value>[^\s]+)/,
|
/(?<key_prefix>[-=])?(?<key>\w+):(?<value>[^\s]+)/,
|
||||||
|
@ -45,7 +46,10 @@ class TopicsFilter
|
||||||
filter_categories(
|
filter_categories(
|
||||||
category_slugs: category_slugs.split(delimiter),
|
category_slugs: category_slugs.split(delimiter),
|
||||||
exclude_subcategories: key_prefix.presence,
|
exclude_subcategories: key_prefix.presence,
|
||||||
|
or_clause: category_or_clause,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
category_or_clause = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -80,7 +84,7 @@ class TopicsFilter
|
||||||
|
|
||||||
private
|
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 = Category.ids_from_slugs(category_slugs)
|
||||||
|
|
||||||
category_ids =
|
category_ids =
|
||||||
|
@ -96,7 +100,11 @@ class TopicsFilter
|
||||||
category_ids = category_ids.flat_map { |category_id| Category.subcategory_ids(category_id) }
|
category_ids = category_ids.flat_map { |category_id| Category.subcategory_ids(category_id) }
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def filter_tags(tag_names:, match_all: true, exclude: false)
|
def filter_tags(tag_names:, match_all: true, exclude: false)
|
||||||
|
|
|
@ -138,6 +138,37 @@ RSpec.describe TopicsFilter do
|
||||||
end
|
end
|
||||||
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
|
describe "when query string is `=category:category`" do
|
||||||
it "should not return topics from subcategories`" do
|
it "should not return topics from subcategories`" do
|
||||||
expect(
|
expect(
|
||||||
|
|
Loading…
Reference in New Issue