Come implementare has_many: attraverso le relazioni con Mongoid e mongodb?

Usando questo esempio modificato dalle guide di Rails , come si modella un’associazione relazionale “has_many: through” usando mongoid?

La sfida è che mongoid non supporta has_many: attraverso come fa ActiveRecord.

# doctor checking out patient class Physician  :appointments has_many :meeting_notes, :through => :appointments end # notes taken during the appointment class MeetingNote  :appointments has_many :physicians, :through => :appointments end # the patient class Patient  :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 

Mongoid non ha has_many: through o una funzione equivalente. Non sarebbe così utile con MongoDB perché non supporta le query di join, quindi anche se si potesse fare riferimento a una raccolta correlata tramite un’altra, sarebbero comunque necessarie più query.

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

Normalmente se si ha una relazione molti in un RDBMS, si modellerebbe in modo diverso in MongoDB usando un campo contenente una serie di chiavi “straniere” su entrambi i lati. Per esempio:

 class Physician include Mongoid::Document has_and_belongs_to_many :patients end class Patient include Mongoid::Document has_and_belongs_to_many :physicians end 

In altre parole si eliminerebbe la tabella di join e avrebbe un effetto simile a has_many: attraverso in termini di accesso all ‘”altra parte”. Ma nel tuo caso questo probabilmente non è appropriato perché la tua tabella di join è una class Appointment che contiene alcune informazioni extra, non solo l’associazione.

Il modo in cui modellate questo dipende in una certa misura dalle query che è necessario eseguire, ma sembra che sia necessario aggiungere il modello di appuntamento e definire le associazioni a Patient and Physician come:

 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 le relazioni in MongoDB devi sempre scegliere tra documenti incorporati o associati. Nel tuo modello direi che MeetingNotes è un buon candidato per una relazione incorporata.

 class Appointment include Mongoid::Document embeds_many :meeting_notes end class MeetingNote include Mongoid::Document embedded_in :appointment end 

Ciò significa che è ansible recuperare le note insieme a un appuntamento tutte insieme, mentre sarebbe necessario più query se si trattasse di un’associazione. Devi solo tenere a mente il limite di 16 MB per un singolo documento che potrebbe entrare in gioco se hai un numero molto elevato di note di riunione.

Solo per approfondire, ecco i modelli estesi con metodi che si comportano in modo molto simile a has_many: attraverso ActiveRecord restituendo un proxy di query invece di un array di record:

 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 

La soluzione di Steven Soroka è davvero fantastica! Non ho la reputazione di commentare una risposta (Ecco perché sto aggiungendo una nuova risposta: P) ma penso che usare la mappa per una relazione sia costosa (specialmente se la tua relazione has_many ha centinaia di record) perché si ottiene i dati dal database, creano ogni record, generano l’array originale e quindi itera sopra l’array originale per crearne uno nuovo con i valori del blocco specificato.

Usando cogliere è più veloce e forse l’opzione più veloce.

 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 

Ecco alcune statistiche con Benchmark.measure:

 > Benchmark.measure { physician.appointments.map(&:patient_id) } => # > Benchmark.measure { physician.appointments.pluck(:patient_id) } => # 

Sto usando solo 250 appuntamenti. Non dimenticare di aggiungere gli indici a: patient_id e: physician_id nel documento Appuntamento!

Spero che aiuti, grazie per aver letto!

Voglio rispondere a questa domanda dalla prospettiva dell’associazione autoreferenziale, non solo l’has_many: attraverso la prospettiva.

Diciamo che abbiamo un CRM con i contatti. I contatti avranno relazioni con altri contatti, ma invece di creare una relazione tra due modelli diversi, creeremo una relazione tra due istanze dello stesso modello. Un contatto può avere molti amici e farsi aiutare da molti altri contatti, quindi dovremo creare una relazione molti-a-molti.

Se utilizziamo un RDBMS e ActiveRecord, utilizzeremo has_many: through. Quindi avremmo bisogno di creare un modello di join, come l’amicizia. Questo modello avrebbe due campi, un contact_id che rappresenta il contatto corrente che sta aggiungendo un amico e un friend_id che rappresenta l’utente che è diventato amico.

Ma stiamo usando MongoDB e Mongoide. Come detto sopra, Mongoid non ha has_many: through o una funzione equivalente. Non sarebbe così utile con MongoDB perché non supporta le query di join. Pertanto, per modellare una relazione molti-molti in un database non RDBMS come MongoDB, si utilizza un campo contenente una serie di chiavi “straniere” su entrambi i lati.

 class Contact include Mongoid::Document has_and_belongs_to_many :practices end class Practice include Mongoid::Document has_and_belongs_to_many :contacts end 

Come afferma la documentazione:

Da molte a molte relazioni in cui i documenti inversi sono archiviati in una raccolta separata dal documento di base sono definiti usando la macro di hash_and_belongs_to_many di Mongoid. Ciò comporta un comportamento simile a Active Record con l’eccezione che non è necessaria alcuna raccolta di join, gli ID di chiave esterna vengono memorizzati come array su entrambi i lati della relazione.

Quando si definisce una relazione di questo tipo, ciascun documento viene archiviato nella rispettiva collezione e ogni documento contiene un riferimento “chiave esterna” all’altro nella forma di una matrice.

 # the contact document { "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), "practice_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ] } # the practice document { "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), "contact_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ] } 

Ora per un’associazione autoreferenziale in MongoDB, hai alcune opzioni.

 has_many :related_contacts, :class_name => 'Contact', :inverse_of => :parent_contact belongs_to :parent_contact, :class_name => 'Contact', :inverse_of => :related_contacts 

Qual è la differenza tra contatti e contatti correlati che hanno molti e appartengono a molte pratiche? Differenza enorme! Uno è una relazione tra due quadro. Altro è un autoreferenziale.