Как реализовать имеет много: через отношения с Mongoid и mongodb?
используя этот измененный пример из рельсы направляющие, как можно моделировать реляционную ассоциацию" has_many :through " с использованием mongoid?
проблема в том, что mongoid не поддерживает has_many :через Как 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
4 ответов:
Mongoid не имеет has_many: through или эквивалентной функции. Это было бы не так полезно с MongoDB, потому что он не поддерживает запросы соединения, поэтому даже если вы можете ссылаться на связанную коллекцию через другую, все равно потребуется несколько запросов.
https://github.com/mongoid/mongoid/issues/544
обычно, если у вас есть много-много отношений в СУБД, вы бы моделировали это по-разному в MongoDB, используя поле, содержащее массив "внешние" ключи с обеих сторон. Например:
class Physician include Mongoid::Document has_and_belongs_to_many :patients end class Patient include Mongoid::Document has_and_belongs_to_many :physicians endдругими словами, вы бы исключили таблицу join, и это имело бы аналогичный эффект для has_many :through с точки зрения доступа к "другой стороне". Но в вашем случае это, вероятно, не подходит, потому что ваша объединенная таблица-это класс назначения, который несет некоторую дополнительную информацию, а не только ассоциацию.
как вы моделируете это зависит в некоторой степени от запросов, которые вам нужно запустить, но кажется, что вы нужно будет добавить модель назначения и определить ассоциации с пациентом и врачом что-то вроде этого:
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С отношениями в MongoDB вы всегда должны сделать выбор между встроенными или связанными документами. В вашей модели я бы предположил, что MeetingNotes-хороший кандидат для встроенных отношений.
class Appointment include Mongoid::Document embeds_many :meeting_notes end class MeetingNote include Mongoid::Document embedded_in :appointment endЭто означает, что вы можете получить указания вместе с назначением все вместе, то вам потребуется несколько запросов, если это была ассоциация. Вы просто должны иметь в виду ограничение размера 16 МБ для одного документа, который может вступить в игру, если у вас есть очень большое количество заметок о встрече.
просто чтобы расширить это, вот модели, расширенные с помощью методов, которые действуют очень похоже на has_many: through from ActiveRecord, возвращая прокси-сервер запроса вместо массива записей:
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
Стивен сорока решение действительно здорово! У меня нет репутации, чтобы прокомментировать ответ(вот почему я добавляю новый ответ :P), но я думаю, что использование map для отношения дорого(особенно если у вашего отношения has_many есть hunders|тысячи записей), потому что он получает данные из базы данных, строит каждую запись, генерирует исходный массив, а затем повторяет исходный массив, чтобы построить новый со значениями из данного блока.
используя выщипывание более быстро и может быть, самый быстрый вариант.
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вот некоторые статистики с бенчмарком.мера:
> 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>Я использую только 250 встреч. Не забудьте добавить индексы в :patient_id и: physician_id в документе назначения!
Я надеюсь, что это помогает, Спасибо за чтение!
Я хочу ответить на этот вопрос с точки зрения самореферентной ассоциации, а не только с точки зрения has_many :through perspective.
допустим, у нас есть 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как указано в документации:
многие ко многим отношениям, где обратные документы хранятся в a отдельная коллекция из базового документа определяется с помощью Mongoid has_and_belongs_to_many макро. Этот показывает подобное поведение к Активная запись, за исключением того, что объединение коллекции не требуется, идентификаторы внешних ключей хранятся в виде массивов по обе стороны отношение.
при определении отношения такого рода, каждый документ хранится в его соответствующая коллекция, и каждый документ содержит "внешний ключ" ссылка на другой в виде массива.
# 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в чем разница между связанными контактами и контактами, имеющими много и принадлежащими ко многим практикам? Огромная разница! Один-это отношения между двумя сущностями. Другой-это самореферентность.
Comments