如何根据提交后的投票和时间实施项目排序?

时间:2015-08-17 16:27:56

标签: ruby-on-rails sorting

许多内容聚合器,如reddit或hackernews,使用基于upvotes数量和提交后的时间组合的算法对故事进行排序。实现这种排序的最简单方法是在数据库中创建一个函数,该函数将计算每个项目的排名并基于此排序 - 但这很快就会变得低效,因为它需要计算所有项目的排名。每个查询。

另一种方法是保存数据库中每个项目的排名。但我什么时候会重新计算呢?如果我只是在提交投票时才这样做,那么那些没有投票的投票将保留相同的排名,即使它们因为时间的推移而应该下降。那么,实现这种排序的最佳方式是什么?我不是问什么是最好的算法,而是如何应用它。

1 个答案:

答案 0 :(得分:0)

您应该使用Rails的标准counter_cache存储投票数:

class Vote < ActiveRecord::Base
  belongs_to :post, counter_cache: true
end

class Post < ActiveRecord::Base

  # Migration for this model should create 'votes_count' column:
  #   t.integer :votes_count

  has_many :votes, dependent: :destroy
end

如何应用复杂的算法,特别是有时间作为其变量/参数之一的算法,我怀疑有更好的方法(一般来说 - 你可以提出更简单的算法来节省工作量),而不是重新计算每个午夜。

在Rails中,您可以使用whenever gem:

# config/schedule.rb
every 1.day, at: '12am' do
  runner Post.update_rating
end

# post.rb
class Post < ActiveRecord::Base

  # Add 'rating' column in migration
  #   t.integer :rating

  def self.update_rating
    # Ruby-way
    # self.find_in_batches do |batch|
    #   batch.each do |post|
    #     post.update(rating: (post.votes.to_f / (Date.today - post.created_at.to_date)).to_i)
    #   end
    # end

    # SQL-way (probably a quicker one, but you should think on how
    #  not to lock your database for a long period of time)

    # SOME_DATE_FUNCTION will depend on DB engine that you use
    self.update_all(:rate, "votes_count / SOME_DATE_FUNCTION(created_at, NOW())")
  end
end

<强> UPD 即可。回应评论中的观点。

  

如果我们处理的网站与reddit一样动态,我们应该更频繁地更新评分。

据我了解,您可以将Reddit的主页视为按评级排序的帖子的参考(使用投票计数和年龄惩罚计算)。我严重怀疑Reddit每天更频繁地重新计算“年龄惩罚”。当然,它可以实时计算投票数(这就是你可以在Rails中使用counter_cache轻松完成的工作)。

因此,当新记录突然获得1000个投票时,它会立即进入主页面。但是每个午夜都会收到罚款,相当于当天收到的20%的选票。

嗯,你说我们不会讨论具体的算法,这可能非常复杂:)

  

此外,您是否认为除了每次帖子投票时都要重新计算排名?

当然,没有什么可以阻止您实时更新评分的“投票计数”部分(并根据评分结果显示记录)。然后,您可以每天为每条记录计算一次“年龄惩罚”,稍微下沉旧帖子。