discourse/app/models/top_topic.rb
Régis Hanol 6b83ed0347 OPTIM: change Top Topic's score computation algorithm
New algorithm will not wipe the entire table anymore and will only remove
invisible topics and add new visible topics.
2014-02-28 20:33:52 +01:00

160 lines
5.4 KiB
Ruby

class TopTopic < ActiveRecord::Base
belongs_to :topic
def self.periods
@periods ||= [:yearly, :monthly, :weekly, :daily]
end
def self.sort_orders
@sort_orders ||= [:posts, :views, :likes]
end
def self.refresh!
transaction do
# update the topics list
remove_invisible_topics
add_new_visible_topics
# update the denormalized data
TopTopic.periods.each do |period|
TopTopic.sort_orders.each do |sort|
TopTopic.send("update_#{sort}_count_for", period)
end
# compute top score
TopTopic.compute_top_score_for(period)
end
end
end
def self.remove_invisible_topics
exec_sql("WITH invisible_topics AS (
SELECT id
FROM topics
WHERE deleted_at IS NOT NULL
OR NOT visible
OR archetype = :private_message
OR archived
)
DELETE FROM top_topics
WHERE topic_id IN (SELECT id FROM invisible_topics)",
private_message: Archetype::private_message)
end
def self.add_new_visible_topics
exec_sql("WITH visible_topics AS (
SELECT t.id
FROM topics t
LEFT JOIN top_topics tt ON t.id = tt.topic_id
WHERE t.deleted_at IS NULL
AND t.visible
AND t.archetype <> :private_message
AND NOT t.archived
AND tt.topic_id IS NULL
)
INSERT INTO top_topics (topic_id)
SELECT id
FROM visible_topics",
private_message: Archetype::private_message)
end
def self.update_posts_count_for(period)
sql = "SELECT topic_id, GREATEST(COUNT(*), 1) AS count
FROM posts
WHERE created_at >= :from
AND deleted_at IS NULL
AND NOT hidden
AND post_type = #{Post.types[:regular]}
AND user_id <> #{Discourse.system_user.id}
GROUP BY topic_id"
TopTopic.update_top_topics(period, "posts", sql)
end
def self.update_views_count_for(period)
sql = "SELECT parent_id as topic_id, COUNT(*) AS count
FROM views
WHERE viewed_at >= :from
GROUP BY topic_id"
TopTopic.update_top_topics(period, "views", sql)
end
def self.update_likes_count_for(period)
sql = "SELECT topic_id, GREATEST(SUM(like_count), 1) AS count
FROM posts
WHERE created_at >= :from
AND deleted_at IS NULL
AND NOT hidden
GROUP BY topic_id"
TopTopic.update_top_topics(period, "likes", sql)
end
def self.compute_top_score_for(period)
# log(views) + (posts * likes)
exec_sql("UPDATE top_topics
SET #{period}_score = CASE
WHEN #{period}_views_count = 0 THEN 0
ELSE log(#{period}_views_count) + (#{period}_posts_count * #{period}_likes_count)
END")
end
def self.start_of(period)
case period
when :yearly then 1.year.ago
when :monthly then 1.month.ago
when :weekly then 1.week.ago
when :daily then 1.day.ago
end
end
def self.update_top_topics(period, sort, inner_join)
exec_sql("UPDATE top_topics
SET #{period}_#{sort}_count = c.count
FROM top_topics tt
INNER JOIN (#{inner_join}) c ON tt.topic_id = c.topic_id
WHERE tt.topic_id = top_topics.topic_id", from: start_of(period))
end
end
# == Schema Information
#
# Table name: top_topics
#
# id :integer not null, primary key
# topic_id :integer
# yearly_posts_count :integer default(0), not null
# yearly_views_count :integer default(0), not null
# yearly_likes_count :integer default(0), not null
# monthly_posts_count :integer default(0), not null
# monthly_views_count :integer default(0), not null
# monthly_likes_count :integer default(0), not null
# weekly_posts_count :integer default(0), not null
# weekly_views_count :integer default(0), not null
# weekly_likes_count :integer default(0), not null
# daily_posts_count :integer default(0), not null
# daily_views_count :integer default(0), not null
# daily_likes_count :integer default(0), not null
# yearly_score :float
# monthly_score :float
# weekly_score :float
# daily_score :float
#
# Indexes
#
# index_top_topics_on_daily_likes_count (daily_likes_count)
# index_top_topics_on_daily_posts_count (daily_posts_count)
# index_top_topics_on_daily_views_count (daily_views_count)
# index_top_topics_on_monthly_likes_count (monthly_likes_count)
# index_top_topics_on_monthly_posts_count (monthly_posts_count)
# index_top_topics_on_monthly_views_count (monthly_views_count)
# index_top_topics_on_topic_id (topic_id) UNIQUE
# index_top_topics_on_weekly_likes_count (weekly_likes_count)
# index_top_topics_on_weekly_posts_count (weekly_posts_count)
# index_top_topics_on_weekly_views_count (weekly_views_count)
# index_top_topics_on_yearly_likes_count (yearly_likes_count)
# index_top_topics_on_yearly_posts_count (yearly_posts_count)
# index_top_topics_on_yearly_views_count (yearly_views_count)
#