Запрос ActiveRecord для количества ассоциаций с использованием таблицы соединений

Я работаю над функцией событий и сталкиваюсь с довольно сложным запросом.

Вот модели для справки:

class Event < ApplicationRecord
  has_one :party

  belongs_to :host, foreign_key: :host_id, class_name: 'User'
  belongs_to :convention, optional: true
  has_many :join_requests
  
  ...
end

Я хочу проверить, какие мероприятия принимают участников, проверив member_limit соответствующей стороны.

class Party < ApplicationRecord
  belongs_to :event
  belongs_to :host, foreign_key: :host_id, class_name: 'User'

  has_many :user_parties
  has_many :members, through: :user_parties, source: 'user'

  ...
end

Party имеет целочисленный столбец: member_limit -- который допускает только определенное количество членов группы.

На данный момент у меня есть область действия Event, которая выглядит так:

scope :accepting_members, -> () do
    joins(party: :user_parties).group('parties.member_limit').select('count(user_parties.user_id) < parties.member_limit')
  end

Что не очень хорошо работает.

Я на правильном пути?

Я пробовал использовать агрегат COUNT(), но он возвращает только хэш целых чисел.

🤔 А знаете ли вы, что...
SQL может выполнять соединение данных из нескольких таблиц с помощью операторов UNION и UNION ALL.


53
2

Ответы:

Я думаю, вам не нужны какие-либо сложные соединения, вы можете сделать это вот так.

Поскольку соотношение следующее

  1. В событии есть_одна вечеринка
  2. Сторона has_many UserParty

Вы можете сделать это вот так.

# event.rb
scope :accepting_members, -> { party.user_parties.count < party.member_limit }

Это вернет логическое значение. если лимит участников не достигнут, он вернет true, в противном случае — false.


Решено

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

Один из способов — создать подзапрос для идентификаторов событий:

SELECT events.* 
FROM events
WHERE events.id IN (
  SELECT parties.events_id
  FROM parties
  OUTER LEFT JOIN user_parties ON parties.id = user_parties.party_id
  GROUP BY parties.event_id
  HAVING(
    COUNT(user_parties.user_id) < parties.member_limit
  )
)

Писать это в ActiveRecord/Arel становится немного сложно:

subquery = Party.select(:events_id)
                .left_joins(:user_parties)
                .group(:event_id)
                .having(
                  # COUNT(user_parties.user_id) < parties.member_limit
                  UserParty.arel_table[:user_id].count.lt(
                    Party.arel_table[:member_limit]
                  )
                )
                .where(
                  # events.id = parties.event_id
                  Event.arel_table[:id].eq(Party.arel_table[:id])
                )
 
Event.where(
  id: subquery
)

Другой способ сделать это — использовать EXIST или боковое соединение.