can't populate the array with mongoose in node.js - 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);
}
});

Related

How this pre hook save works?

I have been trying to use populate in mongoose, and my current idea is to check if a particular document is present in MongoDB, if it is then, the pre-save hook should check and rather then creating a new Document, it should just push it to the refs, so that I can populate later.
Now, the ref is being saved in MongoDB, however, how to check if the document which I am trying to save is there in mongo in pre save(or any other more suitable method)
This is the schema.
var Userschmea = new mongoose.Schema({
user:String,
posts:[{
type:mongoose.Schema.Types.ObjectId,
ref:'Post'
}]
})
var PostSchema = new mongoose.Schema({
content:String,
author:[{
type:mongoose.Schema.Types.ObjectId,
ref:'Author'
}]
})
Userschmea.pre('save',(next)=>{
//what to do here
next()
})
var Post = mongoose.model('Post',PostSchema);
var User = mongoose.model('User',Userschmea);
This is the endPoint by which I am trying to save:
app.post('/save/user',(req,res)=>{
console.log(req.body);
//Can i access this in my pre-save
const newUser = new User({
user: req.body.user
})
newUser.save((err)=>{
const newPost = new Post({
content:req.body.content,
author: newUser._id
})
newPost.save((err)=> {
if(err) {
return res.send(err);
}
})
if(err){
console.log(err);
return res.send(err);
}
})
return res.send(req.body.user);
})
You can use findOne method with some unique key to check whether user is exists or not. I have used here email.
Userschmea.pre('save',(next)=>{
var self = this;
Userschmea.findOne({email : this.email}, 'email', function(err, results) {
if(err) {
next(err);
} else if(results) {
self.invalidate("user", "user is exists");
next(new Error("User is already exists"));
} else {
next();
}
});
})
Hope this help!
====== UPDATE =====
Q: this is coming empty.
Ans:
Function expressions are best for object methods while Arrow functions are best for callbacks or methods like map, reduce, or forEach.
So, Don't use arrow function in this case to access global scope.

Mongoose: Built in validations on additional properties of SchemaType not working like min, max, minlength, maxlength etc

I am new to mongoose (MongoDB) and Nodejs, i am creating RestFul services for CRUD operations, however my issue is, for the Schema expression, the SchemaType additional properties are not being considered in the built in validation of mongoose where as it is considering required property only. Please find my model below for your reference:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let ProductSchema = new Schema({
name: {
type: String,
required: true,
minlength:2,
maxlength:10
},
price:{
type: Number,
required:true,
min:2,
max:100
},
})
//Export the model
module.exports = mongoose.model('Product', ProductSchema);
So in the above model, There are properties like min, max for Number data Type and minlength and maxLength for String data Type which are not at all considered for the validation to take place before saving into the MongoDB.
Am i missing any configuration in this? i have gone through the mongoose documentation and also through the stackoverflow's lot of posts but i did not get any information regarding this particularly.
Here is my controller as well:
const Product = require('../models/product.model');
// Simple version, without validation or sanitation
exports.test = function (req,res) {
res.send('Greetings from the Test Controller!');
};
exports.product_create = function (req,res,next) {
let product = new Product(
{
name: req.body.name,
price: req.body.price
}
);
product.save(function (err) {
if(err){
return next(err);
}
res.send('Product created Successfully');
}
)
};
exports.product_details = function (req,res, next) {
Product.findById(req.params.id, function (err, product){
if(err) {
return next(err);
}
res.send(product);
})
};
exports.product_update = function (req, res, next) {
Product.findOneAndUpdate(req.params.id, {$set: req.body},opts, function (err, product){
if(err) return next(err);
res.send('Product Updated');
})
};
exports.product_delete = function (req,res,next) {
Product.findOneAndRemove(req.params.id, function (err) {
if(err) return next(err);
res.send('Deleted product');
})
};
Please guide me if i am missing something, still that needs to incorporate into it. Thanks.

Need to do a many comments belong to one article relation MongoDB

