ruby-on-rails - ¿Cómo implementar has_many: a través de las relaciones con Mongoid y mongodb?




2 Answers

Mongoid no tiene has_many: through o una función equivalente. No sería tan útil con MongoDB porque no es compatible con las consultas de combinación, por lo que incluso si pudiera hacer referencia a una colección relacionada a través de otra, aún requeriría múltiples consultas.

https://github.com/mongoid/mongoid/issues/544

Normalmente, si tiene una relación de muchos y muchos en un RDBMS, debería modelarlo de forma diferente en MongoDB utilizando un campo que contenga un conjunto de claves "extranjeras" en cada lado. Por ejemplo:

class Physician
  include Mongoid::Document
  has_and_belongs_to_many :patients
end

class Patient
  include Mongoid::Document
  has_and_belongs_to_many :physicians
end

En otras palabras, eliminaría la tabla de unión y tendría un efecto similar a has_many: a través del acceso al "otro lado". Pero en su caso eso probablemente no sea apropiado porque su tabla de unión es una clase de Cita que lleva información adicional, no solo la asociación.

La forma de modelar esto depende, hasta cierto punto, de las consultas que necesita ejecutar, pero parece que tendrá que agregar el modelo de Citas y definir asociaciones para el paciente y el médico, algo como esto:

class Physician
  include Mongoid::Document
  has_many :appointments
end

class Appointment
  include Mongoid::Document
  belongs_to :physician
  belongs_to :patient
end

class Patient
  include Mongoid::Document
  has_many :appointments
end

Con las relaciones en MongoDB siempre tiene que elegir entre documentos incrustados o asociados. En su modelo, supongo que MeetingNotes es un buen candidato para una relación incrustada.

class Appointment
  include Mongoid::Document
  embeds_many :meeting_notes
end

class MeetingNote
  include Mongoid::Document
  embedded_in :appointment
end

Esto significa que puede recuperar las notas junto con una cita juntas, mientras que necesitaría múltiples consultas si se tratara de una asociación. Solo debe tener en cuenta el límite de tamaño de 16 MB para un solo documento que podría entrar en juego si tiene una gran cantidad de notas de reunión.

Usando este ejemplo modificado de las guías de Rails , ¿cómo se modela una asociación relacional "has_many: through" usando mongoid?

El desafío es que mongoid no es compatible con has_many: como lo hace ActiveRecord.

# doctor checking out patient
class Physician < ActiveRecord::Base
  has_many :appointments
  has_many :patients, :through => :appointments
  has_many :meeting_notes, :through => :appointments
end

# notes taken during the appointment
class MeetingNote < ActiveRecord::Base
  has_many :appointments
  has_many :patients, :through => :appointments
  has_many :physicians, :through => :appointments
end

# the patient
class Patient < ActiveRecord::Base
  has_many :appointments
  has_many :physicians, :through => :appointments
  has_many :meeting_notes, :through => :appointments
end

# the appointment
class Appointment < ActiveRecord::Base
  belongs_to :physician
  belongs_to :patient
  belongs_to :meeting_note
  # has timestamp attribute
end



¡La solución de Steven Soroka es realmente genial! No tengo la reputación de comentar una respuesta (es por eso que estoy agregando una nueva respuesta: P) pero creo que usar el mapa para una relación es costoso (especialmente si tu relación has_many tiene miles de registros) porque se vuelve los datos de la base de datos, crean cada registro, generan la matriz original y luego iteran sobre la matriz original para construir una nueva con los valores del bloque dado.

El uso de desplumado es más rápido y tal vez la opción más rápida.

class Physician
  include Mongoid::Document
  has_many :appointments

  def patients
    Patient.in(id: appointments.pluck(:patient_id))
  end
end

class Appointment
  include Mongoid::Document
  belongs_to :physician
  belongs_to :patient 
end

class Patient
  include Mongoid::Document
  has_many :appointments 

  def physicians
    Physician.in(id: appointments.pluck(:physician_id))
  end
end

Aquí algunas estadísticas con Benchmark.measure:

> Benchmark.measure { physician.appointments.map(&:patient_id) }
 => #<Benchmark::Tms:0xb671654 @label="", @real=0.114643818, @cstime=0.0, @cutime=0.0, @stime=0.010000000000000009, @utime=0.06999999999999984, @total=0.07999999999999985> 

> Benchmark.measure { physician.appointments.pluck(:patient_id) }
 => #<Benchmark::Tms:0xb6f4054 @label="", @real=0.033517774, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.0, @total=0.0> 

Estoy usando solo 250 citas. ¡No olvide agregar índices a: patient_id y: physician_id en el documento de cita!

Espero que ayude, ¡gracias por leer!




Related

ruby-on-rails activerecord mongodb data-modeling mongoid