Why does MongoDB not update unless I call ".then res.json(...)" after findOneAndUpdate? - mongodb

understanding questions here. I am doing a MERN setup website, and I was updating a field in my database like this:
router.post("/updateLogApprovementStatus", (req, res) => {
User.findOneAndUpdate(
{ _id: req.body.userId },
{ $set: { log: req.body.newData } }
).then(user => res.json(user.log));
});
But after doing repeated calls to the api with correct data, my data field wasn't updating. However, when I do this:
router.post("/updateLogApprovementStatus", (req, res) => {
User.findOneAndUpdate(
{ _id: req.body.userId },
{ $set: { log: req.body.newData } }
).then(user => res.json(user.log));
});
My database updates perfectly fine. All I did was adding a res.json(), which occurs after the update since it is in the "then" statement, which makes me wonder why it wored.
I am pretty sure that all I did was adding the then res.json() statement. Any clarifications on why this made it work?

Reason being : "The query executes if callback is passed else a Query object is returned." (bellow the returns section)
.then() isn't really a promise, it's disguised by mongoose but acts as an execution.
You can see it execute queries here
.exec() from the documentation "Executes the query" and returns a Promise (true one)
Mongoose queries are not promises. They have a .then() function for co
and async/await as a convenience. If you need a fully-fledged promise,
use the .exec() function.

If I understand your issue correctly, you should go with something like this:
User.findOneAndUpdate(
{ _id: req.body.userId },
{ $set: { log: req.body.newData } }
).exec();
The exec function will run the query and this way you don't have to handle the promise result.

Related

How do you delete a certain id from a document in mongoose? Edit: Why didn't this work (see edit)

So I have this kind-a-like schema at the moment
user:{ _id: string,
shifts:[_id:string],
name: ... ,
...
}
And now I want to delete a shift._id from all my users who have this.
I allready have an array of all the users their id's who have this shift._id.
I've tried this, with shift_id as the id of the shift i want to delete:
userIdArray.forEach(user_id => {
UserSchema.update({_id: user_id}, {$pull: {shifts: shift_id} });
});
and got the error:
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:535:11)
Can somebody explain me what I did wrong?
Edit:
So what i did was, i called a function named:
function deleteShiftIdInUsers(users, shift_id){
users.forEach(user_id => {
UserSchema.update({_id: user_id}, {$pull: {shifts: shift_id} });
});}
and called this function in my async (req, res, next) route.
Now i just execute this code within the async function instead doing it like code...;
deleteShiftIdInUsers(users, shift_id);
res.status(200).json(...);
still new to js, so what did i do wrong?
I think, You need to call the mongoose function with async/await, you should use updateMany with $in instead of looping for user_id
var ObjectId = require("mongoose").Types.ObjectId
async function deleteShiftIdInUsers(users, shift_id){
users = users.map(user_id => ObjectId(user_id))
return await UserSchema.updateMany({"_id": {"$in": users}}, {"$pull": {"shifts": ObjectId(shift_id)} });
}
when you are calling this function
await deleteShiftIdInUsers(users, shift_id);
res.status(200).json(...);

MeteorJS: Get success callback from user update?

