Trying to populate parent object's array but having trouble doing so - node.js

So I have some users/posts/comments and I want to make sure that when I post a comment on a post, that user's comment array is updated to contain the comment they just made. I tried searching around and it seemed like the best way of doing this was to use mongoose's populate, but it doesn't seem to be working. I'm still a beginner with Mongoose so any help or direction would be appreciated. Thanks!
I tried something like this:
comment.save((err) => { //save the comment
if (err) return next(err);
res.status(200).json(comment);
});
User.find({username: username})
.exec((err, user) => {
user.comments.push(comment); // says comments is undefined, but should be []
user.comment_count++; // also, is there a way to set comment_count equal to the length of the comments array? Should I use .pre()?
user.save(() => {
if (err) return next(err);
});
});
This gives me a error like cannot push into users.comments (undefined).
Here are my Schemas:
const PostSchema = new Schema({
postedBy: {
type: String,
required: true
},
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
createdAt: {type: Date, default: Date.now},
comments: [{ type: Schema.Types.ObjectId, ref: 'CommentSchema' }],
likedBy: [User]
});
const CommentSchema = new Schema({
parentID: { type: String,
required: true,
},
postedBy: {
type: User,
required: true
},
content: {
type: String,
required: true
},
createdAt: {type: Date, default: Date.now},
editedAt: {type: Date, default: Date.now},
likedBy: [User],
});
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
email: {
comments: {
type: [{ type: Schema.Types.ObjectId, ref: 'CommentSchema' }],
default: [],
},
comment_count: { // how do I set this equal to the length of the comments array?
type: Number,
default: 0,
},
posts: {
type: [{ type: Schema.Types.ObjectId, ref: 'PostSchema' }],
default: [],
},
post_count: { // how do I set this equal to the length of the posts array?
type: Number,
default: 0,
},
});
I also tried population:
const comment = new Comment({
parentID,
postedBy,
content,
});
comment.save((err) => { //save the comment
if (err) return next(err);
res.status(200).json(comment);
});
User.find({ username: username })
.populate('comments')
.exec((err, user) => {
if(err) next(err);
console.log(user); // returns undefined for some reason
// also not sure what to do in this function...
});
This gives me an error like "cannot set headers after they are sent to the client."

first read more about how promises and call back worked.
quick fix would be.
comment.save((err) => { //save the comment
if (err) return next(err);
User.find({username: username})
.exec((err1, user) => {
if (err1) return next(err1);
res.status(200).json(user);
});
});
"cannot set headers after they are sent to the client." error occurred as you already send response.
res.status(200).json(comment);

Related

MongoDB finding first user instead of the specified Email user

Im trying to find a user by their email in my MongoDB call through express and mongoose. Im getting it through a request body but at the moment it's only returning the first user in the collection or all the users in the collection, how do I find ONE user by their email address? I would obviously also like to then check their passwords...
User Schema looks like this
const users = mongoose.Schema({
Role: {
type: String,
default: 'Customer'
},
name: {
type: String,
required: true
},
password: {
type: String,
required: true
},
birthday:{
type: String,
required: true
},
displayName: String,
createdAt: {
type: Date,
default: Date.now
},
contact:{
email:{
type: String,
required: true
},
cellphone: String,
},
shippingAd:{
house:{
type: Number,
required: true,
},
road:{
type: String,
required: true,
},
complex: String,
city: {
type: String,
required: true,
},
province:{
type: String,
required: true,
},
postalCode:{
type: String,
required: true,
},
Country:{
type: String,
required: true,
},
},
newsletter:{
type: Boolean,
default: false
},
wishlist: [
{ type: mongoose.Schema.Types.ObjectId, ref: 'products'}
]
});
users.pre('save', async function(next){
try {
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(this.password, salt);
this.password = hashedPassword;
next();
} catch (error) {
next(error);
}
})
Express setup for the call
userRouter.post('/api/loginuser',async (req, res) =>{
const findUser = await userSchema.findOne({
email: req.body.email
});
if(findUser){
return res.json(findUser)
} else{
res.json(false)
}
});
Rest API call
const loginUser = (e) =>{
let payload = {
email: formValues.email,
password: formValues.password
}
axios.post('http://localhost:5001/api/loginuser', payload)
.then(res =>{
if(!res.data){
alert('There was no response from the database.')
} else{
if(res.data){
sessionStorage.setItem('user', res.data.user)
// navigate('/')
console.log(res.data)
}else{
alert('Something is wrong in the backend')
}
}
})
.catch(err =>{
console.log(err);
})
}
Your email field is nested within your contact info so in order to make a query to find a user by the email you have to search for that nest value like this.
const findUser = await userSchema.findOne({
"contact.email": req.body.email
});

