如何获得两个AR关系对象的联合?

时间:2017-05-24 22:39:36

标签: ruby-on-rails rails-activerecord

说我有2个AR查询:

users = User.where(...)
users = [user1, user2, user3]

users = User.some_complicated_query
users = [user3, user4, user5]

说我希望方法返回:

all_users = [user1, user2, user3, user4, user5]

我该怎么做这个联盟?我在Rails 4上,所以我没有or

我希望用户3是唯一的,而不是在最终集中重复。

merge上的文档不是很好但我不认为这是我想要的吗?

3 个答案:

答案 0 :(得分:0)

这取决于这些查询实际上是什么以及它们如何重叠。如果您正在寻找符合 条件的用户,则需要使用or,因为@SteveCarey建议:

regular_users = User.not_admins
admin_users = User.admins
all_users = regular_users.or(admin_users)

这不会执行SQL UNION,但结果在这些示例中将是相同的。如果您要加入来自多个子查询的数据,则只需要一个正确的UNION,在这种情况下,您可以use Arel来处理此问题。

请注意,这与Ruby自己的union运算符完全不同,后者仅保留两个联合对象集中的项。如果您正在寻找该行为,您可以合并范围:

User.not_admins.admins

在这种情况下,正如您所看到的,它不会非常有用。 ;)

其他情况:

对于您正在考虑与相关模型进行比较的情况,您可以使用references(:other_things).merge(OtherThing.condition)获得相当远的距离。

如果您希望从存储在不同表中的两个类似对象返回数据,则应考虑使用single table inheritance在启用结果时启用ActiveRecord以实例化正确的对象类型。

如果您想要来自两个不相关对象的数据,但是您想要合并数据以进行演示或类似,那么您真的想要执行两个查询并将结果合并到Ruby中:

posts = Posts.recent
events = Events.recent
news_feed_items = Set.new(posts) + events
news_feed_items.sort_by { |a,b| a.created_at <=> b.created_at }

答案 1 :(得分:0)

total_users = users1 | users2

或者

total_users = (users1 + users2).uniq

应该最有可能做到这一点。与@ Iceman的解决方案类似,但它产生了一个额外的查询,我不赞成。

另外,试试

users1 = User.where(...)
users2 = User.some_complicated_query.where.not(id: users1.pluck(:id))
new_users = users1 + users2

答案 2 :(得分:0)

  

但我需要返回AR关系,而不是数组。 - Jwan622

正如@SteveCarey建议的那样,你可以通过这个得到联盟:

users1 = User.where(...)
users2 = User.some_complicated_query

all_users = users1 | users2

然后进行额外的查询以获得AR关系:

all_users = User.where(id: all_users)

但使用or会使其更简单。 (升级到Rails 5或安装rails_or gem以使用or

all_users = users1.or(users2)