3

MongoDB 用の Mongoid ORM を使用して、新しい Rails 3 プロジェクトを開始したところです。1 つだけ理解できないことがあります。それは、多対多の関係を効果的に構築する方法です。この問題へのアプローチが間違っている可能性は十分にありますが、私の知る限り、私のプロジェクトには多対多の関係を必要とするコンテナが少なくとも 2 つあります。両方のモデルを「ファーストクラス」モデルとして扱い、それぞれに独自のコンテナーを割り当てることをお勧めします。

これは、多対多の関係を構築するために考えることができる最も簡単な方法です。

// Javascript pseudo modeling
// -------------------- Apps
{ 
  app: {
    _id: "app1",
    name: "A",
    event_ids: ["event1","event2"]
  }
}

{ 
  app: {
    _id: "app2",
    name: "B",
    event_ids: ["event1"]
  }
}

// -------------------- Events

{
  event: {
    _id: "event1",
    name: "Event 1",
  }
}

{
  event: {
    _id: "event2",
    name: "Event 2",
  }
}

私が知る限り、これは多対多の関係を推測するために必要な最小限の情報です。どのアプリがイベントに属しているかを判断するには、map reduce 手順が必要になる可能性があると思います。また、イベント モデルにアプリが追加または削除された場合に App.event_ids を更新するために、Event に post commit/save フックを記述する必要があります。

私はここで正しい軌道に乗っていますか?多対多の関係が機能している Mongoid または Mongomapper のコード例を誰かが持っている場合は、共有していただけますか。

4

2 に答える 2

1

構造は機能し、イベントに属するアプリを判別するためにmapreduce関数は必要ありません。イベントIDでアプリコレクションをクエリできます。フィールドcollection.event_idsにインデックスを付けることができます。

イベントIDではなくイベント名でアプリを検索する場合は、そのイベント名をアプリコレクションに追加する必要があります(非正規化)。つまり、イベントの名前が変更された場合は、アプリコレクションも更新する必要があります。それが頻繁に起こるかどうかわかりませんか?

MongoDBを使用するときは、非正規化する必要があることが多いため、最小限の情報は保存しませんが、いくつかのものを「2回」保存します。

于 2010-07-16T06:10:34.410 に答える
1

Mongoid を使用してこの設計を実装することができました。大規模なテストを作成し、ソリューションを機能させることができました。ただし、実装には満足していません。私の実装は維持するのが難しいと思います。

ここにエレガントではないソリューションを投稿しています。うまくいけば、これはより良い実装の開始に役立つでしょう。

class App
  include Mongoid::Document
  field :name

  references_one :account
  references_many :events, :stored_as => :array, :inverse_of => :apps

  validates_presence_of :name
end

class Event
  include Mongoid::Document
  field :name, :type => String

  references_one :account

  validates_presence_of :name, :account

  before_destroy :remove_app_associations

  def apps
    App.where(:event_ids => self.id).to_a
  end

  def apps= app_array
    unless app_array.kind_of?(Array)
      app_array = [app_array]
    end
    # disassociate existing apps that are not included in app_array
    disassociate_apps App.where(:event_ids => self.id).excludes(:id => app_array.map(&:id)).to_a
    # find existing app relationship ids
    existing_relationship_ids = App.where(:event_ids => self.id, :only => [:id]).map(&:id)
    # filter out existing relationship ids before making the new relationship to app
    push_apps app_array.reject { |app| existing_relationship_ids.include?(app.id) }
  end

  def push_app app
    unless app.event_ids.include?(self.id)
      app.event_ids << self.id
      app.save!
    end
  end

  def disassociate_app app
    if app.event_ids.include?(self.id)
      app.event_ids -= [self.id]
      app.save!
    end
  end

  def push_apps app_array
    app_array.each { |app| push_app(app) }
  end

  def disassociate_apps app_array
    app_array.each { |app| disassociate_app(app) }
  end

  def remove_app_associations
    disassociate_apps apps
  end

end
于 2010-07-19T20:37:53.650 に答える