Включить левую объединенную ассоциацию has_many с условием - PullRequest
0 голосов
/ 20 июня 2020

Существует ассоциативный запрос, который я, кажется, не могу выполнить без запуска запроса N + 1.

Предположим, я принимаю Стороны. У меня много Друзей, и каждый раз, когда друг приходит на вечеринку, они создают Присутствие.

И так:

Presence.belongs_to :party
Presence.belongs_to :friend

Friend.has_many :presences
Party.has_many :presences

Пока все хорошо.

I хочу получить список всех моих друзей, зная, присутствуют ли они на этой вечеринке, без запуска запроса N + 1.

Мой набор данных будет выглядеть так:

friends: [
  {name: "Dave Vlopment", presences: [{created_at: "8pm", party_id: 2012}]},
  {name: "Brett E. Hardproblem", presences: [nil]},
  {name: "Ann Plosswan-Quarry", presences: [{created_at: "10pm", party_id: 2012}]},
  ...
]

и т. Д.

У меня много друзей и, конечно же, много вечеринок. (Это, конечно, вымышленный пример.)

Я бы сделал:

Friend.all.includes(:presence).map{ |them| them.parties }

# But then, `them.parties` is not filtered to tonight's party. 

Friend.all.includes(:presence).map{ |them| them.parties.where(party_id: pid) }

# And there I have an N+1.

Я всегда мог бы отфильтровать на уровне Ruby:

Friend.all.includes(:presence).map{ |them| them.parties.select{ |it| it.party_id = party.id } }

Но это плохо работает с as_json(includes: {}) и так далее. Я обнаружил, что это очень чревато ошибками, так как буду производить расчеты по результатам.

И я делаю много вечеринок, понимаете? (все еще вымышленный)

Если я попал в первый запрос, я потеряю левое соединение:

Friend.all.includes(:presence).where(party: party)

Я понятия не имею, что сегодня вечером, Бретт и другие друзей, которые всегда рядом, отсутствуют. (не гарантируется, что это вымышленный опыт) * ​​1044 *

Я буду видеть только друзей, которые присутствуют.

И если я от go до party, ну, конечно, я тоже не увижу, кто отсутствует.

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

Однако я ищу «первоклассный» способ сделать это в Activerecord, без получения N + 1.

Есть ли способ сделать это, используя только инструменты Activerecord? Я еще ничего не нашел.

1 Ответ

0 голосов
/ 20 июня 2020

Я не уверен, соответствует ли это вашим ожиданиям относительно "первоклассного" пути или нет.

Но вы можете использовать этот подход, чтобы избежать N + 1

  # fetch all friends
  friends = Friend.all

  # fetch all presences. grouped by friend_id
  grouped_presences = Presence.all.group_by(&:friend_id)

  # arrange data
  data = []
  friends.each do |friend|
    json = friend.as_json
    json["presences"] = grouped_presences[friend.id].as_json
    data << json
  end

  puts data

Это выполняет только 2 запроса

SELECT `friends`.* FROM `friends`
SELECT `presences`.* FROM `presences`
...