FEATURE: improvements to hot algorithm (#25295)
- Decrease gravity, we come in too hot prioritizing too many new topics - Remove all muted topics / categories and tags from the hot list - Punish topics with zero likes in algorithm
This commit is contained in:
parent
54e6c1d823
commit
df8bb947b2
|
@ -100,11 +100,11 @@ class TopicHotScore < ActiveRecord::Base
|
|||
# we need an extra index for this
|
||||
DB.exec(<<~SQL, args)
|
||||
UPDATE topic_hot_scores ths
|
||||
SET score = topics.like_count /
|
||||
SET score = (topics.like_count - 1) /
|
||||
(EXTRACT(EPOCH FROM (:now - topics.created_at)) / 3600 + 2) ^ :gravity
|
||||
+
|
||||
CASE WHEN ths.recent_first_bumped_at IS NULL THEN 0 ELSE
|
||||
(ths.recent_likes + ths.recent_posters) /
|
||||
(ths.recent_likes + ths.recent_posters - 1) /
|
||||
(EXTRACT(EPOCH FROM (:now - recent_first_bumped_at)) / 3600 + 2) ^ :gravity
|
||||
END
|
||||
,
|
||||
|
|
|
@ -3116,7 +3116,7 @@ dashboard:
|
|||
default: false
|
||||
hot_topics_gravity:
|
||||
hidden: true
|
||||
default: 1.8
|
||||
default: 1.2
|
||||
hot_topics_recent_days:
|
||||
hidden: true
|
||||
default: 7
|
||||
|
|
|
@ -340,6 +340,9 @@ class TopicQuery
|
|||
|
||||
def list_hot
|
||||
create_list(:hot, unordered: true) do |topics|
|
||||
topics = remove_muted_topics(topics, user)
|
||||
topics = remove_muted_categories(topics, user, exclude: options[:category])
|
||||
TopicQuery.remove_muted_tags(topics, user, options)
|
||||
topics.joins("JOIN topic_hot_scores on topics.id = topic_hot_scores.topic_id").order(
|
||||
"topic_hot_scores.score DESC",
|
||||
)
|
||||
|
|
|
@ -75,6 +75,36 @@ RSpec.describe TopicQuery do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#list_hot" do
|
||||
it "excludes muted categories and topics" do
|
||||
muted_category = Fabricate(:category)
|
||||
muted_topic = Fabricate(:topic, category: muted_category)
|
||||
|
||||
TopicHotScore.create!(topic_id: muted_topic.id, score: 1.0)
|
||||
|
||||
expect(TopicQuery.new(user).list_hot.topics.map(&:id)).to include(muted_topic.id)
|
||||
|
||||
tu =
|
||||
TopicUser.create!(
|
||||
user_id: user.id,
|
||||
topic_id: muted_topic.id,
|
||||
notification_level: TopicUser.notification_levels[:muted],
|
||||
)
|
||||
|
||||
expect(TopicQuery.new(user).list_hot.topics.map(&:id)).not_to include(muted_topic.id)
|
||||
|
||||
tu.destroy!
|
||||
|
||||
CategoryUser.create!(
|
||||
user_id: user.id,
|
||||
category_id: muted_category.id,
|
||||
notification_level: CategoryUser.notification_levels[:muted],
|
||||
)
|
||||
|
||||
expect(TopicQuery.new(user).list_hot.topics.map(&:id)).not_to include(muted_topic.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#prioritize_pinned_topics" do
|
||||
it "does the pagination correctly" do
|
||||
num_topics = 15
|
||||
|
@ -245,7 +275,7 @@ RSpec.describe TopicQuery do
|
|||
group = Fabricate(:group)
|
||||
group.add(group_moderator)
|
||||
category = Fabricate(:category, reviewable_by_group: group)
|
||||
topic = Fabricate(:topic, category: category, deleted_at: 1.year.ago)
|
||||
_topic = Fabricate(:topic, category: category, deleted_at: 1.year.ago)
|
||||
|
||||
expect(TopicQuery.new(admin, status: "deleted").list_latest.topics.size).to eq(1)
|
||||
expect(TopicQuery.new(moderator, status: "deleted").list_latest.topics.size).to eq(1)
|
||||
|
@ -265,7 +295,7 @@ RSpec.describe TopicQuery do
|
|||
it "includes users own pms in regular topic lists" do
|
||||
topic = Fabricate(:topic)
|
||||
own_pm = Fabricate(:private_message_topic, user: user)
|
||||
other_pm = Fabricate(:private_message_topic, user: Fabricate(:user))
|
||||
_other_pm = Fabricate(:private_message_topic, user: Fabricate(:user))
|
||||
|
||||
expect(TopicQuery.new(user).list_latest.topics).to contain_exactly(topic)
|
||||
expect(TopicQuery.new(admin).list_latest.topics).to contain_exactly(topic)
|
||||
|
@ -1466,8 +1496,8 @@ RSpec.describe TopicQuery do
|
|||
it "should return random topics excluding topics that are muted by user and not older than `suggested_topics_max_days_old` site setting" do
|
||||
topic2 = Fabricate(:topic, user: user)
|
||||
topic3 = Fabricate(:topic, user: user)
|
||||
topic4 = Fabricate(:topic, user: user, created_at: 8.days.ago)
|
||||
topic5 = Fabricate(:topic).tap { |t| TopicNotifier.new(t).mute!(user) }
|
||||
_topic4 = Fabricate(:topic, user: user, created_at: 8.days.ago)
|
||||
_topic5 = Fabricate(:topic).tap { |t| TopicNotifier.new(t).mute!(user) }
|
||||
|
||||
SiteSetting.suggested_topics_max_days_old = 7
|
||||
|
||||
|
@ -1483,7 +1513,7 @@ RSpec.describe TopicQuery do
|
|||
|
||||
fab!(:topic_in_category_that_user_created_and_has_partially_read) do
|
||||
Fabricate(:topic, user: user, category:).tap do |t|
|
||||
first_post = Fabricate(:post, topic: t)
|
||||
_first_post = Fabricate(:post, topic: t)
|
||||
second_post = Fabricate(:post, topic: t)
|
||||
|
||||
TopicUser.change(
|
||||
|
@ -1498,7 +1528,7 @@ RSpec.describe TopicQuery do
|
|||
|
||||
fab!(:topic_in_category2_that_user_created_and_has_partially_read) do
|
||||
Fabricate(:topic, user: user, category: category2).tap do |t|
|
||||
first_post = Fabricate(:post, topic: t)
|
||||
_first_post = Fabricate(:post, topic: t)
|
||||
second_post = Fabricate(:post, topic: t)
|
||||
|
||||
TopicUser.change(
|
||||
|
@ -1513,7 +1543,7 @@ RSpec.describe TopicQuery do
|
|||
|
||||
fab!(:topic_in_category_that_user_has_partially_read) do
|
||||
Fabricate(:topic, category:).tap do |t|
|
||||
first_post = Fabricate(:post, topic: t)
|
||||
_first_post = Fabricate(:post, topic: t)
|
||||
second_post = Fabricate(:post, topic: t)
|
||||
|
||||
TopicUser.change(
|
||||
|
@ -1528,7 +1558,7 @@ RSpec.describe TopicQuery do
|
|||
|
||||
fab!(:topic_in_category2_that_user_has_partially_read) do
|
||||
Fabricate(:topic, category: category2).tap do |t|
|
||||
first_post = Fabricate(:post, topic: t)
|
||||
_first_post = Fabricate(:post, topic: t)
|
||||
second_post = Fabricate(:post, topic: t)
|
||||
|
||||
TopicUser.change(
|
||||
|
|
|
@ -31,7 +31,7 @@ RSpec.describe TopicHotScore do
|
|||
expect(hot_scoring.recent_likes).to eq(2)
|
||||
expect(hot_scoring.recent_posters).to eq(2)
|
||||
expect(hot_scoring.recent_first_bumped_at).to eq_time(new_reply.created_at)
|
||||
expect(hot_scoring.score).to be_within(0.001).of(1.020)
|
||||
expect(hot_scoring.score).to be_within(0.001).of(1.219)
|
||||
|
||||
expect(TopicHotScore.find_by(topic_id: -1).recent_likes).to eq(0)
|
||||
end
|
||||
|
@ -44,15 +44,15 @@ RSpec.describe TopicHotScore do
|
|||
|
||||
TopicHotScore.update_scores
|
||||
|
||||
expect(TopicHotScore.find_by(topic_id: topic1.id).score).to be_within(0.001).of(0.415)
|
||||
expect(TopicHotScore.find_by(topic_id: topic2.id).score).to be_within(0.001).of(0.551)
|
||||
expect(TopicHotScore.find_by(topic_id: topic1.id).score).to be_within(0.001).of(0.535)
|
||||
expect(TopicHotScore.find_by(topic_id: topic2.id).score).to be_within(0.001).of(1.304)
|
||||
|
||||
freeze_time(2.hours.from_now)
|
||||
|
||||
TopicHotScore.update_scores
|
||||
|
||||
expect(TopicHotScore.find_by(topic_id: topic1.id).score).to be_within(0.001).of(0.165)
|
||||
expect(TopicHotScore.find_by(topic_id: topic2.id).score).to be_within(0.001).of(0.301)
|
||||
expect(TopicHotScore.find_by(topic_id: topic1.id).score).to be_within(0.001).of(0.289)
|
||||
expect(TopicHotScore.find_by(topic_id: topic2.id).score).to be_within(0.001).of(0.871)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue