A settings form is a good example.
On submit, the appropriate collection needs to be updated; and 1, some, none or all of the fields need to be updated.
Assuming I'm not mistaken, Mongoose:
Ignores fields that aren't defined in the schema.
Does some low-level validation on the field based on the schema (i.e. it will reject, (ignore?) a value that isn't of the correct type).
So does that mean that the following is advisable?
.put(function(req, res, next) {
if (mongoose.Types.ObjectId.isValid(req.params._id)) {
Collection.update({_id: req.params._id}, { $set: req.body}, function (err, collection) {
if (err) return next(err);
res.send(200, {success: true});
})
}else{
res.send(400, {success: false});
}
})
i.e. passing req.body directly into the update?
It certainly works, but I can't help but feel it's little lacking in validation/filtering? Is mongoose doing enough in its schema for this approach to be sufficient? Or should I be iterating over each expected field?
I'm happy to set up some tests but thought I'd check with the community and sanity check my approach - any suggested alternatives gratefully received.
As it happens this would be terrible practice as Collection.update bypasses Mongoose validation, so the two assumptions I was making are false in this case.
Related
I am trying to save to two different docs and two different models in the same function, but no matter what I try I seem to get weird errors. It seems like for whatever reason mongoose has made this exclusively not work.
I have two findOne functions nested one is finding the book while the other is finding the prof and the object is to update them both to relate to each other.
Is there a recommended way I should do this perhaps two seperate backend endpoints and two seperate functions? that would be one solution to this problem, but I'd like to know why I cannot do whats below.
await prof.save().then(async () => {
await book
.save()
.then(() => {
return res.status(200).json({
success: true,
message: 'items updated'
}).catch( (err) => {
return res.status(400)
})
})
})
first of all you can not using await and .then .catch together. if you want update two or more collection in mongoose it's better use transactions, it's like rollback in relational database, but if you are newbie in mongoose it's hard to implement transaction,
without transaction you can do like this
try {
await prof.save();
await book.save();
return res.status(200).json({
success: true,
message: "items updated",
});
} catch (error) {
return res.status(400)
}
You probably want to have a look at Mongoose's documentation on middleware, especially the part about pre .save() middleware.
What you want to do is to trigger some function each time an item is saved. Particularly, you want this function to be another save function itself. This needs to be done in your Profs Schema, and you need to require your Books model to make it work:
ProfsSchema.pre('save', async function (next) {
const prof = this;
try {
await Books.findOneAndUpdate({ ... });
next();
} catch (e) {
throw e;
}
})
Of course, this is a very schematic suggestion: the { ... } represents whatever updates you want to apply to your book document. Also, you may want to do different things whether a new prof document is being created with a if (prof.isNew) or only trigger these functions when certain fields have been modified if (prof.field.isModified).
In any case, I strongly suggest you learn to use Mongoose's middleware for this kind of purpose: it's a rather powerful tool to interconnect different models across a MongoDB cluster.
I am using .pull to remove a record from an array in mongo db and it works fine, but a comment I read somewhere on stack overflow (can't find it again to post the link) is bothering me in that it commented that it was bad to use .save instead of using .findByIdAndUpdate or .updateOne
I just wanted to find out if this is accurate or subjective.
This is how I am doing it currently. I check if the product with that id actually exists, and if so I pull that record from the array.
exports.deleteImg = (req, res, next) => {
const productId = req.params.productId;
const imgId = req.params.imgId;
Product.findById(productId)
.then(product => {
if (!product) {
return res.status(500).json({ message: "Product not found" });
} else {
product.images.pull(imgId);
product.save()
.then(response => {
return res.status(200).json( { message: 'Image deleted'} );
})
}
})
.catch(err => {
console.log(err);
});
};
I think what they were saying though was it should rather be done something like this (an example I found after a google)
users.findByIdAndUpdate(userID,
{$pull: {friends: friend}},
{safe: true, upsert: true},
function(err, doc) {
if(err){
console.log(err);
}else{
//do stuff
}
}
);
The main difference is that when you use findById and save, you first get the object from MongoDB and then update whatever you want to and then save. This is ok when you don't need to worry about parallelism or multiple queries to the same object.
findByIdAndUpdate is atomic. When you execute this multiple times, MongoDB will take care of the parallelism for you. Folllowing your example, if two requests are made at the same time on the same object, passing { $pull: { friends: friendId } }, the result will be the expected: only one friend will be pulled from the array.
But let's say you've a counter on the object, like friendsTotal with starting value at 0. And you hit the endpoint that must increase the counter by one twice, for the same object.
If you use findById, then increase and then save, you'd have some problems because you are setting the whole value. So, you first get the object, increase to 1, and update. But the other request did the same. You'll end up with friendsTotal = 1.
With findByIdAndUpdate you could use { $inc: { friendsTotal: 1 } }. So, even if you execute this query twice, on the same time, on the same object, you would end up with friendsTotal = 2, because MongoDB use these update operators to better handle parallelism, data locking and more.
See more about $inc here: https://docs.mongodb.com/manual/reference/operator/update/inc/
I am using .pull to remove a record from an array in mongo db and it works fine, but a comment I read somewhere on stack overflow (can't find it again to post the link) is bothering me in that it commented that it was bad to use .save instead of using .findByIdAndUpdate or .updateOne
I just wanted to find out if this is accurate or subjective.
This is how I am doing it currently. I check if the product with that id actually exists, and if so I pull that record from the array.
exports.deleteImg = (req, res, next) => {
const productId = req.params.productId;
const imgId = req.params.imgId;
Product.findById(productId)
.then(product => {
if (!product) {
return res.status(500).json({ message: "Product not found" });
} else {
product.images.pull(imgId);
product.save()
.then(response => {
return res.status(200).json( { message: 'Image deleted'} );
})
}
})
.catch(err => {
console.log(err);
});
};
I think what they were saying though was it should rather be done something like this (an example I found after a google)
users.findByIdAndUpdate(userID,
{$pull: {friends: friend}},
{safe: true, upsert: true},
function(err, doc) {
if(err){
console.log(err);
}else{
//do stuff
}
}
);
The main difference is that when you use findById and save, you first get the object from MongoDB and then update whatever you want to and then save. This is ok when you don't need to worry about parallelism or multiple queries to the same object.
findByIdAndUpdate is atomic. When you execute this multiple times, MongoDB will take care of the parallelism for you. Folllowing your example, if two requests are made at the same time on the same object, passing { $pull: { friends: friendId } }, the result will be the expected: only one friend will be pulled from the array.
But let's say you've a counter on the object, like friendsTotal with starting value at 0. And you hit the endpoint that must increase the counter by one twice, for the same object.
If you use findById, then increase and then save, you'd have some problems because you are setting the whole value. So, you first get the object, increase to 1, and update. But the other request did the same. You'll end up with friendsTotal = 1.
With findByIdAndUpdate you could use { $inc: { friendsTotal: 1 } }. So, even if you execute this query twice, on the same time, on the same object, you would end up with friendsTotal = 2, because MongoDB use these update operators to better handle parallelism, data locking and more.
See more about $inc here: https://docs.mongodb.com/manual/reference/operator/update/inc/
I'm trying to implement an update method for our API and I'm kinda new to the Node so I didn't know what would be the best practice to carry out the task of updating some fields of a document. Let me elaborate, we have a user model which keeps basic info of a user like name, age, sex, school, bio, birthday etc. Our update method should work as this, the request of the method includes the new values of the fields provided such as {bio:'newBio'} or {school:'newSchool', name:'newName'} I must update the provided fields with the provided data and leave the rest as they are. I was wondering what the best approach to the problem at hand would be. Thanks in advance
The easiest approach what i can think of is to use $Set to perform the update operations.
an example would be :
var updatedUsers= function(db, callback) {
db.collection('users').updateMany(
{ "_id": "value"},
{
$set: { bio: "new bio" }
}
,
function(err, results) {
console.log(results);
callback();
});
};
and invoke your above function as :
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
updatedUsers(db, function() {
db.close();
});
});
When I update a document with Express and Mongoose is the best practice to just delete the _id value and then save the document? Here is an example of the code I'm using.
exports.update = function(req, res, next) {
var data = _.extend(app.locals.crewListing, req.body);
data = data.toObject();
delete data._id;
app.locals.crewListing.save(function(err, result) {
if (err) return next(err);
res.json({
message: null,
data: result
});
});
};
I don't think so. You definitely need to make sure that _id property isn't changed while updating. The best way is,
if(data._id) {
delete data._id;
}
But I would prefer updating each field of the object individually instead of using .extend() function since you know what fields you are updating.