2012-01-09 8 views
6

Come si va sulla profonda clonazione documento MongoDB (mongoid)documento clone profonda con le associazioni incorporati

Ho provato qualcosa di simile;

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

Ma ho problemi di deserializzazione dei valori in seguito.

Come posso creare un clone profondo con tutti gli attributi dei documenti tranne _id?

Modifica: Dopo aver seguito l'esempio di Zachary, ho riscontrato alcuni problemi con una classe di serializzazione personalizzata per i documenti duplicati.

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 

L'oggetto è nullo per i documenti duplicati. Car.find (old_id) .attributes infatti non include il campo con la serializzazione personalizzata, perché è e come posso includerlo?

+0

puoi essere più specifico sui problemi? – Barrie

+0

Quali problemi hai in seguito? –

+0

Immagino che il problema sia che gli ID del documento incorporato non vengono aggiornati. Io sono in conflitto con gli ID dei documenti incorporati nel documento originale. – Yeggeps

risposta

7

Non è necessario chiamare .clone su questo, è possibile utilizzare i dati non elaborati da attributes. Ad esempio il seguente metodo/esempio fornirà nuovi ID in tutto il documento se ne trova uno.

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)) 

E ora avete una copia di Car. Ciò è inefficiente, poiché deve passare attraverso l'intero hash del record per capire se ci sono documenti incorporati in un documento incorporato. Si sarebbe meglio reimpostare la struttura da soli se si sa come sarà, per esempio, se si dispone di un parti incorporati nella macchina, allora si può solo fare:

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} 

che è molto più efficiente di solo facendo un reset generico.

+0

Ciao grazie, ho aggiornato la domanda da quando ho avuto qualche problema con questo approccio. – Yeggeps

+0

Hrm. Non ho molta esperienza con Mongoid :: Fields :: Serializable. Vorrei controllare per vedere quale tipo di dati viene passato quando chiamate 'original.attributes'. Stai ricevendo i dati serializzati o i dati deserializzati? È probabilmente un problema con il passaggio dei dati deserializzati, quindi è necessario serializzarli prima della clonazione. –

+0

Eccellente post .. Dovevo solo rendere conto della sicurezza di mass_assignment in rails3 per fare in modo che funzionasse. Rimuovendo l'attr_accessible dal modello ha funzionato come un incantesimo .. Grazie! – Tigraine

0

Devi usare Car.instantiate se si hanno localizzato i campi in modo che il codice è

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 

Questa soluzione non è molto pulito, ma non ho trovato di meglio.

Problemi correlati