mirror of
https://github.com/discourse/discourse.git
synced 2025-08-17 18:04:11 +08:00
FEATURE: new search order for read topics (#33353)
Introduces order:read and r to search through recently read topics Also applies to filter route
This commit is contained in:
parent
d92de3e4d0
commit
e180c62d2e
4 changed files with 111 additions and 0 deletions
|
@ -864,6 +864,17 @@ class Search
|
|||
else
|
||||
posts = posts.order("posts.like_count DESC")
|
||||
end
|
||||
elsif @order == :read && @guardian.user
|
||||
posts =
|
||||
posts.joins(
|
||||
"JOIN topic_users tu ON tu.topic_id = posts.topic_id AND tu.user_id = #{@guardian.user.id.to_i}",
|
||||
).where("tu.last_visited_at IS NOT NULL")
|
||||
|
||||
if aggregate_search
|
||||
posts = posts.order("MAX(tu.last_visited_at) DESC")
|
||||
else
|
||||
posts = posts.reorder("tu.last_visited_at DESC")
|
||||
end
|
||||
elsif allow_relevance_search
|
||||
posts = sort_by_relevance(posts, type_filter: type_filter, aggregate_search: aggregate_search)
|
||||
end
|
||||
|
@ -938,6 +949,9 @@ class Search
|
|||
if word == "l"
|
||||
@order = :latest
|
||||
nil
|
||||
elsif word == "r"
|
||||
@order = :read if @guardian.user
|
||||
nil
|
||||
elsif word =~ /\Aorder:\w+\z/i
|
||||
@order = word.downcase.gsub("order:", "").to_sym
|
||||
nil
|
||||
|
|
|
@ -545,6 +545,19 @@ class TopicsFilter
|
|||
"views" => {
|
||||
column: "topics.views",
|
||||
},
|
||||
"read" => {
|
||||
column: "tu.last_visited_at",
|
||||
scope: -> do
|
||||
if @guardian.user
|
||||
@scope.joins(
|
||||
"JOIN topic_users tu ON tu.topic_id = topics.id AND tu.user_id = #{@guardian.user.id.to_i}",
|
||||
).where("tu.last_visited_at IS NOT NULL")
|
||||
else
|
||||
# make sure this works for anon
|
||||
@scope.joins("LEFT JOIN topic_users tu ON 1 = 0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
||||
private_constant :ORDER_BY_MAPPINGS
|
||||
|
||||
|
|
|
@ -3135,4 +3135,35 @@ RSpec.describe Search do
|
|||
expect(results.posts).to contain_exactly(regular_post)
|
||||
end
|
||||
end
|
||||
|
||||
it "orders posts by the timestamp of the user's last visit to each topic" do
|
||||
user = Fabricate(:user)
|
||||
|
||||
post2 = nil
|
||||
freeze_time 2.hours.ago do
|
||||
post2 = Fabricate(:post, raw: "Read order term")
|
||||
TopicUser.update_last_read(user, post2.topic.id, post2.post_number, 1, 0)
|
||||
end
|
||||
|
||||
post1 = nil
|
||||
freeze_time 1.hour.ago do
|
||||
post1 = Fabricate(:post, raw: "Read order term")
|
||||
TopicUser.update_last_read(user, post1.topic.id, post1.post_number, 1, 0)
|
||||
end
|
||||
|
||||
_unread_post = Fabricate(:post, raw: "Read order term")
|
||||
|
||||
result = Search.execute("Read order term order:read", guardian: Guardian.new(user))
|
||||
expect(result.posts.map(&:id)).to eq([post1.id, post2.id])
|
||||
|
||||
result = Search.execute("Read order term r", guardian: Guardian.new(user))
|
||||
|
||||
# also allow for the r shortcul like we have l
|
||||
expect(result.posts.map(&:id)).to eq([post1.id, post2.id])
|
||||
|
||||
result = Search.execute("Read order term r", guardian: Guardian.new)
|
||||
|
||||
# no op on anon - all included
|
||||
expect(result.posts.map(&:id).length).to eq(3)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1530,6 +1530,59 @@ RSpec.describe TopicsFilter do
|
|||
include_examples "ordering topics filters", "title", "topic's title"
|
||||
end
|
||||
|
||||
describe "when ordering by user's last visit to topics" do
|
||||
fab!(:user)
|
||||
fab!(:topic)
|
||||
fab!(:topic2) { Fabricate(:topic) }
|
||||
fab!(:topic3) { Fabricate(:topic) }
|
||||
|
||||
before do
|
||||
freeze_time 3.hours.ago do
|
||||
TopicUser.update_last_read(user, topic3.id, 1, 1, 0)
|
||||
end
|
||||
|
||||
freeze_time 2.hours.ago do
|
||||
TopicUser.update_last_read(user, topic.id, 1, 1, 0)
|
||||
end
|
||||
|
||||
freeze_time 1.hour.ago do
|
||||
TopicUser.update_last_read(user, topic2.id, 1, 1, 0)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when query string is `order:read`" do
|
||||
it "should return topics ordered by last visited date in descending order for logged in users" do
|
||||
expect(
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new(user))
|
||||
.filter_from_query_string("order:read")
|
||||
.pluck(:id),
|
||||
).to eq([topic2.id, topic.id, topic3.id])
|
||||
end
|
||||
|
||||
it "should not apply any special ordering for anonymous users" do
|
||||
topics =
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new)
|
||||
.filter_from_query_string("order:read")
|
||||
.where(id: [topic.id, topic2.id, topic3.id])
|
||||
|
||||
expect(topics.pluck(:id)).to contain_exactly(topic.id, topic2.id, topic3.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when query string is `order:read-asc`" do
|
||||
it "should return topics ordered by last visited date in ascending order for logged in users" do
|
||||
expect(
|
||||
TopicsFilter
|
||||
.new(guardian: Guardian.new(user))
|
||||
.filter_from_query_string("order:read-asc")
|
||||
.pluck(:id),
|
||||
).to eq([topic3.id, topic.id, topic2.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "composing multiple order filters" do
|
||||
fab!(:topic) { Fabricate(:topic, created_at: Time.zone.local(2023, 1, 1), views: 2) }
|
||||
fab!(:topic2) { Fabricate(:topic, created_at: Time.zone.local(2024, 1, 1), views: 2) }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue