2013-04-08 32 views
15

È possibile impostare più proprietà su un (sotto) documento in una volta sola con Mangusta? Un esempio di quello che sto cercando di fare:Aggiornamento parziale di un documento secondario con nodejs/mangusta

Diciamo che ho questo schema:

var subSchema = new Schema({ 
    someField: String, 
    someOtherField: String 
}); 

var parentSchema = new Schema({ 
    fieldOne: String, 
    subDocs: [subSchema] 
}) 

Poi vorrei fare:

exports.updateMyDocument = function(req, res) { 
    var parentDoc = req.parentDoc; // The parent document. Set by parameter resolver. 
    var document = req.myDoc; // Sub document of parent. Set by parameter resolver. 
    var partialUpdate = req.body; // updated fields sent as json and parsed by body parser 
    // I know that the statement below doesn't work, it's just an example of what I would like to do. 
    // Updating only the fields supplied in "partialUpdate" on the document 
    document.update(partialUpdate); 
    parentDoc.save(function(err) { 
     if(err) { 
      res.send(500); 
      return; 
     } 
     res.send(204); 
    }); 
}; 

Normalmente, ho potuto ottenere questo utilizzando l'operatore $set, ma il mio problema è che document in questo esempio è un documento secondario (schema incorporato) di parentDoc. Così, quando ho provato a fare

Parent.update({_id: parentDoc._id, "subDocs._id": document._id}, 
    {$set: {"subDocs.$" : partialUpdate}}, 
    function(err, numAffected) {}); 

ha sostituito l'istanza documento secondario identificato da subDocs._id. Attualmente ho "risolto" impostando solo i campi manualmente, ma speravo in un modo migliore per farlo.

risposta

29

Costruire un oggetto $set di programmazione in base ai campi di partialUpdate per aggiornare solo quei campi usando la notazione punto:

var set = {}; 
for (var field in partialUpdate) { 
    set['subDocs.$.' + field] = partialUpdate[field]; 
} 
Parent.update({_id: parentDoc._id, "subDocs._id": document._id}, 
    {$set: set}, 
    function(err, numAffected) {}); 
+0

Sì, l'ho fatto prima. Ma speravo di non doverlo fare. – NilsH

+0

Accettando questo, poiché sembra essere l'unico modo per farlo. In questo caso, c'è bisogno di un controllo 'hasOwnProperty', o non è necessario poiché è un oggetto JSON normale dal corpo della richiesta? – NilsH

+0

Un modo leggermente migliore per eseguire il loop delle proprietà potrebbe essere l'uso di 'Object.keys', poiché includerà solo" proprietà proprie "(anche se probabilmente non ci saranno altre proprietà). – NilsH

6

ho fatto diverso, in un'applicazione REST.

In primo luogo, ho questo percorso:

router.put('/:id/:resource/:resourceId', function(req, res, next) { 
    // this method is only for Array of resources. 
    updateSet(req.params.id, req.params.resource, req, res, next); 
}); 

e il metodo updateSet()

function updateSet(id, resource, req, res, next) { 
    var data = req.body; 
    var resourceId = req.params.resourceId; 

    Collection.findById(id, function(err, collection) { 
     if (err) { 
      rest.response(req, res, err); 
     } else { 
      var subdoc = collection[resource].id(resourceId); 

      // set the data for each key 
      _.each(data, function(d, k) { 
       subdoc[k] = d; 
      }); 

      collection.save(function (err, docs) { 
       rest.response(req, res, err, docs); 
      }); 
     } 
    }); 
} 

La parte geniale è mangusta convaliderà la data se si definisce l'Schema per questo documento secondario. Questo codice sarà valido per qualsiasi risorsa del documento che è una matrice. Non sto mostrando tutti i miei dati per semplicità, ma è una buona pratica controllare queste situazioni e gestire correttamente l'errore di risposta.

1

È possibile assegnare o estendere il documento incorporato.

Doc.findOne({ _id: docId }) 
    .then(function (doc) { 
     if (null === doc) { 
     throw new Error('Document not found'); 
     } 

     return doc.embeded.id(ObjectId(embeddedId)); 
    }) 
    .then(function(embeddedDoc) { 
     if (null === embeddedDoc) { 
     throw new Error('Embedded document not found'); 
     } 

     Object.assign(embeddedDoc, updateData)); 
     return embeddedDoc.parent().save(); 
    }) 
    .catch(function (err) { 
     //Do something 
    }); 

E in questo caso si dovrebbe essere shure che _id non sta assegnando.

0

L'ho gestito in modo leggermente diverso senza utilizzare l'oggetto $ set. Il mio approccio è simile a quello di Guilherme, ma una differenza è che ho avvolto il mio metodo nella funzionalità statica in modo che sia più facile riutilizzarlo nell'intera applicazione. Esempio di seguito.

Nel modello server CollectionSchema.js.

collectionSchema.statics.decrementsubdocScoreById = function decreasesubdoc (collectionId, subdocId, callback) { 
    this.findById(collectionId, function(err, collection) { 
    if (err) console.log("error finding collection"); 
    else { 
     var subdoc = collection.subdocs.filter(function (subdoc) { 
     return subdoc._id.equals(subdocId); 
     })[0]; 

     subdoc.score -= 1; 

     collection.save(callback); 
    } 
    }); 
}; 

controller Server

Collection.decrementsubdocScoreById(collectionId, subdocId, function (err, data) { 
    handleError(err); 
    doStuffWith(data); 
}); 
Problemi correlati