Я хочу ответить на этот вопрос с точки зрения ассоциации, ссылающейся на себя, а не только с точки зрения has_many: через перспективу.
Допустим, у нас есть CRM с контактами. Контакты будут иметь отношения с другими контактами, но вместо создания отношений между двумя разными моделями мы будем создавать отношения между двумя экземплярами одной и той же модели. У контакта может быть много друзей, и многие другие контакты могут с ним дружить, поэтому нам придется создать отношения «многие ко многим».
Если мы используем СУБД и ActiveRecord, мы бы использовали has_many: through. Таким образом, нам потребуется создать модель объединения, например Дружбу. Эта модель будет иметь два поля: contact_id, представляющий текущего контакта, который добавляет друга, и friend_id, представляющий пользователя, с которым дружат.
Но мы используем MongoDB и Mongoid. Как указано выше, в Mongoid нет has_many: through или аналогичной функции. Это было бы не так полезно с MongoDB, потому что он не поддерживает запросы на соединение. Следовательно, чтобы смоделировать отношения «многие-многие» в базе данных, отличной от СУБД, такой как MongoDB, вы используете поле, содержащее массив «внешних» ключей с обеих сторон.
class Contact
include Mongoid::Document
has_and_belongs_to_many :practices
end
class Practice
include Mongoid::Document
has_and_belongs_to_many :contacts
end
Как указано в документации:
Отношения «многие ко многим», в которых обратные документы хранятся в отдельной коллекции от базового документа, определяются с помощью макроса Mongoid has_and_belongs_to_many. Это демонстрирует поведение, аналогичное Active Record, за исключением того, что коллекция соединений не требуется, идентификаторы внешних ключей хранятся в виде массивов по обе стороны от отношения.
При определении отношения такого рода каждый документ сохраняется в своей соответствующей коллекции, и каждый документ содержит ссылку «внешнего ключа» на другой в виде массива.
# the contact document
{
"_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
"practice_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}
# the practice document
{
"_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
"contact_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}
Теперь для ассоциации с саморегулированием в MongoDB у вас есть несколько вариантов.
has_many :related_contacts, :class_name => 'Contact', :inverse_of => :parent_contact
belongs_to :parent_contact, :class_name => 'Contact', :inverse_of => :related_contacts
В чем разница между родственными контактами и контактами, имеющими много и принадлежащими многим практикам? Огромная разница! Один - это отношения между двумя сущностями. Другое - это ссылка на себя.