En Rails, ¿cómo creo un alcance que filtre en un atributo has_one relacionado? Tengo dos modelos, Patient y Appointment . El Patient declara una relación has_many en las Appointment . Ahora estoy agregando una relación next_appointment :
class Appointment < ActiveRecord::Base end class Patient < ActiveRecord::Base has_many :appointments, class_name: "::Appointment", foreign_key: :patient_id, inverse_of: :patient has_one( :next_appointment, -> { where("appointment_date >= now()").order(:appointment_date).limit(1) }, class_name: "::Appointment", foreign_key: :patient_id ) scope :by_range_next_appointment_date, lambda { |from, to| where(...) } end Ahora quiero crear un alcance que devuelva todos los pacientes que tienen su próxima cita dentro de un rango determinado. ¿Cómo puedo completar el where() para lograr esto?
He aquí un ejemplo para ilustrar:
Digamos que Bruce Banner tiene una cita el 2/11/2021 y Peter Parker tiene citas el 27/10/2021 y el 3/11/2021. Ahora quiero que este alcance devuelva a todos los pacientes cuya próxima cita (a partir del 26/10/2021) que sea entre el 1/11/2021 y el 7/11/201. Esto solo debería devolver a Bruce Banner ya que su próxima cita está en ese rango. La próxima cita de Peter Parker es mañana, por lo que no debería estar incluido.
Una solución políglota simple sería utilizar una consulta co-relacionada:
Patient.where( "( SELECT MIN(a.appointment_date) FROM appointments a WHERE a.appointment_date >= NOW() AND a.patient_id = patients.id ) BETWEEN ? AND ?", to, from )En Postgres, podría usar una unión lateral como una alternativa de mejor rendimiento:
Patient.joins(%q{ JOIN LATERAL( SELECT appointment_date FROM appointments a WHERE a.patient_id = patients.id -- lateral reference AND a.appointment_date >= NOW() ORDER BY a.appointment_date LIMIT 1 ) aa ON true }).where("aa.next_appointment_date" => to..from)Dado que la appointment_date está en otra tabla, es necesario un .join para acceder a esta columna. La consulta de un rango de valores se puede realizar simplemente pasando un Range a los parámetros hash para where (generará la cláusula SQL usando el operador BETWEEN ).
scope :by_range_next_appointment_date, lambda { |from, to| joins(:appointments).where(appointment_date: from..to) } Esto debería devolver a todos los pacientes con citas en un intervalo de fechas determinado. Si solo desea citas futuras, puede encadenar .where adicionales.
scope :by_range_next_appointment_date, lambda { |from, to| joins(:appointments) .where(appointment_date: from..to) .where("appointment_date >= now()") } Tenga cuidado con los tipos de valores pasados a .where - convert Valores de Date con .to_datetime si el tipo de columna es datetime .