2013-04-08 16 views
15

Czy można za jednym zamachem ustawić wiele właściwości na (pod) dokumencie z Mongoose? Przykładem tego, co próbuję zrobić:Częściowa aktualizacja subdokumentu z nodejs/mangustą

Powiedzmy mam ten schemat:

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

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

to chciałbym zrobić:

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

Normalnie, można to osiągnąć za pomocą operator $set, ale moim problemem jest to, że document w tym przykładzie jest dokumentem podrzędnym (osadzonym schematem) z parentDoc. kiedy więc starałem się zrobić

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

zastąpiła dokument podrzędny instancji zidentyfikowany przez subDocs._id. Obecnie "rozwiązałem" go ręcznie, ustawiając tylko pola, ale miałem nadzieję na lepszy sposób.

Odpowiedz

29

Budowanie górę $set obiektu programowo opartej na polach partialUpdate zaktualizować tylko te pola, używając notacji DOT:

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

Tak, ja zrobiłem, że w pierwszej kolejności. Miałem jednak nadzieję, że nie muszę tego robić. – NilsH

+0

Zaakceptowanie tego, ponieważ wydaje się, że jest to jedyny sposób, aby to zrobić. W takim przypadku czy istnieje potrzeba sprawdzenia 'hasOwnProperty', czy nie jest to konieczne, ponieważ jest to zwykły obiekt JSON z treści żądania? – NilsH

+0

Nieco lepszym sposobem na zapętlenie właściwości może być użycie 'Object.keys', ponieważ zawiera ona tylko" własne właściwości "(chociaż prawdopodobnie nie będzie żadnych innych właściwości). – NilsH

6

robiłem różne, w aplikacji REST.

Po pierwsze, mam tę trasę:

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

oraz metodę 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); 
      }); 
     } 
    }); 
} 

Genialny część mangusta potwierdzi data jeśli zdefiniować Schema w tym dokument podrzędny. Ten kod będzie ważny dla dowolnego zasobu dokumentu, który jest tablicą. Nie pokazuję wszystkich moich danych dla uproszczenia, ale jest to dobra praktyka, aby sprawdzić te sytuacje i prawidłowo obsłużyć błąd odpowiedzi.

1

Możesz przypisać lub rozszerzyć osadzony dokument.

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

W tym przypadku należy upewnić się, że _id nie przypisuje.

0

Zajmowałem się tym w nieco inny sposób, bez użycia obiektu $ set. Moje podejście jest podobne do Guilherme'a, ale jedną z różnic jest to, że zapakowałem moją metodę do funkcjonalności statyki, dzięki czemu łatwiej ją wykorzystać w całej mojej aplikacji. Przykład poniżej.

W modelu serwera 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); 
    } 
    }); 
}; 

serwer kontrolera

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