在一個大的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
兩次。
我在某種程度上取決於你如何使用它。如果你做了'Foo.new(hash_of_stuff_i_plucked_from_the_db)',那麼記錄的行爲就像是一條新記錄,而不是從數據庫中取出。當涉及到回調時,這會給您帶來意想不到的行爲,當您將其傳遞給表單時會發生什麼情況。 – max
由於'.joins'創建了一個'LEFT INNER JOIN'並且你可以將它寫成'.joins(),所以代碼'joins(「INNER JOIN bars on foos.bar_id = bars.id」)' :條)'。 '.joins'和'.eager_load'之間的主要區別是'.joins'使用'INNER'和'.eager_load''OUTER'。 – max
'.select'和'.pluck'之間的區別在於select返回一個'ActiveRecord :: Relation','.pluck'返回一個數組(數組)。您可以使用'.select'來告訴AR到底要採集哪些列。例如'@foo = Foo.select('foos.id,foos.baz,bars.id,bars.baz')。joins(:bars)'只會加載名爲的列。 – max