Ruby-on-rails 合并两个 ActiveRecord::Relation 对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9540801/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 02:57:34  来源:igfitidea点击:

Combine two ActiveRecord::Relation objects

ruby-on-railsrails-activerecordarel

提问by Patrick Klingemann

Suppose I have the following two objects:

假设我有以下两个对象:

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

is it possible to combine the two relations to produce one ActiveRecord::Relationobject containing both conditions?

是否有可能ActiveRecord::Relation将这两种关系结合起来产生一个包含这两种条件的对象?

Note: I'm aware that I can chain the wheres to get this behavior, what I'm really interested in is the case where I have two separate ActiveRecord::Relationobjects.

注意:我知道我可以链接 wheres 来获得这种行为,我真正感兴趣的是我有两个单独的ActiveRecord::Relation对象的情况。

回答by Andrew Marshall

If you want to combine using AND(intersection), use merge:

如果要使用AND(intersection) 组合,请使用merge

first_name_relation.merge(last_name_relation)

If you want to combine using OR(union), use or?:

如果要使用OR(union)进行组合,请使用? or

first_name_relation.or(last_name_relation)


?Only in ActiveRecord 5+; for 4.2 install the where-orbackport.

? 仅在 ActiveRecord 5+ 中;对于 4.2 安装where-orbackport。

回答by thatmiddleway

Relation objects can be converted to arrays. This negates being able to use any ActiveRecord methods on them afterwards, but I didn't need to. I did this:

关系对象可以转换为数组。这否定了之后能够对它们使用任何 ActiveRecord 方法,但我不需要。我这样做了:

name_relation = first_name_relation + last_name_relation

Ruby 1.9, rails 3.2

红宝石 1.9,导轨 3.2

回答by Tomasz Ja?kiewicz

mergeactually doesn't work like OR. It's simply intersection (AND)

merge实际上不像OR. 它只是交叉点 ( AND)

I struggled with this problem to combine to ActiveRecord::Relation objects into one and I didn't found any working solution for me.

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

Instead of searching for right method creating an union from these two sets, I focused on algebra of sets. You can do it in different way using De Morgan's law

我没有寻找从这两个集合中创建联合的正确方法,而是专注于集合的代数。你可以使用德摩根定律以不同的方式做到这一点

ActiveRecord provides merge method (AND) and also you can use not method or none_of (NOT).

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}%")))

You have here (A u B)' = A' ^ B'

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

UPDATE: The solution above is good for more complex cases. In your case smth like that will be enough:

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

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

回答by 6ft Dan

I've been able to accomplish this, even in many odd situations, by using Rails' built-in Arel.

我已经能够通过使用 Rails 的内置Arel来实现这一点,即使在许多奇怪的情况下。

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

This merges both ActiveRecord relations by using Arel's or.

这通过使用 Arel 的来合并两个 ActiveRecord 关系。



Merge, as was suggested here, didn't work for me. It dropped the 2nd set of relation objects from the results.

正如这里建议的那样,合并对我不起作用。它从结果中删除了第二组关系对象。

回答by echo

There is a gem called active_record_unionthat might be what you are looking for.

有一个名为active_record_union的 gem 可能正是您要找的。

It's example usages is the following:

它的示例用法如下:

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)

回答by daveomcd

This is how I've "handled" it if you use pluck to get an identifier for each of the records, join the arrays together and then finally do a query for those joined ids:

如果您使用 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)

回答by stevenspiel

If you have an array of activerecord relations and want to merge them all, you can do

如果您有一系列 activerecord 关系并希望将它们全部合并,您可以这样做

array.inject(:merge)

回答by Richard Grossman

Brute force it:

蛮力吧:

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

回答by Eric Powell

I've stumbled upon this issue a handful of times and the generally accepted responses never quite worked for me.

我曾多次偶然发现这个问题,但普遍接受的回应对我来说从来没有完全奏效。

The proposed solution in the accepted answer seemed like it would work best:

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

first_name_relation.or(last_name_relation)

But I could never get this to work and would always receive an 'undefined method' error for 'or'.

但是我永远无法让它工作,并且总是会收到“或”的“未定义方法”错误。

My solution:

我的解决方案:

combined_relation = first_name_relation + (last_name_relation - first_name_relation)

This gives me essentially the results of a UNION between the original queries and the relation objects are still the correct ActiveRecord objects.

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

This is similar to the answer provided by @thatmiddleway, though I needed to add the subtraction to get a DISTINCT collection of objects. This does convert the combined collection to an ARRAY, but I could still call model methods on on the individual instances in an 'each' iterator in my view, which solved my issue.

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

This is my first StackOverflow answer (gasp!), so I welcome your feedback!

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