I am using Mongoose/MongoDB and I am trying to associate many comments to one article. My app begins by scraping from a website and then the user has the option to save each article that was scraped into the MongoDB. When the user chooses to save one article, I save it into database. So when a user clicks on one of their saved articles, they can comment on them. Each article has its own comment section I need to retrieve the correct comments.
//My post comment request in JS file
function postComment(){
var articleComment = {
comment: $('#comment').val().trim()
}
$.post('/comments/' + articleID, articleComment).done(function(data){
$('.main-popup').fadeOut();
console.log('DONNE', data);
});
}
//Post route in controller
router.post('/comments/:id', function(req, res){
var newComment = new Comment(req.body);
newComment.save(function(err, doc){
if(err){
console.log(err);
}else{
Comment.findOneAndUpdate({ "_id": doc._id }, { "article": req.params.id }).exec(function(err, doc){
if(err){
console.log(err);
res.send(err);
}else{
res.send(doc);
}
});
}
});
});
//Get request to get correct comments when clicked on specific article
function showCommentBox(){
$('.comments').empty();
$('#comment').val("");
articleID = $(this).attr('data-article-id');
$.get('/comments/' + articleID, function(data){
if(data.article){ //This is undefined*********************
for(var x = 0; x < data.comment.length; x++){
$('.comments').append("<div><h2>" + data.comment[x].comment + "</h2><span><button>×</button></span></div>");
}
}
$('.main-popup').fadeIn();
});
}
//Get route in controller
router.get('/comments/:id', function(req, res){
Comment.findOne({ "article": req.params.id }).populate("article").exec(function(err, doc){
if(err){
console.log(err)
}else{
res.json(doc);
}
});
});
//Article Model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ArticleSchema = new Schema({
title: {
type: String
},
link: {
type: String
},
description: {
type: String
},
img: {
type: String
}
});
var Article = mongoose.model("Article", ArticleSchema);
module.exports = Article;
//Comment Model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CommentSchema = new Schema({
comment: {
type: String
},
article: {
type: Schema.Types.ObjectId,
ref: 'Article'
}
});
var Comment = mongoose.model('Comment', CommentSchema);
module.exports = Comment;
First, you're missing $set when you do .findOneAndUpdate. Also I think you should convert a string to Mongo ObjectId before setting it.
So it might look likt this:
const ObjectId = mongoose.Types.ObjectId;
Comment.findOneAndUpdate({ "_id": doc._id }, {$set: {"article": new ObjectId(req.params.id) }})
Also you don't need to make 2 database calls. You could article id before saving newComment and then simply send it as a response like this:
//Please notice that mongoose.Schema.Types.ObjectId and mongoose.Types.Object are different types.
//You need this one here:
const ObjectId = mongoose.Types.ObjectId;
router.post('/comments/:id', function(req, res){
var newComment = new Comment(req.body);
newComment.article = new ObjectId(req.params.id);
newComment.save(function(err, doc){
if (err) {
console.error(err);
res.send(err);
return;
}
res.send(doc);
});
});

How to model and save in mongoose multi-to-multi relationship?

I want to 2 model, one is user that can belongs to multiple groups, and another is group that can has multiple users.
This is my schemas and models, i don't know whether they the correct:
var Schema = mongoose.Schema;
var UserSchema = new Schema({
joinedGroups:[{type:Schema.Types.ObjectId, ref: 'Group'}]
}
);
var GroupSchema = new Schema({
createdBy: {type: Schema.Types.ObjectId, ref: 'User'},
joinedUsers:[{ type: Schema.Types.ObjectId, ref: 'User' }]
});
var User = mongoose.model('User',UserSchema);
var Group = mongoose.model('Group',GroupSchema);
when receive POST of url:/api/groups with the body of user._id, I want to join this user to new create group, besides, i want to join this new created group to user's joinedGroups and finally i want to response the client of the new group with users in it. Follow is my code of doing this:
app.post('/api/groups', function(req, res){
console.log(req.body);
var userId = req.body.user_id;
var group = {
createdBy : userId
};
Group.create(group, function(err,group){
if(err){
console.log('create group err');
res.send(err);
}
else{
console.log('create group success');
User.update({_id: userId},
{$push: {joinedGroups: group._id}},
function(err,user){
if(err){
console.log('update user err');
res.send(err);
}
else{
Group.update({_id: group._id},
{$push: {joinedUsers: user}},
function(err,group){
if(err){
console.log('update group err:' + err);
res.send(err);
}
else{
group.populate({path:'joinedUsers'},
function(err, group){
if(err){
console.log('populate group err');
res.send(err);
}
else{
console.log('populate group success');
res.json(group);
}
}
);
}
});
}
});
}
});
});
I feel it's really complex, and it occur error :
update group err:CastError: Cast to ObjectId failed for value "1" at path "joinedUsers"
So i want somebody help me with right solution to do this, thanks!
edit:
I also want to support join user in to existed group in PUT /api/group/:group_id like below:
var userId = req.body.user_id;
var groupId = req.params.group_id;
how to do that? thanks!
First of all, your realization is really complex and it can be simplified as this:
var userId = req.body.user_id; // this is an ObjectId
var group = {
createdBy: userId,
joinedUsers: userId
};
Group.create(group, function (err, group) {
if (err || !group) {
return res.send(err);
}
User.findById(userId, function (err, user) {
if (err || !user) {
return res.send(err);
}
user.joinedGroups.push(group._id);
user.save(function (err) {
if (err) {
return res.send(err);
}
group.populate('joinedUsers', function (err, group) {
if (err || group) {
return res.send(err);
}
res.json(group);
});
});
});
});
And the reason why you getting CastError error is: the update method returns 1 as second argument of callback if successfully updated. But your Group#joinedUsers filed expecting User reference.

checking of field in a document

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);
});
}
});
});

Resources