我有一个带有反身关联的ActiveRecord类(注意:由于不公开原因,类名被编辑):
class Thing < ActiveRecord::Base
belongs_to :parent,
class_name: 'Thing'
has_many :children,
class_name: 'Thing',
foreign_key: :parent_id
scope :children, ->{ where(arel_table[:parent_id].not_eq(nil)) }
end
我想让所有的孩子,左外连接到他们的父母,如果它存在,父母的附加条件,同时仍然急于加载父母。
通常在这种情况下我会做类似的事情:
Thing.includes(:other_thing).merge(OtherThing.some_scope)
但在这种情况下不可能:因为这是一个自我加入,如果我合并Thing
的范围,条件将适用于孩子的事情,而不是父母。
我尝试在原始SQL中执行此操作:
scope :some_scope, ->{
children.joins(<<-SQL).references(:parent)
LEFT OUTER JOIN things AS parents
ON things.parent_id = parents.id
AND parents.some_field IN ('foo', 'bar')
SQL
}
查询没问题,但似乎父进程没有得到加载(在此范围内尝试map(&:parent)
时n + 1查询)。
目前,我或多或少的工作解决方案是使用子查询(与父条件匹配的拔符号,然后选择具有parent_id IN
此子集的子项),但我想知道是否有一个更合适/优雅的。
答案 0 :(得分:1)
我没有找到使用merge
执行此操作的方法(这似乎坚持使用模型table_name),但您可以使用includes
执行此操作并围绕预期别名构建条件 - 您需要根据自己的命名修改此别名:
User.includes(:parent).
where('parents_users.some_field IN (?)', ['foo', 'bar']).
references(:parents_users)
但是,您可能需要注意NULL父ID,因为条件将添加到WHERE子句而不是LEFT JOIN本身:
User.includes(:parent).
where('parents_users.some_field IN (?) OR parents_users.id IS NULL', ['foo', 'bar']).
references(:parents_users)
这应该正确执行所有预先加载。