Voglio rispondere a questa domanda dalla prospettiva dell'associazione autoreferenziale, non solo da has_many: attraverso la prospettiva.
Diciamo che abbiamo un CRM con 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 diventare amico di molti altri contatti, quindi dovremo creare una relazione molti-a-molti.
Se stiamo usando un RDBMS e ActiveRecord, useremmo has_many: through. Quindi avremmo bisogno di creare un modello di adesione, 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 sta facendo amicizia.
Ma stiamo usando MongoDB e Mongoid. Come affermato sopra, Mongoid non ha has_many: through o una funzionalità 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 un array di chiavi "esterne" 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:
Le relazioni molti a molti in cui i documenti inversi sono archiviati in una raccolta separata dal documento di base vengono definite utilizzando la macro has_and_belongs_to_many di Mongoid. Ciò presenta un comportamento simile a Active Record con l'eccezione che non è necessaria alcuna raccolta di join, gli ID della chiave esterna vengono archiviati come array su entrambi i lati della relazione.
Quando si definisce una relazione di questa natura, ogni documento viene memorizzato nella rispettiva raccolta e ogni documento contiene un riferimento "chiave esterna" all'altro sotto forma di array.
# 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 correlati e contatti che hanno molti e appartengono a molte pratiche? Differenza enorme! Uno è una relazione tra due entità. Altro è un autoreferenziale.