在一个庞大的Rails应用程序中,我注意到我们有一段代码会生成一个很大的ActiveRecord::Relation
。它在.joins()
语句中使用自定义的SQL代码块,就像这样:
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
比所显示的更复杂;否则我显然会只执行 Foo.joins(bar: :baz)
。) 现在,在使用 ActiveRecord::Relation
的某些地方,这是可以的。 在其他情况下,我们希望将bars
关联预加载到Foo
结果集上。是否有任何方法可以做到这一点: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)
,那么这条记录会像新记录一样运作,而不是从数据库中获取的记录。这会导致在回调和将其传递给表单时出现意外行为。 - maxjoins(“INNER JOIN bars ON foos.bar_id = bars.id”)
也很奇怪,因为.joins
创建了一个LEFT INNER JOIN
,你可以简单地将其写为.joins(:bars)
。.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