Eu quero responder a essa questão da perspectiva da associação autorreferencial, não apenas da perspectiva has_many: por meio da perspectiva.
Digamos que temos um CRM com contatos. Os contatos terão relacionamentos com outros contatos, mas em vez de criar um relacionamento entre dois modelos diferentes, estaremos criando um relacionamento entre duas instâncias do mesmo modelo. Um contato pode ter muitos amigos e ser amigo de muitos outros contatos, então teremos que criar um relacionamento muitos para muitos.
Se estivermos usando um RDBMS e ActiveRecord, usaríamos has_many: through. Portanto, precisaríamos criar um modelo de associação, como Amizade. Este modelo teria dois campos, um contact_id que representa o contato atual que está adicionando um amigo e um friend_id que representa o usuário do qual está fazendo amizade.
Mas estamos usando MongoDB e Mongoid. Conforme declarado acima, o Mongoid não tem has_many: through ou um recurso equivalente. Não seria tão útil com o MongoDB porque não oferece suporte a consultas de junção. Portanto, para modelar um relacionamento muitos-muitos em um banco de dados não-RDBMS como o MongoDB, você usa um campo que contém uma matriz de chaves 'estrangeiras' em cada lado.
class Contact
include Mongoid::Document
has_and_belongs_to_many :practices
end
class Practice
include Mongoid::Document
has_and_belongs_to_many :contacts
end
Conforme afirma a documentação:
Muitos para muitos relacionamentos em que os documentos inversos são armazenados em uma coleção separada do documento base são definidos usando a macro has_and_belongs_to_many do Mongoid. Isso exibe um comportamento semelhante ao Active Record, com a exceção de que nenhuma coleção de junção é necessária, os IDs de chave estrangeira são armazenados como matrizes em ambos os lados da relação.
Ao definir uma relação desta natureza, cada documento é armazenado em sua respectiva coleção, e cada documento contém uma referência de “chave estrangeira” para o outro em forma de array.
# the contact document
{
"_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
"practice_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}
# the practice document
{
"_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
"contact_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}
Agora, para uma associação de autorreferência no MongoDB, você tem algumas opções.
has_many :related_contacts, :class_name => 'Contact', :inverse_of => :parent_contact
belongs_to :parent_contact, :class_name => 'Contact', :inverse_of => :related_contacts
Qual é a diferença entre contatos relacionados e contatos que têm muitos e pertencem a muitas clínicas? Enorme diferença! Um é um relacionamento entre duas entidades. Outro é uma auto-referência.