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
Author: Mario Zigliotto, 2011-08-09

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ń.

 146
Author: Steve,
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
 36
Author: Steven Soroka,
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!

 6
Author: franciscodelgadodev,
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.

 0
Author: Donato,
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