2013-11-21 58 views
1

我有一个ActiveRecord类反身协会(注:类名删节不披露的理由):ActiveRecord的:与条件LEFT OUTER自连接在连接表

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个查询)母公司没有得到渴望加载。

现在,我或多或少的工作解决方案是使用子查询(采用符合父条件的ID,然后选择parent_id IN这个集合的儿童),但我想知道是否有一个更合适/优雅的一个。

回答

1

我还没有找到一种方法来做到这一点使用merge(这似乎坚持使用模型表名),但你可以使用这个和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) 

这个应该正确执行所有热切加载。

+0

+1,谢谢你的关注。我没有提到这个解决方案,但我也测试了它(我只是缺少参考部分)。我想这是对现在我所拥有的一种改进,但我并不是很喜欢“猜测”AR会从其魔术师帽子中拉出什么样的别名...... –