2017-08-16 42 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