Mongoose How to sort .populate field

I'm work with an user/articles profile system. I have been using the .populate() to render the posts but I cannot get the articles sorted by the date they were created.
I am using the createdAt variable as the main way of ordering the posts displayed.
For reference:
router.get('/:id', async (req, res) => {
const user = await User.findById(req.params.id, function(error) {
if(error) {
req.flash("error", "something went wrong")
res.redirect("/");
}
}).populate('articles')
res.render('users/show',{
user: user
});
and the article.js:
const ArticleSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
author: {
type: String
},
markdown: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
},
slug: {
type: String,
required: true,
unique: true
},
sanitizedHtml: {
type: String,
required: true
},
img: {
type: String
},
type:{
type: String
},
user : { type: Schema.Types.ObjectId, ref: 'User' },
}, {timestamps: true});
In advance thank you all for the help.
There is a property called options in populate,
.populate({
path: 'articles',
options: { sort: { createdAt: -1 } }
})

Linking models in MongoDB

I have a model to create an article, and I want to save the id of the user that created that article also. I am doing this, but I have an error. I really appreciate any help!
let articleSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
},
markdown: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
},
user: {type: Schema.ObjectId, ref: 'User'}
});
module.exports = mongoose.model('Article', articleSchema);
let userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
role: {
type: String,
default: 'USER_ROLE',
enum: validRoles
}
});
userSchema.plugin(uniqueValidator, {message: '{PATH} must be unique'});
module.exports = mongoose.model('User', userSchema);
And in the post method of the article I have this:
How can I access the user??
const article = new Article({
title: body.title,
description: body.description,
markdown: body.markdown,
user: req.user._id
});
I think that there's a problem in your article schema. Try to change
user: {type: Schema.ObjectId, ref: 'User'}
to
user: {type: Schema.Types.ObjectId, ref: 'User'}
Then, to display the author information try this in your routes file:
app.get('your-get-article-route', async (req, res) => {
try {
const articles = await Article.find().populate('user');
// other pieces of code if needed
res.status(200).json({ articles });
} catch (error) {
res.status(400).json({ message: 'an error occured!'});
)
If you're not familiar with async/await, you can do it with promises:
app.get('your-get-article-route', (req, res) => {
const articles = Article.find().populate('user').then((articles) => {
// other pieces of code if needed
res.status(200).json({ articles });
}).catch((error) => {
res.status(400).json({message: 'an error occured'}
);
)

Mongoose do not populate objectid in an objectid of array

THIS PROBLEM IS A LITTLE LONGER. SO I TYPED BOLD THE CRITICAL INFORMATIONS FOR YOU.
I develop a project like stackoverflow. I have 4 databases which are:
problems
users
solutions
comments
I referrenced these schemas each other. Here is the Schemas:
Problem Schema
const problemSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'You have to enter a title']
},
content: {
type: String,
required: [true, 'You have to enter a content']
},
createdAt: {
type: Date,
default: Date.now()
},
slug: {
type: String
},
solution: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Solution'
},
],
comment: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
],
votes: {
type: Number,
default: 0
},
views: {
type: Number,
default: 0
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
})
module.exports = mongoose.model('Problem', problemSchema)
User Schema:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: [true, 'You have to enter an email'],
unique: true,
match: [
/^([\w-\.]+#([\w-]+\.)+[\w-]{2,4})?$/,
'Please provide a valid email address.'
]
},
password: {
type: String,
required: [true, 'You have to enter a password'],
minlength: [6, 'Your password cannot be less than 6 character.'],
select: false
},
role: {
type: String,
default: 'user',
enum: ['user', 'admin']
},
createdAt: {
type: Date,
default: Date.now()
},
about: {
type: String
},
place: {
type: String
},
age: {
type: Number
},
blocked: {
type: Boolean,
default: false
},
problem: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Problem'
},
],
solution: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Solution'
}
],
comment: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
})
and Comments Schema:
const commentSchema = new mongoose.Schema({
content: {
type: String,
required: [true, 'You have to enter a content']
},
createdAt: {
type: Date,
default: Date.now()
},
isFunctional: {
type: Boolean,
default: false
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
problem: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Problem'
},
})
module.exports = mongoose.model('Comment', commentSchema)
In my project, I send problems into MongoDB. Then I send comment. After save comments, I add these comments into problems and user DB with a function.
function that comments are saved in DB:
const Comment = require('../models/comment/Comment')
const Problem = require('../models/problem/Problem')
const User = require('../models/user/User')
const asyncErrorWrapper = require('express-async-handler')
const addCommentToProblem = asyncErrorWrapper(async (req, res, next) => {
const {content, problemId} = req.body
const newComment = await Comment.create({
content: content,
problem: problemId,
user: req.user.id,
})
const problemOfComment = await Problem.findByIdAndUpdate(problemId, {
$push: { comment: newComment._id }
})
const userOfComment = await User.findByIdAndUpdate(req.user.id, {
$push: { comment: newComment._id }
})
})
Okey everything is so far so good. The problem comes here. When I try to get a problem, I populate some fields for example user fields. So I can add user information in this detail of problem. When populate user and comment in problem schema, it sends me the data. Still, we're ok. But when I try to get user field in comments, it doesn't populate user. It turns just objectId of user information.
Here is the function that I get problem:
const getAProblem = asyncErrorWrapper(async (req, res, next) => {
const {id} = req.params
const problems = null
await Problem.findByIdAndUpdate(id, {
$inc: { views: 1 }
}, { new: true })
.populate('user') ==> THIS LINE WORKS
.populate('comment') ==> THIS LINE WORKS
.populate('comment.user') ==> THIS LINE DOES NOT WORK
.exec(function(err, post) {
if(err) {
console.log(err)
}
res
.status(200)
.json({
success: true,
data: post
})
});
})
Thanks for reading and your patience. Any help will be appreciated.
See doc at https://mongoosejs.com/docs/populate.html
And try this way.
const getAProblem = asyncErrorWrapper(async (req, res, next) => {
const {id} = req.params
const problems = null
await Problem.findByIdAndUpdate(id, {
$inc: { views: 1 }
}, { new: true })
.populate('user') ==> THIS LINE WORKS
.populate({
'path': 'comment',
'populate': {
'path':'user'
}
})
.exec(function(err, post) {
if(err) {
console.log(err)
}
res
.status(200)
.json({
success: true,
data: post
})
});
})

