合并两个ActiveRecord :: Relation对象

时间:2012-03-02 21:46:09

标签: ruby-on-rails rails-activerecord arel

假设我有以下两个对象:

first_name_relation = User.where(:first_name => 'Tobias') # ActiveRecord::Relation
last_name_relation  = User.where(:last_name  => 'Fünke') # ActiveRecord::Relation

是否可以组合这两个关系来生成一个包含两个条件的ActiveRecord::Relation对象?

注意:我知道我可以链接这些行为,我真正感兴趣的是我有两个单独的ActiveRecord::Relation对象。 < / p>

9 个答案:

答案 0 :(得分:171)

如果要使用AND(十字路口)进行组合,请使用merge

first_name_relation.merge(last_name_relation)

如果您想使用OR(联合)进行组合,请使用or(在ActiveRecord 5+中提供,或在4.2中通过where-or backport提供):

first_name_relation.or(last_name_relation)

答案 1 :(得分:35)

Relation对象可以转换为数组。这无法在之后使用任何ActiveRecord方法,但我不需要。我这样做了:

name_relation = first_name_relation + last_name_relation

Ruby 1.9,rails 3.2

答案 2 :(得分:18)

merge实际上不像OR那样有用。这只是交集(AND

我努力解决这个问题,将ActiveRecord :: Relation对象合并为一个,但我找不到任何有用的解决方案。

我没有从这两个集合中搜索创建联合的正确方法,而是专注于集合的代数。您可以使用De Morgan's law

以不同的方式执行此操作

ActiveRecord提供合并方法(AND),您也可以使用not method或none_of(NOT)。

search.where.none_of(search.where.not(id: ids_to_exclude).merge(search.where.not("title ILIKE ?", "%#{query}%")))

你在这里(A u B)'= A'^ B'

更新: 上述解决方案适用于更复杂的情况。 在你的情况下,像这样就足够了:

User.where('first_name LIKE ? OR last_name LIKE ?', 'Tobias', 'Fünke')

答案 3 :(得分:14)

通过使用Rails的内置 Arel ,即使在很多奇怪的情况下,我也能够做到这一点。

User.where(
  User.arel_table[:first_name].eq('Tobias').or(
    User.arel_table[:last_name].eq('Fünke')
  )
)

使用Arel的合并两个ActiveRecord关系。


如此处所示,合并对我不起作用。它从结果中删除了第二组关系对象。

答案 4 :(得分:13)

有一个名为active_record_union的宝石 这可能就是你要找的东西。

它的示例用法如下:

current_user.posts.union(Post.published)
current_user.posts.union(Post.published).where(id: [6, 7])
current_user.posts.union("published_at < ?", Time.now)
user_1.posts.union(user_2.posts).union(Post.published)
user_1.posts.union_all(user_2.posts)

答案 5 :(得分:7)

这就是我处理&#34;的方式。如果你使用pluck获取每个记录的标识符,将数组连接在一起然后最终对这些连接的id进行查询:

  transaction_ids = @club.type_a_trans.pluck(:id) + @club.type_b_transactions.pluck(:id) + @club.type_c_transactions.pluck(:id)
  @transactions = Transaction.where(id: transaction_ids).limit(100)

答案 6 :(得分:6)

如果你有一组activerecord关系,想要将它们全部合并,你可以

array.inject(:merge)

答案 7 :(得分:0)

蛮力:

first_name_relation = User.where(:first_name => 'Tobias') # ActiveRecord::Relation
last_name_relation  = User.where(:last_name  => 'Fünke') # ActiveRecord::Relation

all_name_relations = User.none
first_name_relation.each do |ar|
  all_name_relations.new(ar)
end
last_name_relation.each do |ar|
  all_name_relations.new(ar)
end

答案 8 :(得分:0)

我偶然发现了这个问题,并且普遍接受的回应从未对我有用。

在接受的答案中提出的解决方案似乎最有效:

first_name_relation.or(last_name_relation)

但我永远无法让这个工作,并且总是会收到一个未定义的方法&#39; &#39;或&#39;的错误。

我的解决方案:

combined_relation = first_name_relation + (last_name_relation - first_name_relation)

这基本上给出了原始查询之间UNION的结果,并且关系对象仍然是正确的ActiveRecord对象。

这与@thatmiddleway提供的答案类似,但我需要添加减法来获取DISTINCT对象集合。这确实将组合的集合转换为ARRAY,但我仍然可以在每个&#39;中的各个实例上调用模型方法。在我看来,迭代器解决了我的问题。

这是我的第一个StackOverflow答案(喘气!),所以我欢迎你的反馈!