mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 21:21:19 +08:00
FIX: Select earliest post when aggregating posts in a topic for search.
This is a revert ofd8c796bc44
and5bf0a0893b
. Linking to the post within a topic that has the highest rank was confusing users and hard to explain because ranking is determined via the PG ranking function. See the following meta topics for the complaints after we switch to the new ordering: 1. https://meta.discourse.org/t/title-search-not-working-as-expected/157737 2. https://meta.discourse.org/t/search-results-should-prioritize-first-post-in-topic-when-title-matches-search-term/175154
This commit is contained in:
126
lib/search.rb
126
lib/search.rb
@ -986,53 +986,30 @@ class Search
|
|||||||
posts
|
posts
|
||||||
end
|
end
|
||||||
|
|
||||||
if aggregate_search
|
|
||||||
aggregate_relation = Post.unscoped
|
|
||||||
.select("subquery.topic_id id")
|
|
||||||
.group("subquery.topic_id")
|
|
||||||
|
|
||||||
posts = posts.select(posts.arel.projections)
|
|
||||||
end
|
|
||||||
|
|
||||||
if @order == :latest
|
if @order == :latest
|
||||||
posts = posts.reorder("posts.created_at DESC")
|
|
||||||
|
|
||||||
if aggregate_search
|
if aggregate_search
|
||||||
aggregate_relation = aggregate_relation
|
posts = posts.order("MAX(posts.created_at) DESC")
|
||||||
.select(
|
else
|
||||||
"MAX(subquery.post_number) post_number",
|
posts = posts.reorder("posts.created_at DESC")
|
||||||
"MAX(subquery.created_at) created_at"
|
|
||||||
)
|
|
||||||
.order("created_at DESC")
|
|
||||||
end
|
end
|
||||||
elsif @order == :latest_topic
|
elsif @order == :latest_topic
|
||||||
posts = posts.order("topics.created_at DESC")
|
|
||||||
|
|
||||||
if aggregate_search
|
if aggregate_search
|
||||||
posts = posts.select("topics.created_at topic_created_at")
|
posts = posts.order("MAX(topics.created_at) DESC")
|
||||||
|
else
|
||||||
aggregate_relation = aggregate_relation
|
posts = posts.order("topics.created_at DESC")
|
||||||
.select(
|
|
||||||
"MIN(subquery.post_number) post_number",
|
|
||||||
"MAX(subquery.topic_created_at) topic_created_at"
|
|
||||||
)
|
|
||||||
.order("topic_created_at DESC")
|
|
||||||
end
|
end
|
||||||
elsif @order == :views
|
elsif @order == :views
|
||||||
posts = posts.order("topics.views DESC")
|
|
||||||
|
|
||||||
if aggregate_search
|
if aggregate_search
|
||||||
posts = posts.select("topics.views topic_views")
|
posts = posts.order("MAX(topics.views) DESC")
|
||||||
|
else
|
||||||
aggregate_relation = aggregate_relation
|
posts = posts.order("topics.views DESC")
|
||||||
.select(
|
|
||||||
"MIN(subquery.post_number) post_number",
|
|
||||||
"MAX(subquery.topic_views) topic_views"
|
|
||||||
)
|
|
||||||
.order("topic_views DESC")
|
|
||||||
end
|
end
|
||||||
elsif @order == :likes
|
elsif @order == :likes
|
||||||
posts = posts.order("posts.like_count DESC")
|
if aggregate_search
|
||||||
|
posts = posts.order("MAX(posts.like_count) DESC")
|
||||||
|
else
|
||||||
|
posts = posts.order("posts.like_count DESC")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
rank = <<~SQL
|
rank = <<~SQL
|
||||||
TS_RANK_CD(
|
TS_RANK_CD(
|
||||||
@ -1069,31 +1046,22 @@ class Search
|
|||||||
"(#{rank} * #{category_priority_weights})"
|
"(#{rank} * #{category_priority_weights})"
|
||||||
end
|
end
|
||||||
|
|
||||||
if aggregate_search
|
posts =
|
||||||
posts = posts.select("#{data_ranking} rank", "topics.bumped_at topic_bumped_at")
|
if aggregate_search
|
||||||
.order("rank DESC", "topic_bumped_at DESC")
|
posts.order("MAX(#{data_ranking}) DESC")
|
||||||
|
else
|
||||||
|
posts.order("#{data_ranking} DESC")
|
||||||
|
end
|
||||||
|
|
||||||
aggregate_relation = aggregate_relation
|
posts = posts.order("topics.bumped_at DESC")
|
||||||
.select(
|
end
|
||||||
"(ARRAY_AGG(subquery.post_number ORDER BY subquery.rank DESC, subquery.topic_bumped_at DESC))[1] post_number",
|
|
||||||
"MAX(subquery.rank) rank", "MAX(subquery.topic_bumped_at) topic_bumped_at"
|
posts =
|
||||||
)
|
if secure_category_ids.present?
|
||||||
.order("rank DESC", "topic_bumped_at DESC")
|
posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted) OR (categories.id IN (?))", secure_category_ids).references(:categories)
|
||||||
else
|
else
|
||||||
posts = posts.order("#{data_ranking} DESC", "topics.bumped_at DESC")
|
posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted)").references(:categories)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if secure_category_ids.present?
|
|
||||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted) OR (categories.id IN (?))", secure_category_ids).references(:categories)
|
|
||||||
else
|
|
||||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted)").references(:categories)
|
|
||||||
end
|
|
||||||
|
|
||||||
if aggregate_search
|
|
||||||
posts = yield(posts) if block_given?
|
|
||||||
posts = aggregate_relation.from(posts)
|
|
||||||
end
|
|
||||||
|
|
||||||
if @order
|
if @order
|
||||||
advanced_order = Search.advanced_orders&.fetch(@order, nil)
|
advanced_order = Search.advanced_orders&.fetch(@order, nil)
|
||||||
@ -1169,36 +1137,28 @@ class Search
|
|||||||
Search.min_post_id
|
Search.min_post_id
|
||||||
end
|
end
|
||||||
|
|
||||||
if @order == :likes
|
min_or_max = @order == :latest ? "max" : "min"
|
||||||
# likes are a pain to aggregate so skip
|
|
||||||
query = posts_query(limit, **default_opts).select('topics.id', 'posts.post_number')
|
|
||||||
|
|
||||||
if min_id > 0
|
query =
|
||||||
low_set = query.dup.where("post_search_data.post_id < #{min_id}")
|
if @order == :likes
|
||||||
high_set = query.where("post_search_data.post_id >= #{min_id}")
|
# likes are a pain to aggregate so skip
|
||||||
|
posts_query(limit, type_filter: opts[:type_filter])
|
||||||
{ default: wrap_rows(high_set), remaining: wrap_rows(low_set) }
|
.select('topics.id', "posts.post_number")
|
||||||
else
|
else
|
||||||
{ default: wrap_rows(query) }
|
posts_query(limit, aggregate_search: true, type_filter: opts[:type_filter])
|
||||||
end
|
.select('topics.id', "#{min_or_max}(posts.post_number) post_number")
|
||||||
else
|
.group('topics.id')
|
||||||
query = posts_query(limit, **default_opts, aggregate_search: true) do |posts|
|
|
||||||
if min_id > 0
|
|
||||||
posts.select("post_search_data.post_id post_search_data_post_id")
|
|
||||||
else
|
|
||||||
posts
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if min_id > 0
|
if min_id > 0
|
||||||
low_set = query.dup.where("subquery.post_search_data_post_id < #{min_id}")
|
low_set = query.dup.where("post_search_data.post_id < #{min_id}")
|
||||||
high_set = query.where("subquery.post_search_data_post_id >= #{min_id}")
|
high_set = query.where("post_search_data.post_id >= #{min_id}")
|
||||||
|
|
||||||
{ default: wrap_rows(high_set), remaining: wrap_rows(low_set) }
|
return { default: wrap_rows(high_set), remaining: wrap_rows(low_set) }
|
||||||
else
|
|
||||||
{ default: wrap_rows(query) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# double wrapping so we get correct row numbers
|
||||||
|
{ default: wrap_rows(query) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def aggregate_posts(post_sql)
|
def aggregate_posts(post_sql)
|
||||||
|
@ -601,8 +601,8 @@ describe Search do
|
|||||||
expect(result.posts.pluck(:id)).to eq([post2.id, post.id])
|
expect(result.posts.pluck(:id)).to eq([post2.id, post.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'aggregates searches in a topic by returning the post with the highest rank' do
|
it 'aggregates searches in a topic by returning the post with the lowest post number' do
|
||||||
_post = Fabricate(:post, topic: topic, raw: "this is a play post")
|
post = Fabricate(:post, topic: topic, raw: "this is a play post")
|
||||||
post2 = Fabricate(:post, topic: topic, raw: "play play playing played play")
|
post2 = Fabricate(:post, topic: topic, raw: "play play playing played play")
|
||||||
post3 = Fabricate(:post, raw: "this is a play post")
|
post3 = Fabricate(:post, raw: "this is a play post")
|
||||||
|
|
||||||
@ -613,7 +613,7 @@ describe Search do
|
|||||||
results = Search.execute('play')
|
results = Search.execute('play')
|
||||||
|
|
||||||
expect(results.posts.map(&:id)).to eq([
|
expect(results.posts.map(&:id)).to eq([
|
||||||
post2.id,
|
post.id,
|
||||||
post3.id
|
post3.id
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
@ -1892,9 +1892,11 @@ describe Search do
|
|||||||
|
|
||||||
it 'allows to define custom order' do
|
it 'allows to define custom order' do
|
||||||
expect(Search.new("advanced").execute.posts).to eq([post1, post0])
|
expect(Search.new("advanced").execute.posts).to eq([post1, post0])
|
||||||
|
|
||||||
Search.advanced_order(:chars) do |posts|
|
Search.advanced_order(:chars) do |posts|
|
||||||
posts.reorder("(SELECT LENGTH(raw) FROM posts WHERE posts.topic_id = subquery.topic_id) DESC")
|
posts.reorder("MAX(LENGTH(posts.raw)) DESC")
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(Search.new("advanced order:chars").execute.posts).to eq([post0, post1])
|
expect(Search.new("advanced order:chars").execute.posts).to eq([post0, post1])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user