Active Record-查询以从父表中排除条目(如果其任何子项匹配)

时间:2018-09-07 17:51:10

标签: sql activerecord has-many-through

让我们说我正在看一起预约的医师和患者,并且我想过滤掉与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 :(得分:1)

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)

如您所见,对于某些本应简单的事情,它开始变得异常复杂。怪异来自模型结构,没有反映AppointmentUserUser患者之间的业务关联。