Jak wdrożyć ma wiele: poprzez relacje z Mongoid i mongodb?
Używając tego zmodyfikowanego przykładu z prowadnic Rails, Jak można modelować relacyjne skojarzenie "has_many :through" używając mongoid?
Wyzwanie polega na tym, że mongoid nie obsługuje has_many: through tak jak 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 answers
Mongoid nie posiada funkcji has_many: through ani równoważnej. Nie byłoby to tak przydatne w przypadku MongoDB, ponieważ nie obsługuje zapytań join, więc nawet gdybyś mógł odwołać się do powiązanej kolekcji za pośrednictwem innej, nadal wymagałoby to wielu zapytań.
Https://github.com/mongoid/mongoid/issues/544
Normalnie, jeśli masz relację wiele-wiele w RDBMS, możesz to inaczej modelować w MongoDB, używając pola zawierającego tablicę 'obcych' kluczy na obu bok. Na przykład:
class Physician
include Mongoid::Document
has_and_belongs_to_many :patients
end
class Patient
include Mongoid::Document
has_and_belongs_to_many :physicians
end
Innymi słowy, wyeliminowałbyś tabelę join i miałoby to podobny efekt do has_many: through pod względem dostępu do'drugiej strony'. Ale w Twoim przypadku to prawdopodobnie nie jest właściwe, ponieważ twój join table jest klasą spotkania, która niesie dodatkowe informacje, nie tylko stowarzyszenie.
Jak modelujesz to zależy w pewnym stopniu od zapytań, które musisz uruchomić, ale wydaje się, że będziesz musiał dodać model spotkania i zdefiniuj skojarzenia z pacjentem i lekarzem coś takiego:
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
W przypadku relacji w MongoDB zawsze musisz dokonać wyboru pomiędzy dokumentami osadzonymi lub powiązanymi. W twoim modelu domyślam się, że spotkania są dobrym kandydatem do osadzonego związku.
class Appointment
include Mongoid::Document
embeds_many :meeting_notes
end
class MeetingNote
include Mongoid::Document
embedded_in :appointment
end
Oznacza to, że możesz pobrać notatki razem z umówionym spotkaniem razem, podczas gdy potrzebowałbyś wielu zapytań, gdyby było to skojarzenie. Trzeba tylko pamiętać o Limit rozmiaru 16 MB dla pojedynczego dokumentu, który może wejść w grę, jeśli masz bardzo dużą liczbę notatek ze spotkań.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 20:31:24
Aby to rozwinąć, oto modele rozszerzone o metody, które działają bardzo podobnie do has_many: through from ActiveRecord zwracając proxy zapytania zamiast tablicy rekordów:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-04 17:32:23
Steven Soroka rozwiązanie jest naprawdę świetne! Nie mam reputacji, aby skomentować odpowiedź(dlatego dodaję nową odpowiedź: P), ale myślę, że używanie map do relacji jest drogie (szczególnie jeśli twoja relacja has_many ma hunders|tysiące rekordów), ponieważ pobiera dane z bazy danych, buduje każdy rekord, generuje oryginalną tablicę, a następnie iteracja nad oryginalną tablicą, aby zbudować nową z wartościami z danego bloku.
Używanie plucka jest szybsze i może najszybsza opcja.
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
Tutaj kilka statystyk z benchmarkiem.miara:
> 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>
Używam tylko 250 wizyt.
Nie zapomnij dodać indeksów do :patient_id oraz: physician_id w dokumencie powołania!
Mam nadzieję, że to pomoże, Dzięki za przeczytanie!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-24 16:27:16
Chcę odpowiedzieć na to pytanie z perspektywy asocjacji, a nie tylko has_many: through perspective.
Powiedzmy, że mamy CRM z kontaktami. Kontakty będą miały relacje z innymi kontaktami, ale zamiast tworzyć relacje między dwoma różnymi modelami, będziemy tworzyć relacje między dwoma instancjami tego samego modelu. Kontakt może mieć wielu przyjaciół i być zaprzyjaźniony przez wiele innych kontaktów, więc będziemy musieli stworzyć związek wielu do wielu.Jeśli używamy RDBMS i ActiveRecord, użyjemy has_many: through. W ten sposób musielibyśmy stworzyć wspólny model, jak przyjaźń. Model ten będzie miał dwa pola, contact_id, który reprezentuje bieżący kontakt, który dodaje znajomego i friend_id, który reprezentuje użytkownika, który jest zaprzyjaźniony.
Ale używamy MongoDB i Mongoid. Jak wspomniano powyżej, Mongoid nie posiada funkcji has_many: through ani równoważnej funkcji. To nie byłoby tak przydatne z MongoDB, ponieważ nie obsługuje zapytań join. Dlatego, aby modelować relację wiele-wiele w bazie danych innej niż RDBMS, takiej jak MongoDB, używasz pola zawierającego tablicę 'obcych' kluczy po obu stronach.
class Contact
include Mongoid::Document
has_and_belongs_to_many :practices
end
class Practice
include Mongoid::Document
has_and_belongs_to_many :contacts
end
Jak stwierdza dokumentacja:
Wiele do wielu relacji, w których odwrotne dokumenty są przechowywane w zbiór oddzielny od dokumentu bazowego definiowany jest za pomocą has_and_belongs_to_many makro. Wykazuje podobne zachowanie do Active Record z tym wyjątkiem, że nie jest wymagana kolekcja join, identyfikatory kluczy obcych są przechowywane w postaci tablic po obu stronach pokrewieństwo.
Przy definiowaniu relacji tego rodzaju, każdy dokument jest przechowywany w swój zbiór, a każdy dokument zawiera "klucz obcy" odniesienie do drugiego w postaci tablicy.
# the contact document
{
"_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
"practice_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}
# the practice document
{
"_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
"contact_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}
Teraz, jeśli chodzi o Samorelację w MongoDB, masz kilka opcji.
has_many :related_contacts, :class_name => 'Contact', :inverse_of => :parent_contact
belongs_to :parent_contact, :class_name => 'Contact', :inverse_of => :related_contacts
Jaka jest różnica między kontakty i kontakty mające wiele i należące do wielu praktyk? Ogromna różnica! Jeden to relacja między dwoma bytami. Inne to samo odniesienie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-29 21:34:51