discourse/lib/score_calculator.rb

75 lines
2.2 KiB
Ruby

class ScoreCalculator
def self.default_score_weights
{
reply_count: 5,
like_count: 15,
incoming_link_count: 5,
bookmark_count: 2,
avg_time: 0.05,
reads: 0.2
}
end
def initialize(weightings=nil)
@weightings = weightings || ScoreCalculator.default_score_weights
end
# Calculate the score for all posts based on the weightings
def calculate
# First update the scores of the posts
exec_sql(post_score_sql, @weightings)
# Update the percent rankings of the posts
exec_sql("UPDATE posts SET percent_rank = x.percent_rank
FROM (SELECT id, percent_rank()
OVER (PARTITION BY topic_id ORDER BY SCORE DESC) as percent_rank
FROM posts) AS x
WHERE x.id = posts.id")
# Update the topics
exec_sql "UPDATE topics AS t
SET has_best_of = (t.like_count >= :likes_required AND
t.posts_count >= :posts_required AND
x.max_score >= :score_required),
score = x.avg_score
FROM (SELECT p.topic_id,
MAX(p.score) AS max_score,
AVG(p.score) AS avg_score
FROM posts AS p
GROUP BY p.topic_id) AS x
WHERE x.topic_id = t.id",
likes_required: SiteSetting.best_of_likes_required,
posts_required: SiteSetting.best_of_posts_required,
score_required: SiteSetting.best_of_score_threshold
# Update percentage rank of topics
exec_sql("UPDATE topics SET percent_rank = x.percent_rank
FROM (SELECT id, percent_rank()
OVER (ORDER BY SCORE DESC) as percent_rank
FROM topics) AS x
WHERE x.id = topics.id")
end
private
def exec_sql(sql, params=nil)
ActiveRecord::Base.exec_sql(sql, params)
end
# Generate a SQL statement to update the scores of all posts
def post_score_sql
"UPDATE posts SET score = ".tap do |sql|
components = []
@weightings.keys.each do |k|
components << "COALESCE(#{k.to_s}, 0) * :#{k.to_s}"
end
sql << components.join(" + ")
end
end
end