Active Record - запрос на исключение записей из родительской таблицы, если какой-либо из ее дочерних элементов совпадает - PullRequest
0 голосов
/ 07 сентября 2018

Допустим, я смотрю, как врачи и пациенты записываются на приемы вместе, и я хочу отфильтровать встречи, в которых участвовал врач с user.id = 1. Мои врачи и пациенты хранятся в одном классе Users (есть user_roles и таблица ролей, чтобы различать их, но это не важно для этого примера) и привязана к каждому назначению через таблицу UserAppointments, поэтому она выглядит примерно так:

class User < ActiveRecord::Base
    has_many :user_appointments

class UserAppointment < ApplicationRecord
    belongs_to :user
    belongs_to :appointment

class Appointment < ApplicationRecord
    has_many :user_appointments
    has_many :users, through: :user_appointments

Сначала я попробовал что-то похожее на Appointment.joins(:users).where.not("users.id = 1"), но к любому Назначению, включая пользователя 1, все равно был подключен действительный пациент, так что это включало бы внутреннее присоединение к записи UserAppointment пациента и включало бы запись этого назначения. В этом запросе дважды отображаются действительные встречи, поскольку для двух пользователей есть две записи UserAppointment.

Таким образом, я мог бы установить фильтр для любого Appointment.id, который появляется дважды (указывая, что участвующий врач не был пользователем 1), или создать список назначений с участием пользователя 1, и отфильтровать их напрямую. Но мне просто интересно, есть ли команда, похожая на .where.not, которая могла бы исключить встречу, если она соответствовала какому-либо условию, даже если у нее были другие допустимые дочерние элементы, которые не соответствовали условию.

1 Ответ

0 голосов
/ 08 сентября 2018

Appointment.joins(users: :roles).where(roles: {code: :physician}).where.not(users: {id: 1})

Таким образом, вы рассчитываете каждый прием на одного врача. (Замените code эквивалентным столбцом в вашей таблице roles)

Тем не менее, вы, кажется, говорите, что встреча всегда происходит между пациентом и пользователем. Если это так, то лучше подойдет следующая архитектура:

class User < ActiveRecord::Base
has_many :user_appointments

class Appointment < ApplicationRecord
belongs_to :patient, class_name: 'User' # needs a patient_id foreign key
belongs_to :physician, class_name: 'User' # needs a physician_id foreign key

Тогда вы можете запросить Appointment.where.not(physician_id: 1)

Редактировать комментарий:

Если вы хотите получить, скажем, все назначения для пациента № 10, исключая при этом встречи с врачами № 1 и № 2, вот как вы могли бы это осуществить:

class Appointment < ApplicationRecord
  scope :joins_physician, -> { joins(users: :roles).where(roles: {code: :physician}) }
  scope :joins_patient, -> { joins(users: :roles).where(roles: {code: :patient}) }
  scope :excluding_physicians, -> (*physician_ids) { where.not(id: unscoped.joins_physician.where(users: {id: physician_ids})) }
  scope :for_patients, -> (*patient_ids) { where(id: unscoped.joins_patient.where(users: {id: patient_ids})) }
end

Appointment.for_patients(1).excluding_physicians(1,2)

Как вы видите, это начинает становиться странно сложным для чего-то, что должно быть простым. Странность проистекает из структуры модели, не отражающей бизнес-ассоциации между Appointment, User и User пациентом.

...