So, I just want to know if my user update was successful on client, so I can notify the user that the update worked.
//Client Side
return Meteor.call('handleUpdateUser', _id, _username, function(err, res) {
if (err) {
// also, what is best practices for handling these errors?
}
console.log(res);
});
//Server Side
Meteor.methods({
handleUpdateUser(id, username) {
if (check for user...) {
return if found
}
return Meteor.users.update({_id: id}, {$set: {username: username}},
function(err, count, res) {
if (err) {
console.log(err) // again, best practices for handling these errors?
}
return res;
});
}
I currently get undefined in console on client.
permissions.js:
//Server Side
Meteor.users.allow({
update:function(userId, doc, fields, modifier) {
return userId && doc._id === userId;
}
});
Thoughts?
you're getting undefined because your server side method is async and, absent treating it as such, your client will see the result of the synchronous part. i.e. the implicit undefined returned at the end of handleUserUpdate().
i use a Future to treat it as async. e.g.
const Future = Npm.require('fibers/future');
Meteor.methods({
handleUpdateUser(id, username) {
let future = new Future();
Meteor.users.update({_id: id}, {$set: {username: username}}, function(err, res) {
if (err) {
console.log(err);
future.throw(new Meteor.Error('500', err));
}
future.return(res);
});
return future.wait();
}
});
now, Meteor will wait to notify the client until the future is handled, either through a return or a throw. your client call should work as you expect.
From the docs: https://docs.meteor.com/api/collections.html#Mongo-Collection-update
On the server, if you don’t provide a callback, then update blocks
until the database acknowledges the write, or throws an exception if
something went wrong. If you do provide a callback, update returns
immediately. Once the update completes, the callback is called with a
single error argument in the case of failure, or a second argument
indicating the number of affected documents if the update was
successful.
So if you want to wait for result, you should think of update as of a sync operation to avoid all those Futures:
Meteor.methods({
handleUpdateUser(id, username) {
return Meteor.users.update({ _id: id }, { $set: { username: username } });
}
});
If operation fails, it will throw an error and pass it on to the client. If it succeeds, then it will return the result.
If you want to check what was the error (on server side), then wrap it into a regular try {} catch(e) {}.

What is the syntax for a mongoose query where I just want one property's value?

I am sending a query to mongoDB using mongoose. The collection is named Step. I want the result of this query to be an array of _id values, one per step. Currently I am getting all of the step objects in their entirety, because req.query isn't defined in this case.
service:
this.getSteps = function() {
return $http({
method: 'GET',
url: '/api/step'
})
.then(function(response) {
return response.data;
});
};
controller:
readStep: function (req, res) {
Step.find(req.query, function(err, result) {
if (err) {
res.status(500).send(err);
}
res.status(200).send(result);
});
}
Set the second parameter of the find query to '_id' to retrieve only the _id of the objects.
Step.find(req.query, '_id', function(err, result) {
This will return data like this:
[{_id: 123}, {_id: 234}]
If you want to get an array of the Step ids on their own, use the javascript map function like so
result = result.map(function(doc) {
return doc._id;
});
which will give you an array like this:
[123, 234]
You'll need to use query.select, something like as shown below:
Step.find(query).select({ "_id": 1}).then(....);
I'm not able to type much because I'm responding from my handheld.
Hope this help!

can findOneAndUpdate trigger schema post update/save?

I am stuck with socket io with problem emit the changes. Just wondering is there a way to trigger scheme.post update or save. at the moment scheme.post save doesn't work with findOneAndUpdate, please help out been stuck for hrs... any help much appreciated.
project.schema.post('update', function (doc) {
onSave(socket, doc);
});
project.schema.post('save', function (doc) {
onSave(socket, doc);
});
where does the udate
project.findOneAndUpdate(
{
_id: req.body._id
},
{ $set:
{ 'watchers' : req.body.watchers }
},
function(err, data) {
return res.json(200, data)
}
);
That was an issue that is covered in some detail here. I'll let you read through for the why but the solution was the maintainers added a special post hook for it:
project.schema.post('findOneAndUpdate', function (doc) {
onSave(socket, doc);
});
NOTE: I am not sure if doc is provided though.

Mongoose Aggregate: Empty results never trigger the callback

I am working with mongoose and defined some aggregations in the mongoshell.
Sometimes the match criteria filters out all data. Nevertheless: Mongoose does not call a callback.
This was very easy reproducable,e.g.
Contract.aggregate( {
$match:{user:'dummydata'}},
function (err, result) {
console.log('this never happens');
});
If I put in a existing user id, the callback gets called.
If I use a non existend user id, the callback just is never called.
But how should I know what happend?
Just by accident I found out, that there is the need for a second "no results" callback.
So the right solution is:
Contract.aggregate( {
$match:{user:'dummydata'}
},
function (err, result) {
console.log('this never happens');
},
function(err,result) {
console.log('Crap there is no result');
});