Rails按多个表的属性对帖子进行排序

时间:2013-01-11 06:16:09

标签: mysql ruby-on-rails ruby sorting

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

现在我需要做的就是在(我猜)模型中按照他们的分数除以他们的相对创建时间(这个帖子创建的页面加载后多少秒)来订购这些帖子。 Rails在我的帖子对象中给了我一个created_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
帖子模型中的

ID与投票模型中的post_id相对应。

:direction是一个布尔值。与投票相关联的0和与downvoting相关联的1。 Post_ID是投票的帖子的ID。

我应该如何订购这些帖子,以便他们在主页上的位置取决于他们的创作时间和他们的投票结果?

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

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

3 个答案:

答案 0 :(得分:4)

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

shweta的答案看起来非常接近,所以我会尝试用一些SQL黑客和斜线来完成它:

按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小时的帖子的分数增加了三倍。

答案 1 :(得分: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排序然后upvotes

@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")

答案 2 :(得分:1)

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

所以......在数据库上执行它需要自Post#creation以来计算Times。在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

更新:汇总上面提到的所有建议(BTW join(:votes)默认是左连接)。我想知道是否有任何陷阱......

其中,vote_count将来自帖子的缓存列或加入+群组,如先前​​的评论所示。

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