2013-11-21 87 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會從其魔術師帽子中拉出什麼樣的別名...... –