Mongoid многие-ко-многим с версиями - PullRequest
3 голосов
/ 15 октября 2011

Предположим, что пользователь хочет создать набор, состоящий из элементов, созданных другими пользователями. Mongoid Документ для элемента имеет управление версиями, и пользователь, который создает набор, может не наслаждаться изменениями, которые авторы элементов делают с элементами набора. Поэтому я хотел бы, чтобы заданный документ ссылался на конкретные версии элементов, позволяя автору набора при необходимости обновлять ссылки на элементы. Я планирую добавить массив номеров версий элементов в заданный документ и некоторые методы для получения заданных элементов определенной версии и для обновления версий элементов. Считаете ли вы этот подход разумным? Как бы вы решили эту проблему?

class Item
  include Mongoid::Document
  include Mongoid::Paranoia
  include Mongoid::Versioning
  field :title, type: String
  has_and_belongs_to_many :item_sets
end

class ItemSet
  include Mongoid::Document
  field :name, type: String
  field :item_versions, type: Array
  has_and_belongs_to_many :items
end

1 Ответ

6 голосов
/ 15 октября 2011

Я решил подобные проблемы, создав модель в «середине», например «ItemReference». MongoDB - это хранилище документов, а не реляционная база данных, поэтому при необходимости можно хранить дублирующую информацию.MongoDB имеет возможность хранить встроенные документы, поэтому мы собираемся использовать эту замечательную функцию.

ItemReference содержит всю важную информацию о Item, необходимую для создания представления.Это уменьшает количество запросов на стороне представления, но увеличивает количество запросов на стороне вставки / обновления.

Дело в том, что вам нужен «составной первичный ключ», который состоит из item_id и номера версии.

Давайте поговорим код:

Модель товара:

class Item
  include Mongoid::Document
  include Mongoid::Paranoia
  include Mongoid::Versioning

  field :title, :type => String

  # create a reference
  def to_reference
    # create new reference, containing all crucial attributes for displaying
    ItemReference.new(  
      :item_id => self._parent.nil? ? self.id : self._parent.id,
      :version => self.version, 
      :title => self.title
    )
  end

  # get a certain version of this item
  def get_version(version_number)
    return self if version_number == self.version
    self.versions.where(:version => version_number).first
  end

end

Модель набора предметов

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

  embeds_many :item_references
end

Модель ItemReference

class ItemReference 
  include Mongoid::Document
  embedded_in :item_sets  

  field :title, :type => String

  # this points to the newest version
  belongs_to :item

  # get the original version
  def original
    self.item.get_version(self.version)
  end

  # update this reference to a certain version
  def update_to!(new_version)
    new_version     = self.item.get_version(new_version)
    if new_version.present?
      # copy attribute, except id
      self.attributes = new_version.to_reference.attributes.reject{|(k,v)| k == "_id"} 
      self.save  
    else
      # version not found
      false
    end
  end

  # update to the newest version
  def update_to_head!
    self.update_to!(self.item.version)
  end
end

Эта комбинация позволяет создавать наборы, содержащие элементы с разными версиями, и вы можете обновить определенные ItemReferences в наборе до определенной версии.

Вот пример:

first         = Item.create(:title => 'Item 1')
first.title   = 'Item 1.1'
first.save

myset         = ItemSet.create(:title => 'My Set')
myset.item_references << first.to_reference
myset.save

first.title   = 'Item 1.2'
first.save

p myset.item_references.first.title # prints Item 1.1
p myset.item_references.first.update_to_head! 
p myset.item_references.first.title # prints Item 1.2
p myset.item_references.first.update_to!(1)
p myset.item_references.first.title # prints Item 1
...