2

所以我在这里有一个社交书签式的设置。4 个表:帖子、用户、评论和投票。主页只是指向帖子模型的索引页面并在Post.all数组上运行一个循环并显示一些基本信息:title, upvotes- downvotes(我称它为帖子的“分数”)。

现在我需要做的是通过它们的分数除以它们的相对创建时间(自页面加载以来创建这篇文章以来的秒数?)在模型中排序这些帖子(我猜)。Railscreated_at在我的帖子对象中给了我一个列,我有一个投票表,如下所示:

帖子模型

class Post < ActiveRecord::Base
  attr_accessible  :id, :title, :url, :user_id, :comment_count, :agreement
  has_many :votes
  has_many :comments
  belongs_to :user
end

投票模型

class Vote < ActiveRecord::Base
  attr_accessible :direction, :post_id, :vote_type, :user_id
  belongs_to :user
  belongs_to :post
  belongs_to :comment
end

post 模型中的 ID 对应于投票模型中的 post_id。

:direction是一个布尔值。0与赞成投票和反对投票1相关。Post_ID 是投票的帖子的 ID。

我应该如何排序帖子,以便他们在主页上的位置由他们的创建时间和他们的赞成票 - 反对票决定?

def index
    @posts = Post.join(:votes).order(<???>)
end

ETA:@shweta 在加入帖子和投票表方面走在了正确的轨道上。但是,他的答案的选择部分不起作用。

4

3 回答 3

4

免责声明:我不知道 ruby​​ 或 rails,只知道 SQL。

shweta 的答案似乎非常接近,所以我将尝试用一些 SQL hack 和斜线来完成它:

按upvotes 排序,然后created_at

@posts = Post.joins(:votes).select("posts.id,sum(if(direction = 0, 1, -1)) as score,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("score DESC,posts.created_at")

此外,要包含零票的帖子,您需要进行左连接:

@posts = Post.joins("LEFT JOIN votes ON posts.id = votes.post_id").select("posts.id,sum(if(direction = 0, 1, -1)) as score,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("score DESC,posts.created_at")

更新:修复未投票帖子的排序:

@posts = Post.joins("LEFT JOIN votes ON posts.id = votes.post_id").select("posts.id,sum(if(direction = 0, 1, if(direction is null, 0, -1))) as score,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("score DESC,posts.created_at")

更新 2:提高排名

如果您排除较旧的项目,您最终可能会得到一个空列表(例如在安静的日子),这可能不是您想要的。您最好添加另一列进行排名,如下所示:

@posts = Post.joins("LEFT JOIN votes ON posts.id = votes.post_id").select(
    "posts.id," +
    "sum(if(direction = 0, 1, if(direction is null, 0, -1))) as score," +
    "(sum(if(direction = 0, 1, if(direction is null, 0, -1))) * " +
    " if(unix_timestamp() - unix_timestamp(posts.created_at) < 7200, 3, 1)) as rank," +
    "posts.created_at,OTHER_ATTRS_YOU_NEED"
).group("posts.id").order("rank DESC,posts.created_at")

出于排名目的,这会使不到 2 小时的帖子得分增加三倍。

于 2013-01-16T09:06:48.527 回答
1

按upvotes 排序,然后created_at

@posts = Post.joins(:votes).select("posts.id,count(votes.id) AS vote_count,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("vote_count DESC,posts.created_at")

按 created_at 排序,然后投票

@posts = Post.joins(:votes).select("posts.id,count(votes.id) AS vote_count,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("posts.created_at, vote_count DESC")
于 2013-01-11T07:49:14.600 回答
1

所以你想订购rating/(time elapsed since creation)。它可以在纯 Ruby 中使用 sort_by 完成,但不如原始 sql 高效。

所以......在数据库上执行此操作需要计算自 Post#creation 以来经过的时间。在 Mysql 上,您有TIMEDIFF(在 Postres/Oracle/etc 上可能会有所不同)用于这种计算。SQL 或多或少包括(在轻量级/伪代码中):

class Post
  def self.order_by_popularity
    score        = "sum(if(direction = 0, 1, -1)) as score"
    votes_count  = "count(votes.id) as votes_count"
    time_elapsed = "ABS(Timediff(#{Time.now}, created_at)) as time_elapsed"

    select("posts.*, votes.id, #{score}, #{votes_count}, #{time_elapsed}").
    join(:votes).group("votes.id").
    order "ABS(votes_count/time_elapsed)"
  end
end

更新:汇总您获得上述所有建议(顺便说一句,默认情况下 join(:votes) 是左连接)。我想知道有没有什么陷阱......

正如之前的评论所暗示的,vote_count 将来自 Posts 上的缓存列或 join+group 事物。

旁注:我成功地使用了这种类型的查询,所以我希望为您指明正确的方向,但坦率地说,这可能需要比典型的 stackoverflow 帮助提供的更多的测试/试验。

于 2013-01-15T22:39:52.493 回答