2013-05-06 16 views
9

Utilizzando Mongoose versione 3.6.4Mongoose.js: aggiornamento atomico delle proprietà annidate?

dire che ho un documento MongoDB in questo modo:

{ 
    "_id" : "5187b74e66ee9af96c39d3d6", 
    "profile" : { 
     "name" : { 
      "first" : "Joe", 
      "last" : "Pesci", 
      "middle" : "Frank" 
     } 
    } 
} 

e ho il seguente schema per gli utenti:

var UserSchema = new mongoose.Schema({ 
    _id: { type: String }, 
    email: { type: String, required: true, index: { unique: true }}, 
    active: { type: Boolean, required: true, 'default': false }, 
    profile: { 
    name: { 
     first: { type: String, required: true }, 
     last:  { type: String, required: true }, 
     middle: { type: String } 
    } 
    } 
    created: { type: Date, required: true, 'default': Date.now}, 
    updated: { type: Date, required: true, 'default': Date.now} 
); 

E io presentare un modulo di passaggio un campo denominato: profile[name][first] con un valore di Joseph

e quindi voglio aggiornare solo l'utente fi primo nome, ma lascia il suo ultimo e mezzo da solo, ho pensato di fare solo:

User.update({email: "[email protected]"}, req.body, function(err, result){}); 

Ma quando faccio questo, "cancella" le proprietà profile.name.last e profile.name.middle e io alla fine con un documento che sembra :

{ 
    "_id" : "5187b74e66ee9af96c39d3d6", 
    "profile" : { 
     "name" : { 
      "first" : "Joseph" 
     } 
    } 
} 

quindi è praticamente sovrascrivere tutti profile con req.body.profile, che credo abbia senso. C'è un modo per aggirarlo senza dover essere più esplicito specificando i miei campi nella query di aggiornamento invece di req.body?

+0

Considera di accettare la risposta di Aichholzer in quanto fornisce una vera soluzione al problema. – zbr

risposta

7

Lei ha ragione, Mongoose converte aggiornamenti $set per voi. Ma questo non risolve il tuo problema. Provalo nella shell di mongodb e vedrai lo stesso comportamento.

Invece, per aggiornare una singola proprietà profondamente nidificata è necessario specificare il percorso completo della proprietà deep nello $set.

User.update({ email: '[email protected]' }, { 'profile.name.first': 'Joseph' }, callback) 
+3

Giusto, ma speravo di poter passare in 'req.body', quindi non dovevo specificare tutti i campi possibili. Così ho finito per fare un 'findById()' prima, cancellando '__v' e' _id' dal documento trovato, usando '_.deepExtend()' per unirmi nelle nuove proprietà aggiornate e poi passando il mio oggetto frankenstein a quello di Mongoose 'update'. – k00k

+1

@aaronheckmann, sta comunque raccontando a mongoose o mongo che "ecco il mio ultimo oggetto, semplicemente aggiornalo"? –

+0

@ k00k - L'approccio è atomico? Non scriverà ancora l'intero documento di nuovo? Perché devi rimuovere __v e _id? –

0

Penso che si sta cercando $ set

http://docs.mongodb.org/manual/reference/operator/set/

User.update({email: "[email protected]"}, { $set : req.body}, function(err, result){}); 

Prova che

+0

Ho pensato che Mongoose fosse "sicuro" di default e quindi un 'update' è davvero un' $ set', ho sbagliato? – k00k

+0

E per aggiungere, l'ho appena provato e non funziona. Sembra essere un problema con le proprietà nidificate. Giusto per chiarire, sono solo le proprietà annidate allo stesso livello (fratelli) che vengono spazzate via. – k00k

+0

stai cercando $ set con un nome di campo specifico - "nome.first". Questo non ha nulla a che fare con le scritture sicure o non sicure, solo se si sta dicendo di aggiornare l'intero documento o un singolo campo. –

0

Forse è una buona soluzione - aggiunge opzione per Model.update, che sostituiscono gli oggetti nidificati come:

{field1: 1, fields2: {a: 1, b: 2}} => { 'field1': 1, 'field2.a': 1, 'field2.b': 2}

nestedToDotNotation: function(obj, keyPrefix) { 
    var result; 
    if (keyPrefix == null) { 
     keyPrefix = ''; 
    } 
    result = {}; 
    _.each(obj, function(value, key) { 
     var nestedObj, result_key; 
     result_key = keyPrefix + key; 
     if (!_.isArray(value) && _.isObject(value)) { 
     result_key += '.'; 
     nestedObj = module.exports.nestedToDotNotation(value, result_key); 
     return _.extend(result, nestedObj); 
     } else { 
     return result[result_key] = value; 
     } 
    }); 
    return result; 
    } 

});

bisogno miglioramenti movimentazione riferimento circolare, ma questo è molto utile quando si lavora con oggetti nidificati

sto usando sottolineatura.js qui, ma queste funzioni facilmente possono essere sostituiti con altri analoghi

8

Uno molto facile modo per risolvere questo con Moongose 4.1 e il pacchetto flat:

var flat = require('flat'), 
    Schema = mongoose.Schema, 
     schema = new Schema(
      { 
       name: { 
        first: { 
         type: String, 
         trim: true 
        }, 
        last: { 
         type: String, 
         trim: true 
        } 
       } 
      } 
     ); 

    schema.pre('findOneAndUpdate', function() { 
     this._update = flat(this._update); 
    }); 


    mongoose.model('User', schema); 

req.body (per esempio) può ora essere:

{ 
    name: { 
     first: 'updatedFirstName' 
    } 
} 

L'oggetto verrà appiattito prima dell'esecuzione della query effettiva, pertanto $set aggiornerà solo le proprietà previste anziché l'intero oggetto name.

+0

fai attenzione quando hai nidificato ObjectId. flat trasformerà quelli in 'something._bsontype': 'ObjectID', 'something.id': ' – tdecs