Глубокий клон документа со встроенными ассоциациями - PullRequest
6 голосов
/ 09 января 2012

Как бы вы занялись глубоким клонированием документа в MongoDB (mongoid)

Я пробовал что-то вроде этого;

original = Car.find(old_id)
@car = original.clone
@car._id = BSON::ObjectId.new

Но впоследствии у меня возникают проблемы с десериализацией значений.

Как сделать глубокий клон со всеми атрибутами документов, кроме _id?

Редактировать: После примера Захари у меня возникли проблемы с пользовательским классом сериализации для дублированных документов.

class OptionHash
  include Mongoid::Fields::Serializable

  # Convert the keys from Strings to Symbols
  def deserialize(object)
    object.symbolize_keys!
  end

  # Convert values into Booleans
  def serialize(object)
    object.each do |key, value|
    object[key] = Boolean::MAPPINGS[value]
  end
end

Объект равен нулю для дублированных документов.Car.find (old_id) .attributes действительно не включает поле с пользовательской сериализацией, почему это так и как я могу включить его?

Ответы [ 2 ]

7 голосов
/ 10 января 2012

Вам не нужно вызывать .clone для этого, вы можете использовать необработанные данные из attributes.Например, приведенный ниже метод / пример даст новые идентификаторы по всему документу, если он их найдет.

def reset_ids(attributes)
    attributes.each do |key, value|
        if key == "_id" and value.is_a?(BSON::ObjectId)
            attributes[key] = BSON::ObjectId.new
        elsif value.is_a?(Hash) or value.is_a?(Array)
            attributes[key] = reset_ids(value)
        end        
    end
    attributes
end


original = Car.find(old_id)
car_copy = Car.new(reset_ids(original.attributes))

И теперь у вас есть копия автомобиля.Однако это неэффективно, поскольку необходимо пройти через весь хеш, чтобы запись выяснила, есть ли какие-либо встроенные документы во встроенном документе.Вам лучше сбросить структуру самостоятельно, если вы знаете, как это будет, например, если у вас есть детали, встроенные в автомобиль, тогда вы можете просто сделать:

original = Car.find(old_id)
car_copy = Car.new(original.attributes)
car_copy._id = BSON::ObjectId.new
car_copy.parts.each {|p| p._id = BSON::ObjectId.new}

Что намного большеэффективнее, чем просто сделать общий сброс.

0 голосов
/ 13 августа 2013

Вы должны использовать Car.instantiate, если у вас есть локализованные поля, поэтому код

def reset_ids(attributes)
    attributes.each do |key, value|
        if key == "_id" && value.is_a?(Moped::BSON::ObjectId)
            attributes[key] = Moped::BSON::ObjectId.new
        elsif value.is_a?(Hash) || value.is_a?(Array)
            attributes[key] = reset_ids(value)
        end        
    end
    attributes
end

car_original = Car.find(id)
car_copy = Car.instantiate(reset_ids(car_original.attributes))
car_copy.insert

Это решение не очень чистое, но я не нашел лучшего.

...