Rails ActiveRecord eager_load с INNER JOIN

В большом приложении Rails я заметил, что у нас есть кусок кода, который производит большой ActiveRecord::Relation, Он использует пользовательские фрагменты SQL в .joins() звонки - что-то вроде этого:

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

(Обратите внимание, что JOINs сложнее, чем показано в этом примере; иначе я бы, очевидно, просто сделал 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 дважды.

1 ответ

Ну, я закончил реструктуризацию foos метод, чтобы он мог просто выполнить includes там. Все еще не супер счастлив со всеми полями, являющимися SELECTЭд, но я думаю, это то, что вы получаете за использование ActiveRecord вместо чего-то вроде Sequel,

Другие вопросы по тегам