2017-08-16 69 views
2

在一個大的Rails應用程序中,我注意到我們有一段代碼產生一個大的ActiveRecord::Relation。它使用自定義的SQL代碼片段在.joins()通話,這樣的事情:Rails的ActiveRecord eager_load與INNER JOIN

def foos 
    Foo. 
    joins("INNER JOIN bars ON foos.bar_id = bars.id"). 
    joins("INNER JOIN baz ON bars.baz_id = baz.id"). 
    where(<some condition on bars>) 
end 

(注意JOIN s爲比這個例子中,如圖比較複雜,否則我明明只是做Foo.joins(bar: :baz))。現在,在一些ActiveRecord::Relation被使用的地方,那很好。但在其他情況下,我們希望在Foo結果集上預先加載bars關聯。

有沒有辦法做這樣的事情:

def scope_with_bars_eager_loaded 
    foos.eager_load(:bars, using_existing_join_in_query: true) 
end 

我可以想出最接近的事是:

def array_with_bars_eager_loaded 
    foos.pluck(<fields we need>).map do |fields| 
    bar = Bar.new(<get bar data from fields>) 

    # This of course doesn't behave as well as a Foo 
    # that we've loaded normally in regards to callbacks, 
    # fields we didn't SELECT, etc. But it's probably 
    # fine enough for this use-case (we're using this 
    # data to render a page). 
    Foo.new(<get foo data from fields>, bar: bar) 
    end 
end 

這是很多更復雜,也沒有給我們作爲ActiveRecord::Relation的好處。任何幫助在這裏將不勝感激!

-

注:

是避免‘在一個查詢數據庫中的每個列有時多次負載,’Rails的默認行爲,任何建議都特別讚賞(這就是爲什麼我用.pluck代替.select,因爲.select構造查詢,即使明確地告訴它不要,也會加載Foo中的所有內容)。示例:Foo.includes(:bar).where(bars: { condition: true }).select(:id)選擇foos中的每一列,並選擇foos.id兩次

+0

我在某種程度上取決於你如何使用它。如果你做了'Foo.new(hash_of_stuff_i_plucked_from_the_db)',那麼記錄的行爲就像是一條新記錄,而不是從數據庫中取出。當涉及到回調時,這會給您帶來意想不到的行爲,當您將其傳遞給表單時會發生什麼情況。 – max

+0

由於'.joins'創建了一個'LEFT INNER JOIN'並且你可以將它寫成'.joins(),所以代碼'joins(「INNER JOIN bars on foos.bar_id = bars.id」)' :條)'。 '.joins'和'.eager_load'之間的主要區別是'.joins'使用'INNER'和'.eager_load''OUTER'。 – max

+0

'.select'和'.pluck'之間的區別在於select返回一個'ActiveRecord :: Relation','.pluck'返回一個數組(數組)。您可以使用'.select'來告訴AR到底要採集哪些列。例如'@foo = Foo.select('foos.id,foos.baz,bars.id,bars.baz')。joins(:bars)'只會加載名爲的列。 – max

回答

0

那麼,我最終重組了我的foos方法,以便它可以簡單地在那裏執行includes。仍然不是很高興所有的領域是SELECT版,但我想這就是你使用ActiveRecord而不是像Sequel