将两个不同的ActiveRecord :: Relation“追加”到一个ActiveRecord :: Relation中

时间:2013-10-11 18:09:38

标签: ruby-on-rails ruby activerecord

我遇到与ICTylor's post here类似的情况。

所以我有:

user1=User.find(1);
user2=User.find(2);
written=Micropost.where("user_id=2");
written.class #=> ActiveRecord::Relation
written.length   #=> 50
replied=Micropost.where("response = 2 ")  #=> 1
replied.class #=> ActiveRecord::Relation

现在,如果我:

alltogether= written + replied;
alltogether.class #=> Array
alltogether.length #=> 51

但是我想要的东西相当于:

all_sql= Micropost.where("user_id = 2  OR response = 2")
all_sql.class #=> ActiveRecord::Relation
all_sql.length #=> 51

换句话说我想以某种方式追加一个Micropost.where(...)找到的记录到另一个Micropost.where(...)找到的记录 ActiveRecord :: Relation 对象。导致相当于 all_sql ,但分两步完成。

一点解释。此部分应用程序旨在为回复消息提供类似于Twitter的功能。

例如:当User.id = 1的用户将此邮件发送至User.id=2时:

@2: hey this is a reply.

应用程序将使用以下参数创建Micropost

@post= Micropost.create(user:1, content:"@2: hey this is a reply", response: 2)

所以response只是表示回复的 receiver id。如果邮件不是回复类型,则为response = nil

遵循这个想法,我希望能够:

def replies_to(user)
  Micropost.where("response = #{user.id}")
end

def written_posts_by(user)
  Micropost.where("user_id = #{user.id}")
end

def interesting_posts(user)
  replies= replies_to(user)
  written= written_posts_by(user)
  #And now the question arises!

  new_relation= replies union written#<---How can I do this!?
end

3 个答案:

答案 0 :(得分:1)

This blog post讨论了此问题,并提供patch以在ActiveRecord中启用可链接的联盟。

答案 1 :(得分:1)

我认为现在有一种方法可以在ActiveRecord :: Relation上使用内置的Rails来建立联合。你最好用OR写一个查询,即使它不是那么干。

使用arel:

def interesting_posts(user)
  posts = Micropost.arel_table
  Micropost.where(posts[:response].eq(user.id).or(posts[:user_id].eq(user.id)))
end

使用SQL:

def interesting_posts(user)
  Micropost.where('response = ? OR user_id = ?', user.id, user.id)
end

答案 2 :(得分:1)

设计本身存在一些问题,如果帖子回复两个或更多接收者怎么办?表中也会有太多的空数据,这是不好的。

无论如何,基于当前的设计仅允许一个接收器,模型需要进行一些更改。

class User < ActiveRecord::Base
  has_many :posts
  has_many :replies, class_name: 'Post', foreign_key: 'response_id'

  def written_posts
    posts
  end

  def posts_replied_to_me
    replies
  end
end

上述变化的注释:

  1. API在User表中更好,因此它们不需要像(user)
  2. 这样的参数
  3. 通过关联,上述两种公共方法实际上是不必要的,仅用于目的。您可以直接从关联API访问这些帖子。
  4. 现在为interesting_posts。由于上述重构,您不再依赖上述方法来构建聚合查询,因为它们具有不同的结构。如Mori所提到的那样修补是一种解决方案,但如果可能的话,我自己也不愿意触摸libs。

    我更希望使用一种方法来聚合专用于此案例的查询。

    def interested_posts
      Post.where(interesting_criteria)
    end
    
    private
    def interesting_criteria
      conditions = []
      conditions << written_by_me
      conditions << replied_to_me
      conditions << foo_bar
      conditions.join(' AND ')
    end
    
    def written_by_me
      "user_id = #{self.id}"
    end
    
    def replied_to_me
      "response_id = #{self.id}"
    end
    
    def foo_bar
      "feel free to add more"
    end