Рельсы, избегающие связи N + 1 has_one - PullRequest
1 голос
/ 11 марта 2019

У меня есть следующий воображаемый класс

class A < ActiveRecord::Base
  has_many :objects, -> { order(:created_at) }, class_name: 'B'
  has_one :last_object, -> { order(created_at: :desc).limit(1) }, class_name: 'B'

  scope :with_last_object, -> { includes(:last_object) }
end

Я добавил вторую ассоциацию для модели A и область действия, чтобы избежать запроса N + 1 в следующем случае: A.all.map(&:last_object). Поэтому я пишу A.all.with_last_object.map(&:last_object). Но он терпит неудачу: он получает только 1 last_object для всех экземпляров A. Из журналов Postgres

 SELECT  "b".* FROM "b" WHERE "b"."a_id" IN (1, 2, 3, ...) ORDER BY "b"."created_at" DESC LIMIT $1

Есть ли способ избежать проблемы N + 1 в этой ситуации?

Ответы [ 2 ]

1 голос
/ 11 марта 2019

Проблема заключается в том, что вы определили last_object, активная запись которого не может быть преобразована в активную загрузку.

Либо вы усердно играете с самостоятельными объединениями, поэтому вы можете получить несколько хитрых (и, возможно, неэффективных) SQL, таких как

SELECT  "b".* FROM "b"
JOIN "b" AS "b_from_same_a"
  ON "b"."a_id" = "b_from_same_a"."id"
WHERE "b"."created_at" = MAX("b_from_same_a"."created_at")
GROUP BY "b".*

или вы можете явно отследить последний:

class B < ActiveRecord::Base
  after_save :set_last, on: :create

  def set_last
    self.class.where(a_id: a_id).update_all(last: false)
    self.update_column(last: true)
  end
end

и соответствующим образом измените свои области ассоциации.

0 голосов
/ 18 марта 2019

Я закончил с этим (спасибо моему коллеге)

has_one :last_object, -> {
    from(
      <<~SQL
        (select * from
          (select
              *,
              row_number() over(partition by b.a_id order by b.created_at desc) as rn
          from
              b) as inner_b
        where rn = 1) as b
      SQL
    )
  }, class_name: 'B', dependent: :delete

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...