How to push objectId to mongodb schema field array of object using findByIdAndUpdate

Everytime I hit api I am getting same error-
I have tried sending value as parameters also but failed. Any help would be appreciated. When i use $set it updates same value everytime the web service is called but it does work but not with $push.
MongoError: The field 'songId' must be an array but is of type objectId in document
{_id: ObjectId('59709590380026118c22dd61')}
My Playlist schema code:-
var PlaylistSchema = new mongoose.Schema({
name: String,
coverphoto: { type: String, default: '' },
userId: {
type: ObjectId,
ref: 'User'
},
songId: [{ type: ObjectId, ref: 'Song' }],
updated_at: { type: Date, default: Date.now },
});
my Song Schema code
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var SongSchema = new mongoose.Schema({
audioName:{type:String,default:''},
title: String,
// coverphoto: { type: String, default: '' },
singer: { type: String, default: '' },
movie: { type: String, default: '' },
album: { type: String, default: '' },
lyricist: { type: String, default: '' },
actors: { type: String, default: '' },
lyrics: { type: String, default: '' },
genre: { type: String, default: 'Random' },
duration: { type: String, default: '' },
size: { type: String, default: '' },
userId: {
type: ObjectId,
ref: 'User'
},
categoryId: {
type: ObjectId,
ref: 'Category'
},
updated_at: { type: Date, default: Date.now },
});
module.exports = mongoose.model('Song', SongSchema);
My Api code
/* post api to add song in playlist */
router.post('/addSongToPlaylist', function (req, res, next) {
Playlist.findOne({ '_id': req.body.playlistId }, function (err, playlist) {
if (err) return next(err);
console.log(playlist)
console.log("req.body.songId", req.body.songId);
if (playlist) {
Playlist.findByIdAndUpdate(
req.body.playlistId,
{ $push: { "songId": req.body.songId } },
{ new: true },
function (err, playlistData) {
if (err) return next(err);
res.json({ message: 'New Song added successfully', playlist: playlistData, success: true });
});
} else if (!Song) {
res.json({ message: 'Failed to add song', success: false });
}
});
});

Resources