从多个关系中检索,ruby on rails

时间:2009-08-16 19:44:13

标签: ruby-on-rails associations

用户(has_many :userhas_many :comments) 发帖(belongs_to :userhas_many :comments) 评论(belongs_to :userbelongs_to :post

现在我正在尝试检索所有帖子(至少有一条用户评论)更新了该帖子中用户的上一条评论的评论(我将使用用户的updated_at属性)

示例: 发布A

用户X,Y

X在A

中发表评论

然后Y在A

中发表评论

现在我想要检索帖子A,因为在用户X的最后评论之后在A中发布了新评论。

同样,我想检索在用户评论后发布评论的所有帖子。 (每次他在任何帖子中发表评论时,我都更新了updated_at用户的值 (我还更新了帖子的updated_at值,每次有人发布帖子中的评论)

我已经了解了如何在RoR中实现这一目标,但是没有任何线索,所以最后我转而在StackOverflow中提供帮助:)

先谢谢你们

2 个答案:

答案 0 :(得分:0)

SQL魔术:

class User
  def posts_with_comments_since_my_last_comment_in_that_post
     Post.find_by_sql([%Q| SELECT DISTINCT posts.* from posts INNER JOIN users on 
       (posts.user_id = users.id) INNER JOIN comments on 
       (comments.user_id = users.id AND comments.post_id = posts.id) 
       WHERE (posts.updated_at > comments.updated_at) AND (users.id = ?)|, id])
  end

  def posts_with_comments_since_my_last_comment
    Post.find_by_sql([%Q| SELECT DISTINCT posts.* from posts INNER JOIN users on 
         (posts.user_id = users.id) INNER JOIN comments on 
         (comments.user_id = users.id AND comments.post_id = posts.id) 
         WHERE (posts.updated_at > user.updated_at) AND (users.id = ?)|, id])
  end
end

您可以通过创造性地使用缓存来避免这种情况,但我不会建议。 确保您的表格已正确编入索引。如果您运营的网站包含大量评论,则可能会非常非常错误

答案 1 :(得分:0)

帕特里克有一些很好的建议。但我认为他误解了你的要求。当他的查询将帖子加入发布帖子的用户时,而不是评论他们的用户。他走在正确的轨道上,他对索引的肯定是正确的。

以下内容不起作用,因为您尝试在Comment对象上获取帖子。

@post_ids = Comment.all(:select => "DISTINCT post_id",
  :conditions => ["user_id = ? AND updated_at > ?", 
  @userinfo.id, @userinfo.updated_at.to_date.to_time]) 

@posts = Post.find(@post_ids)

您可以将所有评论映射到帖子ID。 即:Post.find(@post_ids.map{&:post_id})但这样效率低,需要两次数据库访问并实例化命中注释。

相反,您应该使用命名范围或某些内容根据您的条件选择帖子。

class Post < ActiveRecord::Base
  ...
  #this is what you asked for.
  named_scope :with_unseen_comments_for_user, lamda do |user|
    {
     :select => "DISTINCT posts.*", :joins => "INNER JOIN comments, 
        comments others_comments, users ON (comments.user_id = users.id AND 
        others_comments.post_id = post.id AND comments.post_id = posts.id", 
      :conditions => ["users.id = ? AND 
        comments_posts.updated_at > users.updated_at", user]
    }
  end

end

#usage:
Post.updated_after_users_last_comment_in_post(@user)

创建高效查询需要复杂的连接语句,因为您希望连接工作列表:

其他评论&lt; --- post_id - id ---&gt;帖子&lt; --- id - post_id ---&gt;用户评论&lt; --- user_id - id ---&gt;用户

假设我的SQL是我记忆中的,那么这应该检索给定用户评论的所有帖子,并且他的最后一篇帖子是在同一帖子上的其他评论之前发布的。

无论如何,当用户在多个线程中发帖时,您将遇到问题。所以你可能想重新考虑你的关系并更新方法。

例如,如果发生以下情况,您的用户将不会收到有关帖子A的新评论的通知:

用户X对帖子A的评论

用户Y对帖子A的评论

在注意到用户Y对帖子A发表评论之前,用户X对帖子B发表评论。

您可以通过在评论中添加last_seen_at字段来解决此问题,并在所有者查看评论时对其进行更新。这对于:after_filter来说相当微不足道。事实上,它使命名范围更加简单。因为我们不需要将用户记录拖到查询中来比较日期。

  named_scope :unseen_comments, lamda do |user|
    {
     :select => "DISTINCT posts.*", :joins => [:comments, :comments], 
      :conditions => ["comments.user_id = ? AND 
        others_comments.updated_at > comments.last_seen_at", user]
    }
  end

:joins => [:comments, :comments]有点奇怪,但它会将评论加入查询中的帖子两次,第二次将其别名为comments_posts。它没有在find或named_scope中具体记录,但它使用与关联连接参数相同的语法。