checking of field in a document - node.js

Let's say I have the following schema
var userSchema = new Schema({
name : String
});
var User = mongoose.model('User',userSchema);
EDIT: If an user trying to update field, that does not exists, I need throw exception. My question is how can I check that an updating field does not exists in the updating document. Here is a little example what I need:
app.post('/user/update/:id', function (req, res) {
var field = req.param('field'),
value = req.param('value'),
id = req.param('id');
User.findOne({_id: id},function(err, user){
if(err) throw err;
if (user) {
user[field] = value; // Here is I need to check that field is exists
// in user schema. If does't I have to throw
// an execption.
user.save(function (err){
return res.send(200);
});
}
})
});

Try adding $exists to the query parameter of update(). This will allow you to only update documents if a certain field exists (or not).
http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24exists

From the Mongoose v3.1.2 guide:
The strict option, (enabled by default), ensures that values added to our model instance that were not specified in our schema do not get saved to the db. NOTE: do not set to false unless you have good reason.
The strict option may also be set to "throw" which will cause errors to be produced instead of ignoring the bad data.
http://mongoosejs.com/docs/guide.html#strict

var CollectionSchema = new Schema({name: 'string'}, {strict: 'throw'});
Collection.findById(id)
.exec(function (err, doc) {
if (err) {// handle error};
// Try to update not existing field
doc['im not exists'] = 'some';
doc.save(function (err) {
if (err) {
// There is no an errors
}
return res.json(200, 'OK');
});
});
In the expample above I don't get an error when I do update a not existing field.

You can check if the field exists in the schema by using .schema.path(). In your specific use case you can do the following:
app.post('/user/update/:id', function (req, res) {
var field = req.param('field'),
value = req.param('value'),
id = req.param('id');
User.findOne({_id: id},function(err, user){
if(err) throw err;
if (user) {
if(User.schema.path(field)) {
user[field] = value;
} else {
throw new Error('Field [' + field + '] does not exists.');
}
user.save(function (err){
return res.send(200);
});
}
});
});

Related

MongoDb delete documents by passing user id

I am new to node and mongo db. I have a list of users with delete link in each row.I am trying to delete a user with its _id. However its not working.
Here is my router code.
router.get('/delete/:id', function (req,res) {
const ObjectId = require('mongodb').ObjectID;
var id = req.params.id;
console.log(id);
db.collection('users').deleteOne({ _id: ObjectId(req.params.id) }, function(err, res) {
if (err) {
throw err;
} else {
return res.redirect('/');
}
});
});
Here is my view, on clicking this link I am getting the _id in my url as this : http://localhost:3000/delete/4428439e14e3343ba4ac31c1
<td>Delete</td>
console.log(id) gives me 4428439e14e3343ba4ac31c1
But it throws me the below error
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
at new ObjectID
Try this, you don't need to create ObjectID if the string is a valid ObjectID
For prevention, you can use a function like below to test if valid ObjectID is passed or not
function validateObjectId (id) {
if (ObjectId.isValid(id)) {
const obj = new ObjectId(id);
if (obj == id) {
return true;
}
}
return false;
},
if(!validateObjectId(req.params.id))
return res.send({'error':'Invalid ObjectID Passed',id:req.params.id});
db.collection('users').deleteOne({ _id: ObjectId(req.params.id) }, function(err, res)
{
if (err) {
throw err;
} else {
return res.redirect('/');
}
});
Also remove extra space from here
<td>Delete</td>
You need to create a instance of ObjectId using new. Currently, you are passing the ObjectId directly so you are getting that error.
db.collection('users').deleteOne({_id: new ObjectId(req.params.id)}, function(err, res) {
if (err) {
throw err;
} else {
return res.redirect('/');
}
});
you can use findByIdAndRemove
router.get('/delete/:id', function (req,res) {
var id = req.params.id;
db.collection('users').findByIdAndRemove( id , function(err, res) {
if (err) {
throw err;
} else {
return res.redirect('/');
}
});
});
Maybe you can try this code below:
router.get("/delete/:id", function(req, res) {
const ObjectId = require("mongodb").ObjectId;
var { id } = req.params;
console.log(id);
db.collection("users").findOneAndDelete({ _id: ObjectId(id) }, function(
error,
response
) {
if (error) {
throw err;
} else {
return res.redirect("/");
}
});
});
Updated:
Try looking at your code. There's confusing code, you use 2 times res. One is res from express and the other isres when it succeeds in removing at mongodb.
So, the res.redirect ('/') that you use in your mongodb function is res from mongodb, notres from express.
Try replacing {err, res} with {error, response}.
I hope it can help you.

Mongoose model.save( ) is not working as expected

I have this code and MongoDB is passing back an error after I attempt to save a new document:
var data = {
_id: '55d65cfde9cf73322361860b' // _id is a string here
};
var model = new Model(data);
model.save(function (err, result) {
if (err) {
done(err); //the error is captured here in my code
}
else{
done(null, result);
}
});
and I get an error:
MongoError: E11000 duplicate key error index: dev_local_db_smartconnect.jobs.$_id_ dup key: { : ObjectId('55d65cfde9cf73322361860b') }
however, I was under the impression that save would update the model/document if it exists (by using an implicit upsert=true option), and it would use the _id field to look for an existing document.
Does anyone know why this error would occur?
On the other hand, this works for me:
where data is the new data for the model, and _id is a string not an ObjectID, and Model is the mongoose class, not the instance.
Model.update({_id:_id}, {$set: data}, {upsert:true} , function(err,result){
if (err) {
done(err);
}
else if (result) {
done(null, result);
}
else {
done(new Error('grave error'));
}
});
Since you are creating a new local document Mongoose doesn't know it already exists on the server so it will try to save it as a new document. Mongo will then reject the document since an existing document with that ID already exists.
If you query for that document first and then save the returned document it would work as expected. Something like this:
Model.find({id: '55d65cfde9cf73322361860b'}, function (err, doc) {
// update doc with something
// ...
doc.save(function (err, result) {
if (err) {
done(err); //the error is captured here in my code
}
else {
done(null, result);
}
});
});

Mongoose: can't update in save callback

I've this mongoose Schema:
var UrlSchema = new mongoose.Schema({
description: String
});
Then, I create a model istance:
var newUrl = new Url({
"description": "test"
});
newUrl.save(function (err, doc) {
if (err) console.log(err);
else{
Url.update({_id: doc._id},{description: "a"});
}
});
But any update performed... Why?
Thanks
You need to add a callback to the update method or call #exec() to perform the update:
var newUrl = new Url({
"description": "test"
});
newUrl.save(function (err, doc) {
if (err) console.log(err);
else{
Url.update({_id: doc._id},{description: "a"}, function (err, numAffected) {
// numAffected should be 1
});
// --OR--
Url.update({_id: doc._id},{description: "a"}).exec();
}
});
Just FYI: I personally stay away from update because it bypasses defaults, setters, middleware, validation, etc which is the main reason to use an ODM like mongoose anyway. I only use update when dealing with private data (no user input) and auto incrementing values. I would rewrite as this:
var newUrl = new URL({
"description": "test"
});
newUrl.save(function(err, doc, numAffected) {
if (err) console.log(err);
else {
doc.set('description', 'a');
doc.save();
}
});

Return data from mongoDB nested queries

What is the method to return data from nested queries.
Im merging multiple function calls into one single module ,
I have the following code
retrieveStudentSessions: function(req, res, callback){
MongoClient.connect(config.mongoPath+config.dbName, function(err, db) {
if(err){
return callback(new Error("Unable to Connect to DB"));
}
var collection = db.collection(config.userList);
var sessionCollection = db.collection(config.session);
var templateCollection = db.collection(config.template);
var tempSession = {session:[], templates:[]};
//Obtain UserID
collection.find({'email' : req.user['_json'].email}).nextObject(function(err, doc) {
if(err){
return callback(new Error("Error finding user in DB"));
}
//Search Session collection for userID
sessionCollection.find({ $or : [{'studentsB' : doc['userid']},{'studentsA' : doc['userid']}]}).each(function(err, doc) {
if(err){
return callback(new Error("Error finding user in DB"));
}
//Update JSON
tempSession.session.push(doc);
//Query for Template Title using Template ID from Session Collection
templateCollection.find({'_id' : doc['templateId']}).nextObject(function(err, doc){
if(err){
return callback(new Error("Error finding user in DB"));
}
//Update JSON
tempSession.templates.push(doc);
});
});
return callback(null, tempSession);
});
});
}
Caller Function
dbCall.retrieveStudentSessions(req, res, function(err, result){
if(err){
console.log(err);
return;
}
console.log(result);
});
Above code returns error undefined is not a function when i try to return variable tempSession. The same works fine with single queries . Is there any specific method of return, when it comes to nested queries?

can't populate the array with mongoose in node.js

This is my schema on the course
var CourseSchema = mongoose.Schema({
students:[{ type: ObjectId, ref: 'User' }]
});
var CourseModel = mongoose.model('Course',CourseSchema);
var UserSchema = mongoose.Schema({ name:String})
var UserModel = mongoose.model('User',UserSchema);
In the mongodb, I have created the existing courses and users, and when the user want to participate the course, the user reference will be added to the students array in the course model.
Here is how I try to add the user reference to the students
function joinCourse(cid,uid,callback){
var options = { new: false };
var uid = mongoose.Types.ObjectId(uid);
CourseModel.findOneAndUpdate({'_id':cid},{'$addToSet':{'students':uid}},options,function(err,ref){
if(err) {
console.log('update joinCourse'.red,err);
callback(err, null);
}else{
console.log('update joinCourse '.green+ref);
callback(null,ref);
}
})
}
when the above function is executed, the students array has the objectID or reference of the user. However, when I want to populate the students from the course model, it doesn't work.
var id = mongoose.Types.ObjectId(id);
CourseModel.findById(id).populate('students').exec(function(err, users) {
if(err){callback(err, null);}
else{
//// users.length == undefined
console.log("findCourseStudentsById".green,users.length);
callback(null, users);
}
})
I didn't find any problem on populate function, so I wonder is there something wrong with joinCourse function? so I change the function as
courseModel.findCourseById(cid,function(err,course){
if(err) next(err);
else{
course.students.push({'_id': mongoose.Types.ObjectId(uid)});
course.save(function (err) {
if (err) next(err);
});
}
})
but still the populate doesn't work. Note, I am using mongoose 3.6
populate populates the model instance, but the callback is passed the model instance on which you call populate, and not the populated data itself:
CourseModel.findById(id).populate('students').exec(function(err, course) {
if(err){callback(err, null);}
else{
console.log("findCourseStudentsById".green, course.students.length);
callback(null, course);
}
});

Resources