I want to append a value into my Mongoose array but my array never seems to update. I do the following:
In my controller, I append an eventName into the array eventsAttending like so:
$scope.currentUser.eventsAttending.push(event.eventName);
$http.put('/api/users/' + $scope.currentUser._id, $scope.currentUser)
.success(function(data){
console.log("Success. User " + $scope.currentUser.name);
});
I try to update the array like so:
// Updates an existing event in the DB.
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
User.findById(req.params.id, function (err, user) {
if (err) { return handleError(res, err); }
if(!user) { return res.send(404); }
user.markModified('req.body.eventsAttending');
user.save(function (err) {
if (err) { return handleError(res, err);}
return res.json(200, user);
});
});
};
But my array never seems to update. I've also tried the following:
// Updates an existing event in the DB.
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
User.findById(req.params.id, function (err, user) {
if (err) { return handleError(res, err); }
if(!user) { return res.send(404); }
var updated = _.merge(user, req.body);
updated.markModified('eventsAttending');
updated.save(function (err) {
if (err) { return handleError(res, err);}
return res.json(200, user);
});
});
};
With this approach, my array updates properly, but when I try to perform the http put after one time, I get an error saying Error: { [VersionError: No matching document found.] message: 'No matching document found.', name: 'VersionError' }
Here is my UserSchema:
var UserSchema = new Schema({
name: String,
username: String,
eventsAttending: [{ type: String, ref: 'Event'}],
});
If anyone could help that would be much appreciated.
My guess is the object returning from _.merge is no longer a Mongoose model and some information is getting lost in the transform. I would try manually setting all of the fields coming from the request and use events.attending.push() to add to the array, then saving the updated object and see what happens.
Your first example with markModified looks wrong. Looking at the documentation it should be the name of the field that is modified and it appears that you've put the source location for it.
user.markModified('user.eventsAttending')
However that should not be necessary if you use the push method as Mongoose overrides the built-in array function to track changes.
Related
I'm having a problem updating an object field with a key and a value on the user instance. The code runs successfully by saving but does not update the key on the object. Here is the code that I'm running
let query = new Parse.Query(Parse.User);
app.post("/saveListName", function (req, res) {
query.get(req.body.userObjectId, {
success: function (user) {
console.log(user);
user.set("list.foo", "Hello world"); //this is what I'm updating
user.save(null, {
useMasterKey: true
}).then(function (res) {
console.log("User saved");
console.log(res);
}).catch(function (err) {
console.log(err);
})
res.send(true);
},
error: function (err) {
console.log(err);
res.send(false);
}
})
})
foo is the key I'm trying to update on the list object. And I want to dynamically add new keys to the list object but not only one key. Please, I need help with this. I'm wondering if this feature(dynamically appending/removing fields on an object) is even supported in parse because going through documentation http://docs.parseplatform.org/js/guide/#updating-objects and I have not seen anything like that. Thanks
CODE:
server-side
/**
* List of Articles
*/
exports.list = function (req, res) {
Article.find({ 'user.displayName': 'GIGANTOR !' }).sort('-created').populate('user', 'displayName').exec(function (err, articles) {
if (err) {
return res.status(422).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(articles);
}
});
};
SITUATION:
What I tried above does not work. I checked the mongoose docs: http://mongoosejs.com/docs/queries.html
but can't seem to get the query to work. Currently, the query just returns nothing.
QUESTION:
How to query all articles by a user with a specific displayName ?
TL;DR You can't query a document by a field that belongs to a populated object.
Since article simply has a ref to User, you'll have just get all articles, and then filter them in memory. Or, since the article.user field is an _id, you can find articles by the user ID (but your question is asking about finding them by user.displayName).
Mongoose populate does not do the populating in the MongoDB server itself; it populates on the application server. This means that multiple round-trips to the database are happening (see article Understanding Mongoose Population.) Therefore, you can't query by a field that exists as part of a populated object.
So, here's your 2 solutions:
Article.find({}).sort('-created').populate('user', 'displayName').exec(function (err, articles) {
if (err) {
return res.status(422).send({
message: errorHandler.getErrorMessage(err)
});
} else {
let filteredArticles = articles
.filter(article => article.user.displayName === 'GIGANTOR !');
res.json(filteredArticles);
}
});
Or, if you can query by _id, you can do this:
Article.find({ user: 'somemongoobjectidofuser' }).sort('-created').populate('user', 'displayName').exec(function (err, articles) {
if (err) {
return res.status(422).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(articles);
}
});
It gets to be a bit hairy and out of scope of the question, but another solution is the aggregation pipeline, which is only usually recommended for backend analytics. But, it'll provide you more flexibility in your query (especially if you user MongoDB's new $graphLookup).
Or, you can always store a copy of the user as a denormalized object inside the article document itself, but then you run into the much-discussed issue of maintaining denormalized documents in-sync.
Just putting the code I ended up using here for people who could need it:
/**
* List of Articles
*/
exports.list = function (req, res) {
Article.find({ user: req.user._id.toString() }).sort('-created').populate('user', 'displayName').exec(function (err, articles) {
if (err) {
return res.status(422).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(articles);
}
});
};
postRegistrationHandler: function (account, req, res, next) {
console.log('postRegistrationHandler activated');
account.getCustomData(function(err, data) {
if (err) {
console.log(err.toString, "error string");
return next(err);
} else {
data.mongo_id = userCreationCtrl(account);
data.save();
next();
}
});
},
This function almost works properly, but the line:
data.save();
runs before the previous line finishes which means that the data I want to save isn't present at the appropriate time.
data.mongo_id = userCreationCtrl(account);
This line calls a function that creates a mongoDB document with information in the account object and then returns the _id (which is what I am trying to save.
I thought maybe using a .then() would help but that seems to be unavailable here for some reason. If anyone sees something I'm missing, that would be quite helpful. Thank you!
Here is the userCreationCtrl file as requested:
var UserSchema = require('./../models/UserModel.js');
var createNewUser = function (account, res, next){
// We will return mongoId after it is created by submitting a newUser
var mongoId = "";
// Save StormpathID (last 22 characters of account.href property)
var newStormpathId = account.href.slice(account.href.length - 22);
console.log('stormpath ID:', newStormpathId, 'just registered!');
console.log(account);
// Create new user from model by recycling info from the Stormpath registration form and include the stormpathId as well.
var newUser = new UserSchema({
stormpathId: newStormpathId,
firstName: account.givenName,
lastName: account.surname,
email: account.email,
street: account.street,
city: account.city,
zip: account.zip
});
// This saves the user we just created in MongoDB
newUser.save(function(err, result){
console.log(result);
if (err) {
console.error(err);
}
else {
console.log("User created in MongoDB, attempting to return mongoDB _id to stormpath customData");
// Keep track of the new user's mongo _id so we can return it to the previous function and save it as Stormpath custom data.
mongoId = result._id;
console.log(mongoId, "mongoid");
return result._id;
}
});
};
module.exports = createNewUser;
You have userCreationCtrl expecting 3 arguments, account, res, and next. next is the callback that should be called after the user is created so instead of return result._id you should call next like so:
// inside of createNewUser()
newUser.save(function(err, result){
console.log(result);
if (err) {
console.error(err);
}
else {
console.log("User created in MongoDB, attempting to return mongoDB _id to stormpath customData");
// Keep track of the new user's mongo _id so we can return it to the previous function and save it as Stormpath custom data.
mongoId = result._id;
console.log(mongoId, "mongoid");
// IMPORTANT change to make it all work...
// get rid of return result._id because its not doing anything
// pass the value to your callback function instead of returning the value
next(null, result._id);
}
});
then calling code in postRegistrationHandler should look like this:
account.getCustomData(function(err, data) {
if (err) {
console.log(err.toString, "error string");
return next(err);
} else {
// pass in a callback as the 3rd parameter that will be called by newUser.save() when its finished
userCreationCtrl(account, null, function(err, resultId) {
data.save();
next();
});
}
});
Background:
I must create or update an document based on post request that I have zero control over. I'm calling the function updateOrCreate()
Question:
How can I properly find a document by an field called nuid without using _id in mongo/mongoose
example payload:
curl -H "Content-Type: application/json" -X POST -d '{"participant":{"nuid":"98ASDF988SDF89SDF89989SDF9898"}}' http://localhost:9000/api/things
thing.controller:
exports.updateOrCreate = function(req, res) {
//Thing.findByNuid() will not work but it will explain what i'm trying to accomplish
/**
Thing.findByNuid(req.body.participant.nuid, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) {
Thing.create(req.body.participant, function(err, thing) {
if(err) { return handleError(res, err); }
});
}
var updated = _.merge(thing, req.body.participant);
updated.save(function (err) {
if (err) { return handleError(res, err); }
});
});
**/
//this block will fetch all the things that have nuids but that seems really heavy and awful practice
Thing.find({'nuid':req.body.participant.nuid}, function(err, thing){
console.log(thing);
});
// This block is here to communicate this will create a new thing as expected.
Thing.create(req.body.participant, function(err, thing) {
if(err) { return handleError(res, err); }
});
}
Schema
var ThingSchema = new Schema({
nuid: String
});
UPDATE:
var query = {"nuid": req.body.participant.nuid};
var update = {nuid: 'heyyy'};
Thing.findOneAndUpdate(
query,
update,
{upsert: true},
function(err, thing){
console.log(thing, "thing");
console.log(err, "err");
}
);
I would use findOneAndUpdate first and then based on the result do an insert. findOneAndUpdate use mongoDB findAndModify command.
You should also look at new & upsert options of it which would create a document if not found.
Hi I am currently new to nodejs and mongodb what I want to do is make a function to update my win,lose,draw record from my userschema.
My Schema:
UserSchema = new mongoose.Schema({
username:'string',
password:'string',
email:'string',
//Change Made
win:{ type: Number, default: 0 },
lose:{ type: Number, default: 0 },
draw:{ type: Number, default: 0 }
});
My Function for updating:
//Update scores
app.post("/user/updateScores", function (req, res) {
var user = new User({
username:req.body.username,
win:req.body.win,
lose:req.body.lose,
draw:req.body.draw
});
Users.findOne({ username : req.params.username }, function(error, user) {
if (error || !user) {
res.send({ error: error });
} else {
user.update(function (err, user) {
if (err) res.json(err)
req.session.loggedIn = true;
res.redirect('/user/' + user.username);
});
}
});
});
The problem is when I try updating, when I try updating via my html file. It does not update anything and just stays the same (the values win,lose,draw the default value is 0 so when I logout and login again the values of the win,lose,draw record is still zero). I thoroughly checked if the problem was the html and javascript functions that I have made but this is not the case so I think that the problem is the update function I have made. Any of you guys have an idea where I went wrong? Thanks!
Assuming your post is being called correctly from the client, you'll need to be careful about variable and parameter names, as the scope right now is that you're saving an exact duplicate of the user object that was just fetched via findOne.
You had user declared as a variable of the post callback, and then again within the findOne. The inner variable user will take precedence.
app.post("/user/updateScores", function (req, res) {
var username = req.body.username;
Users.findOne({ username : username }, function(error, user) {
if (error || !user) {
res.send({ error: error });
} else {
// update the user object found using findOne
user.win = req.body.win;
user.lose = req.body.lose;
user.draw = req.body.draw;
// now update it in MongoDB
user.update(function (err, user) {
if (err) res.json(err) {
req.session.loggedIn = true;
}
res.redirect('/user/' + user.username);
});
}
});
});