DEV: Add support for more filters for /filter route (#21097)

* DEV: Support `likes-(min:max):<count>` on `/filter` route

This commit adds support for the following filters: 

1. `likes-min` 
2. `likes-max`
3. `views-min`
4. `views-max`
5. `likes-op-min`
6. `likes-op-max`

If the filter has an invalid value, i.e string that cannot be converted
into an integer, the filter will be ignored.

If either of each filter is specify multiple times, only the last
occurrence of each filter will be taken into consideration.
This commit is contained in:
Alan Guo Xiang Tan
2023-04-14 10:21:04 +08:00
committed by GitHub
parent 4b9061ae3f
commit 1f0207ba06
2 changed files with 129 additions and 142 deletions

View File

@@ -847,182 +847,136 @@ RSpec.describe TopicsFilter do
end
end
describe "when filtering by number of posts in a topic" do
fab!(:topic_with_1_post) { Fabricate(:topic, posts_count: 1) }
fab!(:topic_with_2_posts) { Fabricate(:topic, posts_count: 2) }
fab!(:topic_with_3_posts) { Fabricate(:topic, posts_count: 3) }
describe "when query string is `posts-min:1`" do
it "should only return topics with at least 1 post" do
shared_examples "filtering for topics by range" do |filter|
describe "when query string is `#{filter}-min:1`" do
it "should only return topics with at least 1 #{filter}" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posts-min:1")
.filter_from_query_string("#{filter}-min:1")
.pluck(:id),
).to contain_exactly(topic_with_1_post.id, topic_with_2_posts.id, topic_with_3_posts.id)
).to contain_exactly(topic_with_1_count.id, topic_with_2_count.id, topic_with_3_count.id)
end
end
describe "when query string is `posts-min:3`" do
it "should only return topics with at least 3 posts" do
describe "when query string is `#{filter}-min:3`" do
it "should only return topics with at least 3 #{filter}" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posts-min:3")
.filter_from_query_string("#{filter}-min:3")
.pluck(:id),
).to contain_exactly(topic_with_3_posts.id)
).to contain_exactly(topic_with_3_count.id)
end
end
describe "when query string is `posts-max:1`" do
it "should only return topics with at most 1 post" do
describe "when query string is `#{filter}-max:1`" do
it "should only return topics with at most 1 #{filter}" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posts-max:1")
.filter_from_query_string("#{filter}-max:1")
.pluck(:id),
).to contain_exactly(topic_with_1_post.id)
).to contain_exactly(topic_with_1_count.id)
end
end
describe "when query string is `posts-max:3`" do
it "should only return topics with at most 3 posts" do
describe "when query string is `#{filter}-max:3`" do
it "should only return topics with at most 3 #{filter}" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posts-max:3")
.filter_from_query_string("#{filter}-max:3")
.pluck(:id),
).to contain_exactly(topic_with_1_post.id, topic_with_2_posts.id, topic_with_3_posts.id)
).to contain_exactly(topic_with_1_count.id, topic_with_2_count.id, topic_with_3_count.id)
end
end
describe "when query string is `posts-min:1 posts-max:2`" do
it "should only return topics with at least a post and at most 2 posts" do
describe "when query string is `#{filter}-min:1 #{filter}-max:2`" do
it "should only return topics with at least 1 like and at most 2 #{filter}" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posts-min:1 posts-max:2")
.filter_from_query_string("#{filter}-min:1 #{filter}-max:2")
.pluck(:id),
).to contain_exactly(topic_with_1_post.id, topic_with_2_posts.id)
).to contain_exactly(topic_with_1_count.id, topic_with_2_count.id)
end
end
describe "when query string is `posts-min:3 posts-min:2 posts-max:1 posts-max:3`" do
it "should only return topics with at least 2 posts and at most 3 posts as it ignores earlier filters which are duplicated" do
describe "when query string is `#{filter}-min:3 #{filter}-min:2 #{filter}-max:1 #{filter}-max:3`" do
it "should only return topics with at least 2 #{filter} and at most 3 #{filter} as it ignores earlier filters which are duplicated" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posts-min:3 posts-min:2 posts-max:1 posts-max:3")
.filter_from_query_string(
"#{filter}-min:3 #{filter}-min:2 #{filter}-max:1 #{filter}-max:3",
)
.pluck(:id),
).to contain_exactly(topic_with_2_posts.id, topic_with_3_posts.id)
).to contain_exactly(topic_with_2_count.id, topic_with_3_count.id)
end
end
describe "when query string is `posts-min:invalid posts-max:invalid`" do
describe "when query string is `#{filter}-min:invalid #{filter}-max:invalid`" do
it "should ignore the filters with invalid values" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posts-min:invalid posts-max:invalid")
.filter_from_query_string("#{filter}-min:invalid #{filter}-max:invalid")
.pluck(:id),
).to contain_exactly(topic_with_1_post.id, topic_with_2_posts.id, topic_with_3_posts.id)
).to contain_exactly(topic_with_1_count.id, topic_with_2_count.id, topic_with_3_count.id)
end
end
end
describe "when filtering by number of likes in a topic" do
fab!(:topic_with_1_count) { Fabricate(:topic, like_count: 1) }
fab!(:topic_with_2_count) { Fabricate(:topic, like_count: 2) }
fab!(:topic_with_3_count) { Fabricate(:topic, like_count: 3) }
include_examples("filtering for topics by range", "likes")
end
describe "when filtering by number of posters in a topic" do
fab!(:topic_with_1_participant) { Fabricate(:topic, participant_count: 1) }
fab!(:topic_with_2_participants) { Fabricate(:topic, participant_count: 2) }
fab!(:topic_with_3_participants) { Fabricate(:topic, participant_count: 3) }
fab!(:topic_with_1_count) { Fabricate(:topic, participant_count: 1) }
fab!(:topic_with_2_count) { Fabricate(:topic, participant_count: 2) }
fab!(:topic_with_3_count) { Fabricate(:topic, participant_count: 3) }
describe "when query string is `posters-min:1`" do
it "should only return topics with at least 1 participant" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posters-min:1")
.pluck(:id),
).to contain_exactly(
topic_with_1_participant.id,
topic_with_2_participants.id,
topic_with_3_participants.id,
)
end
include_examples("filtering for topics by range", "posters")
end
describe "when filtering by number of posts in a topic" do
fab!(:topic_with_1_count) { Fabricate(:topic, posts_count: 1) }
fab!(:topic_with_2_count) { Fabricate(:topic, posts_count: 2) }
fab!(:topic_with_3_count) { Fabricate(:topic, posts_count: 3) }
include_examples("filtering for topics by range", "posts")
end
describe "when filtering by number of views in a topic" do
fab!(:topic_with_1_count) { Fabricate(:topic, views: 1) }
fab!(:topic_with_2_count) { Fabricate(:topic, views: 2) }
fab!(:topic_with_3_count) { Fabricate(:topic, views: 3) }
include_examples("filtering for topics by range", "views")
end
describe "when filtering by number of likes in the first post of a topic" do
fab!(:topic_with_1_count) do
post = Fabricate(:post, like_count: 1)
post.topic
end
describe "when query string is `posters-min:3`" do
it "should only return topics with at least 3 participants" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posters-min:3")
.pluck(:id),
).to contain_exactly(topic_with_3_participants.id)
end
fab!(:topic_with_2_count) do
post = Fabricate(:post, like_count: 2)
post.topic
end
describe "when query string is `posters-max:1`" do
it "should only return topics with at most 1 participant" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posters-max:1")
.pluck(:id),
).to contain_exactly(topic_with_1_participant.id)
end
fab!(:topic_with_3_count) do
post = Fabricate(:post, like_count: 3)
post.topic
end
describe "when query string is `posters-max:3`" do
it "should only return topics with at most 3 participants" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posters-max:3")
.pluck(:id),
).to contain_exactly(
topic_with_1_participant.id,
topic_with_2_participants.id,
topic_with_3_participants.id,
)
end
end
describe "when query string is `posters-min:1 posters-max:2`" do
it "should only return topics with at least 1 participant and at most 2 participants" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posters-min:1 posters-max:2")
.pluck(:id),
).to contain_exactly(topic_with_1_participant.id, topic_with_2_participants.id)
end
end
describe "when query string is `posters-min:3 posters-min:2 posters-max:1 posters-max:3`" do
it "should only return topics with at least 2 participants and at most 3 participants as it ignores earlier filters which are duplicated" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posters-min:3 posters-min:2 posters-max:1 posters-max:3")
.pluck(:id),
).to contain_exactly(topic_with_2_participants.id, topic_with_3_participants.id)
end
end
describe "when query string is `posters-min:invalid posters-max:invalid`" do
it "should ignore the filters with invalid values" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("posters-min:invalid posters-max:invalid")
.pluck(:id),
).to contain_exactly(
topic_with_1_participant.id,
topic_with_2_participants.id,
topic_with_3_participants.id,
)
end
end
include_examples("filtering for topics by range", "likes-op")
end
end
end