2013-04-08 51 views
15

是否可以使用Mongoose一次性在(子)文檔上設置多個屬性?什麼我想一個例子做:使用nodejs/mongoose部分更新子文檔

比方說,我有這樣的模式:

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

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

然後,我想這樣做:

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

通常情況下,我可以使用做到這一點$set運算符,但我的問題是document在這個示例中是parentDoc的子文檔(嵌入模式)。所以,當我試圖做

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

它換成subDocs._id確定的子文檔實例。目前我通過手動設置字段來「解決」它,但我希望有一個更好的方法來做到這一點。

回答

29

樹立編程基礎上,partialUpdate領域的更新只是那些使用點符號字段$set對象:

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

是的,我沒有這樣做第一。但我希望我不必這樣做。 – NilsH 2013-04-08 14:18:53

+0

接受這一點,因爲它似乎是唯一的方法。在這種情況下,是否需要'hasOwnProperty'檢查,或者這是不需要的,因爲它是來自請求主體的普通JSON對象? – NilsH 2013-04-09 07:36:20

+0

循環屬性的稍微好一點的方法可能是使用Object.keys,因爲它只包含「自己的屬性」(儘管可能不會有任何其他屬性)。 – NilsH 2013-04-09 11:36:49

6

我已經做了不同的,在REST應用程序。

首先,我有這樣的路線:

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

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

燦爛的部分是貓鼬將驗證data如果此子文檔定義Schema。該代碼對於作爲數組的文檔的任何資源都是有效的。爲了簡單起見,我沒有顯示所有數據,但是檢查這種情況並正確處理響應錯誤是一種很好的做法。

1

您可以分配或擴展嵌入式文檔。

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

在這種情況下,你應該是舒爾是_id不分配。

0

我用稍微不同的方式處理了這個,而不使用$ set對象。我的方法類似於Guilherme的方法,但有一點不同,我將我的方法封裝到靜態功能中,以便在整個應用程序中重用。下面的例子。

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

在服務器控制器

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