Is it possible to set multiple properties on a (sub)document in one go with Mongoose? An example of what I\'m trying to do:
Let\'s say I have this schema:
I've done different, in a REST application.
First, I have this route:
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);
});
and the updateSet()
method
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);
});
}
});
}
The brilliant part is mongoose will validate the data
if you define the Schema
for this subdocument. This code will be valid for any resource of the document that is an Array. I'm not showing all my data for simplicity, but is a good practice to check for this situations and handle the response error properly.
You can assign or extend embedded document.
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
});
And in this case you should be shure that _id is not assigning.
Build up a $set
object programmatically based on the fields of partialUpdate
to update just those fields using dot notation:
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) {});
I handled this in a slightly different manner without using the $set object. My approach is similar to Guilherme's but one difference is that I wrapped my method into the statics functionality so that it is easier to re-use throughout my application. Example below.
In CollectionSchema.js server model.
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);
}
});
};
In Server Controller
Collection.decrementsubdocScoreById(collectionId, subdocId, function (err, data) {
handleError(err);
doStuffWith(data);
});