Переосмысление отношений «многие ко многим» для MongoDB - PullRequest
3 голосов
/ 16 июля 2010

Я только начинаю новый проект Rails 3, используя Mongoid ORM для MongoDB. Есть только одна вещь, которую я не могу понять, а именно, как эффективно иметь отношения многие ко многим. Теперь есть большая вероятность, что я могу неправильно подойти к этой проблеме, но, насколько я знаю, в моем проекте есть как минимум два контейнера, которые требуют отношения «многие ко многим». Я бы предпочел рассматривать обе модели как модели «первого класса» и выделять для каждой свой контейнер.

Это самый простой способ структурирования отношений «многие ко многим»:

// 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",
  }
}

Насколько я могу судить, это минимальный объем информации, необходимый для вывода отношения «многие ко многим». Я предполагаю, что мне может потребоваться процедура уменьшения карты, чтобы определить, какие приложения относятся к событию. Мне также пришлось бы писать ловушки после фиксации / сохранения события, чтобы обновить App.event_ids, если приложение добавлено или удалено из модели события.

Я на правильном пути? Если у кого-нибудь есть примеры кода Mongoid или Mongomapper, в которых работают отношения «многие ко многим», не могли бы вы поделиться.

Ответы [ 2 ]

1 голос
/ 20 июля 2010

Мне удалось реализовать этот дизайн с помощью 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
1 голос
/ 16 июля 2010

Ваша структура может работать, и вам не нужна функция mapreduce, чтобы определить, какие приложения относятся к событию.Вы можете запросить коллекцию приложений в вечернее время.Вы можете проиндексировать поле collection.event_ids.

Если вы не хотите искать приложения по вечерам, а по имени события, вам нужно добавить это имя события в коллекцию приложений (денормализация).Это означает, что вам также необходимо обновить коллекцию приложений при изменении имени события.Я не знаю, случается ли это очень часто?

Вам часто приходится денормализовать, когда вы используете MongoDB, поэтому вы не храните минимальный объем информации, а храните некоторые вещи «дважды».

...