我试图了解ActiveRecord Relations合并的工作原理,因为似乎缺乏文档和示例。它的文档似乎非常简单,它返回调用对象和合并参数"other"的交集。
我有一个简单的用户类和适当的迁移
class User < ApplicationRecord
...
end
在大多数情况下,合并的行为似乎与我的预期完全一样,但在检查后它的行为出乎意料。如果我建立没有重叠项目的平凡关系,我希望它们的交集将是空关系
relation1 = User.where(id: 1)
relation2 = User.where(id: 2)
relation1.merge(relation2) # returns Relation containing User with id: 2
relation2.merge(relation1) # returns Relation containing User with id: 1
这就是它的短处。我尝试做的更长版本是我使用 Pundit 来设置用户的策略范围,在本例中是属于用户的所有财务帐户。然后在我的控制器中,我按类型(储蓄、支票、信用等(过滤了金融账户的索引操作的路由。因此,我希望用户只看到属于他们的那些过滤的财务帐户,这是两组的交集:1(所有支票账户,2(属于用户的所有金融账户。所以看起来合并应该可以解决问题,但它的工作方式似乎与我预期的不同。
Rails 5.1.6
ruby 2.3.3p222
从我从文档中可以看出的一点点,由于"其他"关系是 ActiveRecord 关系(而不是数组(,因此它不会严格生成交集。相反,它会合并条件。在我的琐碎示例中,由于条件非常相似(即 where(id: ?(,合并过程不会 AND 条件,而是用参数对象的条件替换调用对象的条件。
解决此问题的一种方法是使用 to_a 将参数对象转换为 Array。在这种情况下,相同的合并函数会计算正确的交集。
relation1 = User.where(id: 1)
relation2 = User.where(id: 2)
relation1.merge(relation2.to_a) # returns an empty Array (note: not a Relation)
这样做的缺点是查询不是完全在数据库中完成的,因此效率较低。
这似乎也解释了为什么对于我更复杂的合并,它确实经常按预期工作。更复杂的合并将具有更复杂的条件,这些条件很少相似到足以像在更简单的情况下那样被覆盖/替换。