DEV: Support filtering by date columns on /filter route (#21233)
This commit adds support for the following date filters: 1. `activity-before:<YYYY-MM-DD>` which filters for topics that have been bumped at or before given date 2. `activity-after:<YYYY-MM-DD>` which filters for topics that have been bumped at or after given date 3. `created-before:<YYYY-MM-DD>` which filters for topics that have been created at or before given date 4. `created-after:<YYYY-MM-DD>` which filters for topics that have been created at or after given date 5. `latest-post-before:<YYYY-MM-DD>` which filters for topics with the latest post posted at or before given date 6. `latest-post-after:<YYYY-MM-DD>` which filters for topics with the latest post posted at or after given date If the filter has an invalid value, i.e string that cannot be converted into a proper date in the `YYYY-MM-DD` format, 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:
parent
e5ec0b84a9
commit
141555136a
|
@ -36,12 +36,24 @@ class TopicsFilter
|
|||
filter_values = extract_and_validate_value_for(filter, values)
|
||||
|
||||
case filter
|
||||
when "activity-before"
|
||||
filter_by_activity(before: filter_values)
|
||||
when "activity-after"
|
||||
filter_by_activity(after: filter_values)
|
||||
when "category"
|
||||
filter_categories(values: key_prefixes.zip(filter_values))
|
||||
when "created-after"
|
||||
filter_by_created(after: filter_values)
|
||||
when "created-before"
|
||||
filter_by_created(before: filter_values)
|
||||
when "created-by"
|
||||
filter_created_by_user(usernames: filter_values.flat_map { |value| value.split(",") })
|
||||
when "in"
|
||||
filter_in(values: filter_values)
|
||||
when "latest-post-after"
|
||||
filter_by_latest_post(after: filter_values)
|
||||
when "latest-post-before"
|
||||
filter_by_latest_post(before: filter_values)
|
||||
when "likes-min"
|
||||
filter_by_number_of_likes(min: filter_values)
|
||||
when "likes-max"
|
||||
|
@ -99,8 +111,21 @@ class TopicsFilter
|
|||
|
||||
private
|
||||
|
||||
YYYY_MM_DD_REGEXP =
|
||||
/^(?<year>[12][0-9]{3})-(?<month>0?[1-9]|1[0-2])-(?<day>0?[1-9]|[12]\d|3[01])$/
|
||||
private_constant :YYYY_MM_DD_REGEXP
|
||||
|
||||
def extract_and_validate_value_for(filter, values)
|
||||
case filter
|
||||
when "activity-before", "activity-after", "created-before", "created-after",
|
||||
"latest-post-before", "latest-post-after"
|
||||
value = values.last
|
||||
|
||||
if match_data = value.match(YYYY_MM_DD_REGEXP)
|
||||
Time.zone.parse(
|
||||
"#{match_data[:year].to_i}-#{match_data[:month].to_i}-#{match_data[:day].to_i}",
|
||||
)
|
||||
end
|
||||
when "likes-min", "likes-max", "likes-op-min", "likes-op-max", "posts-min", "posts-max",
|
||||
"posters-min", "posters-max", "views-min", "views-max"
|
||||
value = values.last
|
||||
|
@ -117,6 +142,18 @@ class TopicsFilter
|
|||
end
|
||||
end
|
||||
|
||||
def filter_by_activity(before: nil, after: nil)
|
||||
filter_by_topic_range(column_name: "topics.bumped_at", min: after, max: before)
|
||||
end
|
||||
|
||||
def filter_by_created(before: nil, after: nil)
|
||||
filter_by_topic_range(column_name: "topics.created_at", min: after, max: before)
|
||||
end
|
||||
|
||||
def filter_by_latest_post(before: nil, after: nil)
|
||||
filter_by_topic_range(column_name: "topics.last_posted_at", min: after, max: before)
|
||||
end
|
||||
|
||||
def filter_by_number_of_posts(min: nil, max: nil)
|
||||
filter_by_topic_range(column_name: "topics.posts_count", min:, max:)
|
||||
end
|
||||
|
|
|
@ -847,7 +847,7 @@ RSpec.describe TopicsFilter do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples "filtering for topics by range" do |filter|
|
||||
shared_examples "filtering for topics by counts" do |filter|
|
||||
describe "when query string is `#{filter}-min:1`" do
|
||||
it "should only return topics with at least 1 #{filter}" do
|
||||
expect(
|
||||
|
@ -933,7 +933,7 @@ RSpec.describe TopicsFilter do
|
|||
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")
|
||||
include_examples("filtering for topics by counts", "likes")
|
||||
end
|
||||
|
||||
describe "when filtering by number of posters in a topic" do
|
||||
|
@ -941,7 +941,7 @@ RSpec.describe TopicsFilter do
|
|||
fab!(:topic_with_2_count) { Fabricate(:topic, participant_count: 2) }
|
||||
fab!(:topic_with_3_count) { Fabricate(:topic, participant_count: 3) }
|
||||
|
||||
include_examples("filtering for topics by range", "posters")
|
||||
include_examples("filtering for topics by counts", "posters")
|
||||
end
|
||||
|
||||
describe "when filtering by number of posts in a topic" do
|
||||
|
@ -949,7 +949,7 @@ RSpec.describe TopicsFilter do
|
|||
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")
|
||||
include_examples("filtering for topics by counts", "posts")
|
||||
end
|
||||
|
||||
describe "when filtering by number of views in a topic" do
|
||||
|
@ -957,7 +957,7 @@ RSpec.describe TopicsFilter do
|
|||
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")
|
||||
include_examples("filtering for topics by counts", "views")
|
||||
end
|
||||
|
||||
describe "when filtering by number of likes in the first post of a topic" do
|
||||
|
@ -976,7 +976,104 @@ RSpec.describe TopicsFilter do
|
|||
post.topic
|
||||
end
|
||||
|
||||
include_examples("filtering for topics by range", "likes-op")
|
||||
include_examples("filtering for topics by counts", "likes-op")
|
||||
end
|
||||
|
||||
shared_examples "filtering for topics by date column" do |filter, column, description|
|
||||
fab!(:topic) { Fabricate(:topic, column => Time.zone.local(2022, 1, 1)) }
|
||||
fab!(:topic2) { Fabricate(:topic, column => Time.zone.local(2023, 5, 12)) }
|
||||
|
||||
describe "when query string is `#{filter}-after:invalid-date-test`" do
|
||||
it "should ignore the filter" do
|
||||
expect(
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new)
|
||||
.filter_from_query_string("#{filter}-after:invalid-date-test")
|
||||
.pluck(:id),
|
||||
).to contain_exactly(topic.id, topic2.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when query string is `#{filter}-after:2022-01-01`" do
|
||||
it "should only return topics with #{description} after 2022-01-01" do
|
||||
expect(
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new)
|
||||
.filter_from_query_string("#{filter}-after:2022-01-01")
|
||||
.pluck(:id),
|
||||
).to contain_exactly(topic.id, topic2.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when query string is `#{filter}-after:2023-01-1`" do
|
||||
it "should only return topics with #{description} after 2023-01-01" do
|
||||
expect(
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new)
|
||||
.filter_from_query_string("#{filter}-after:2023-01-1")
|
||||
.pluck(:id),
|
||||
).to contain_exactly(topic2.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when query string is `#{filter}-after:2023-6-01`" do
|
||||
it "should only return topics with #{description} after 2023-06-01" do
|
||||
expect(
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new)
|
||||
.filter_from_query_string("#{filter}-after:2023-6-01")
|
||||
.pluck(:id),
|
||||
).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
describe "when query string is `#{filter}-before:2023-01-01`" do
|
||||
it "should only return topics with #{description} before 2023-01-01" do
|
||||
expect(
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new)
|
||||
.filter_from_query_string("#{filter}-before:2023-01-01")
|
||||
.pluck(:id),
|
||||
).to contain_exactly(topic.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when query string is `#{filter}-before:2023-1-1`" do
|
||||
it "should only return topics with #{description} before 2023-01-01" do
|
||||
expect(
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new)
|
||||
.filter_from_query_string("#{filter}-before:2023-1-1")
|
||||
.pluck(:id),
|
||||
).to contain_exactly(topic.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when query string is `#{filter}-before:2000-01-01`" do
|
||||
it "should only return topics with #{description} before 2000-01-01" do
|
||||
expect(
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new)
|
||||
.filter_from_query_string("#{filter}-before:2000-01-01")
|
||||
.pluck(:id),
|
||||
).to eq([])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "when filtering by activity of topics" do
|
||||
include_examples "filtering for topics by date column", "activity", :bumped_at, "bumped date"
|
||||
end
|
||||
|
||||
describe "when filtering by creation date of topics" do
|
||||
include_examples "filtering for topics by date column", "created", :created_at, "created date"
|
||||
end
|
||||
|
||||
describe "when filtering by last post date of topics" do
|
||||
include_examples "filtering for topics by date column",
|
||||
"latest-post",
|
||||
:last_posted_at,
|
||||
"last posted date"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue