Mongoid 및 mongodb와의 관계를 통해 has_many를 구현하는 방법은 무엇입니까?
Rails 가이드 에서 수정 된이 예제를 사용하여 mongoid를 사용하여 관계형 "has_many : through"연관을 어떻게 모델링합니까?
문제는 mongoid가 ActiveRecord처럼 has_many : through를 지원하지 않는다는 것입니다.
# 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
Mongoid에는 has_many : through 또는 이와 동등한 기능이 없습니다. MongoDB에서는 조인 쿼리를 지원하지 않기 때문에 유용하지 않으므로 다른 컬렉션을 통해 관련 컬렉션을 참조 할 수 있더라도 여전히 여러 쿼리가 필요합니다.
https://github.com/mongoid/mongoid/issues/544
일반적으로 RDBMS에 다 대다 관계가있는 경우 양쪽에 '외래'키 배열을 포함하는 필드를 사용하여 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
즉, 조인 테이블을 제거하고 '다른 쪽'에 대한 액세스 측면에서 has_many : through와 유사한 효과를 갖습니다. 그러나 귀하의 경우에는 조인 테이블이 연결뿐만 아니라 추가 정보를 전달하는 Appointment 클래스이기 때문에 적절하지 않을 수 있습니다.
이를 모델링하는 방법은 실행해야하는 쿼리에 따라 어느 정도 달라 지지만 약속 모델을 추가하고 다음과 같이 환자 및 의사에 대한 연결을 정의해야하는 것처럼 보입니다.
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
This means that you can retrieve the notes together with an appointment all together, whereas you would need multiple queries if this was an association. You just have to bear in mind the 16MB size limit for a single document which might come into play if you have a very large number of meeting notes.
Just to expand on this, here's the models extended with methods that act very similar to the has_many :through from ActiveRecord by returning a query proxy instead of an array of records:
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
Steven Soroka solution is really great! I don't have the reputation to comment an answer(That's why I'm adding a new answer :P) but I think using map for a relationship is expensive(specially if your has_many relationship have hunders|thousands of records) because it gets the data from database, build each record, generates the original array and then iterates over the original array to build a new one with the values from the given block.
Using pluck is faster and maybe the fastest option.
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
Here some stats with Benchmark.measure:
> 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>
I am using just 250 appointments. Don't forget to add indexes to :patient_id and :physician_id in Appointment document!
I hope it helps, Thanks for reading!
I want to answer this question from the self-referencing association perspective, not just the has_many :through perspective.
Let's say we have a CRM with contacts. Contacts will have relationships with other contacts, but instead of creating a relationship between two different models, we’ll be creating a relationship between two instances of the same model. A contact can have many friends and be befriended by many other contacts so we’re going to have to create a many-to-many relationship.
If we are using a RDBMS and ActiveRecord, we would use has_many :through. Thus we would need to create a join model, like Friendship. This model would have two fields, a contact_id that represents the current contact who’s adding a friend and a friend_id that represents the user who’s being befriended.
But we are using MongoDB and Mongoid. As stated above, Mongoid doesn't have has_many :through or an equivalent feature. It would not be so useful with MongoDB because it does not support join queries. Therefore, in order to model a many-many relationship in a non-RDBMS database like MongoDB, you use a field containing an array of 'foreign' keys on either side.
class Contact
include Mongoid::Document
has_and_belongs_to_many :practices
end
class Practice
include Mongoid::Document
has_and_belongs_to_many :contacts
end
As the documentation states:
Many to many relationships where the inverse documents are stored in a separate collection from the base document are defined using Mongoid’s has_and_belongs_to_many macro. This exhibits similar behavior to Active Record with the exception that no join collection is needed, the foreign key ids are stored as arrays on either side of the relation.
When defining a relation of this nature, each document is stored in its respective collection, and each document contains a “foreign key” reference to the other in the form of an array.
# the contact document
{
"_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
"practice_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}
# the practice document
{
"_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
"contact_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}
Now for a self-referencing Association in MongoDB, you have a few options.
has_many :related_contacts, :class_name => 'Contact', :inverse_of => :parent_contact
belongs_to :parent_contact, :class_name => 'Contact', :inverse_of => :related_contacts
What is difference between related contacts and contacts having many and belonging to many practices? Huge difference! One is a relationship between two entities. Other is a self-reference.
'developer tip' 카테고리의 다른 글
Python에서 파일 읽기 및 덮어 쓰기 (0) | 2020.08.28 |
---|---|
Android SQLite : 삽입 / 바꾸기 메서드의 nullColumnHack 매개 변수 (0) | 2020.08.28 |
델리게이트 또는 람다로 스톱워치 타이밍을 래핑 하시겠습니까? (0) | 2020.08.28 |
파일이 존재하는지 확인하는 빠른 테스트 방법은 무엇입니까? (0) | 2020.08.28 |
쿼리 기록을 표시하는 SQL 명령 (0) | 2020.08.28 |