DEV: Support ordering filters on /filter route (#21275)

This commit adds support for the following ordering filters:

1. `order:activity` which orders the topics by `Topic#bumped_at` in descending order
2. `order:activity-asc` which orders the topics by `Topic#bumped_at` in ascending order
3. `order:latest-post` which orders the topics by `Topic#last_posted_at` in descending order
4. `order:latest-post-asc` which orders the topics by `Topic#last_posted_at` in ascending order
5. `order:created` which orders the topics by `Topic#created_at` in descending order
6. `order:created-asc` which orders the topics by `Topic#created_at` in ascending order
7. `order:views` which orders the topics by `Topic#views` in descending order
8. `order:views-asc` which orders the topics by `Topic#views` in ascending order
9. `order:likes` which orders the topics by `Topic#likes` in descending order
10. `order:likes-asc` which orders the topics by `Topic#likes` in ascending order
11. `order:likes-op` which orders the topics by `Post#like_count` of the first post in the topic in descending order
12. `order:likes-op-asc` which orders the topics by `Post#like_count` of the first post in the topic in ascending order
13. `order:posters` which orders the topics by `Topic#participant_count` in descending order
14. `order:posters-asc` which orders the topics by `Topic#participant_count` in ascending order
15. `order:category` which orders the topics by `Category#name` of the topic's category in descending order
16. `order:category-asc` which orders the topics by `Category#name` of the topic's category in ascending order

Multiple order filters can be composed together and the order of ordering is applied based on the position of the filter
in the query string. For example, `order:views order:created` will order the topics by `Topic#views` in descending order
and then order the topics by `Topics#created_at` in descending order.
This commit is contained in:
Alan Guo Xiang Tan
2023-04-27 14:44:58 +07:00
committed by GitHub
parent 141555136a
commit 6e5e607072
2 changed files with 203 additions and 4 deletions

View File

@@ -62,6 +62,8 @@ class TopicsFilter
filter_by_number_of_likes_in_first_post(min: filter_values)
when "likes-op-max"
filter_by_number_of_likes_in_first_post(max: filter_values)
when "order"
order_by(values: filter_values)
when "posts-min"
filter_by_number_of_posts(min: filter_values)
when "posts-max"
@@ -171,10 +173,7 @@ class TopicsFilter
column_name: "first_posts.like_count",
min:,
max:,
scope:
@scope.joins(
"INNER JOIN posts AS first_posts ON first_posts.topic_id = topics.id AND first_posts.post_number = 1",
),
scope: self.joins_first_posts(@scope),
)
end
@@ -426,4 +425,57 @@ class TopicsFilter
def include_topics_with_any_tags(tag_ids)
@scope = @scope.joins(:topic_tags).where("topic_tags.tag_id IN (?)", tag_ids).distinct(:id)
end
ORDER_BY_MAPPINGS = {
"activity" => {
column: "topics.bumped_at",
},
"category" => {
column: "categories.name",
scope: -> { @scope.joins(:category) },
},
"created" => {
column: "topics.created_at",
},
"latest-post" => {
column: "topics.last_posted_at",
},
"likes" => {
column: "topics.like_count",
},
"likes-op" => {
column: "first_posts.like_count",
scope: -> { joins_first_posts(@scope) },
},
"posters" => {
column: "topics.participant_count",
},
"views" => {
column: "topics.views",
},
}
private_constant :ORDER_BY_MAPPINGS
ORDER_BY_REGEXP = /^(?<order_by>#{ORDER_BY_MAPPINGS.keys.join("|")})(?<asc>-asc)?$/
private_constant :ORDER_BY_REGEXP
def order_by(values:)
values.each do |value|
match_data = value.match(ORDER_BY_REGEXP)
if match_data && column_name = ORDER_BY_MAPPINGS.dig(match_data[:order_by], :column)
if scope = ORDER_BY_MAPPINGS.dig(match_data[:order_by], :scope)
@scope = instance_exec(&scope)
end
@scope = @scope.order(column_name => match_data[:asc] ? :asc : :desc)
end
end
end
def joins_first_posts(scope)
scope.joins(
"INNER JOIN posts AS first_posts ON first_posts.topic_id = topics.id AND first_posts.post_number = 1",
)
end
end