1. ホーム
  2. ruby-on-rails

[解決済み] Mongoidとmongodbでhas_many :through リレーションシップを実装するには?

2023-02-01 21:46:48

質問

この修正された例を使って Railsのガイド からの修正された例を使って、どのようにmongoidを使ってリレーショナルな "has_many :through" 関連付けをモデル化するのでしょうか?

課題は、ActiveRecordのようにmongoidが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モデルを追加し、PatientとPhysicianに以下のような関連付けを定義する必要があるように思われます。

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

これは、これが関連付けであった場合、複数のクエリーを必要とするのに対し、メモをアポイントメントと一緒にまとめて取得することができることを意味します。ただ、非常に多くのミーティングノートがある場合、1つのドキュメントに対する16MBのサイズ制限を念頭に置く必要があります。