Linking models in MongoDB - node.js

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

Related

How to get comment from embedded document in mongodb?

I want to get the comment by ID that is an array of embedded schema object in feedback schema model, see the code below:
Feedback schema:
const FeedbackSchema = new Schema({
receiver: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
comments: [
{
text: { type: String, maxlength: 500 },
postedBy: { type: Schema.Types.ObjectId, ref: 'User' },
date: { type: Date, default: Date.now() },
},
],
createdAt: {
type: Date,
default: Date.now(),
},
});
module.exports = {
FeedbackSchema: mongoose.model('Feedback', FeedbackSchema),
};
Comment controller and route:
router.get('/comment/:feedback_id/:comment_id', userAuthorization, getComment);
exports.getComment = async (req, res) => {
const feedback = await FeedbackSchema.findById({
_id: req.params.feedback_id,
});
if (!feedback) return res.status(404).send('Feedback not found');
const comment = feedback.comments.find({ _id: req.params.comment_id }); //error is in this line
if (!comment) return res.status(404).send('Comment not found');
return res.json(comment);
}
How do I get the comment based on feedback_id and comment_id?
Try to use findOne:
exports.getComment = async (req, res) => {
const feedback = await FeedbackSchema.findOne({
_id: req.params.feedback_id,
comments._id: req.params.comment_id
});
if (!feedback) return res.status(404).send('Feedback not found');
return res.json(feedback.comments);
};
Also, make sure that you declare the Comment schema separately:
const CommentSchema = new Schema({
text: { type: String, maxlength: 500 },
postedBy: { type: Schema.Types.ObjectId, ref: 'User' },
date: { type: Date, default: Date.now() },
})
const FeedbackSchema = new Schema({
receiver: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
comments: [CommentSchema],
createdAt: {
type: Date,
default: Date.now(),
},
});

mongoose .save() doesn't work on a specific collection

so I'm trying to create a party with creator field with id of a user, and at the same time adding a party id to users parties using mongoose sessions. Here's the code of a request:
const createParty = async (req, res, next) => {
const {title, description, address, creator} = req.body;
const createdParty = new Party({
title,
description,
image: 'https://media-cdn.tripadvisor.com/media/photo-s/14/03/b3/4e/tlv.jpg',
address,
creator,
savedBy: []
});
let user;
try {
user = await User.findById(creator);
} catch (err) {
let error = new HttpError('Fetching user failed', 500);
return next(error);
}
if (!user) {
return next(new HttpError('Could not find user for providen id', 404));
}
try {
const sess = await mongoose.startSession();
sess.startTransaction();
await createdParty.save({ session: sess });
user.parties.push(createdParty);
console.log(user);
await user.save({ session: sess });
await sess.commitTransaction();
} catch (err) {
let error = new HttpError('Creating party failed', 500);
return next(error);
}
res.status(201).json({party: createdParty});
};
And my user and parties schemas:
const userSchema = new Schema({
username: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true, minlength: 6 },
image: { type: String, required: true },
parties: [{ type: mongoose.Types.ObjectId, required: true, ref: 'Party' }],
savedParties: [{ type: mongoose.Types.ObjectId, required: true, ref: 'Party' }]
});
const partySchema = new Schema({
title: { type: String, required: true },
description: { type: String, required: true },
image: { type: String, required: true },
address: { type: String, required: true },
creator: { type: mongoose.Types.ObjectId, required: true, ref: 'User' },
savedBy: [{ type: mongoose.Types.ObjectId, required: true, ref: 'User' }]
});
The problem is I can't save a user with new party id, only this line fails:
await user.save({ session: sess });. Tried to move this line to a separate try/catch, tried to add user.markModified('parties'); didn't help. Please help those who may know the solution.🙏🏻
UPDATE ON THE PROBLEM
So I did some testing and found out that if I delete everything from the database, and I'll create a user I will be able to add parties, and it'll work as it should. But if I'll create another user and afterward will try to add a party to one of the users it won't work.
when you session it won't create the collection if it doesn't exist and you need to do it manually in the data

post author Id turn into author username in node js for mongodb

i am trying to have my post's author's name in frontend. so i want to find the post according to it's user Id. but in model schema i used obejct Id of user in post Schema.
Here is my userSchema:
const mongoose = require('mongoose');
// user schema
const userSchema = new mongoose.Schema(
{
email: {
type: String,
trim: true,
required: true,
unique: true,
lowercase: true
},
name: {
type: String,
trim: true,
},
password: {
type: String,
required: true
},
salt: String,
bio: {
type: String,
trim: true
},
role: {
type: String,
default: 'subscriber'
},
resetPasswordToken: String,
resetPasswordExpire: Date,
},
{
timestamps: true
}
);
module.exports = mongoose.model('User', userSchema);
here is my postSchema model:
const mongoose = require("mongoose");
const PostSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
comments: [{
text: String,
created: { type: Date, default: Date.now },
postedBy: { type: mongoose.Schema.ObjectId, ref: 'User'}
}],
created: {
type: Date,
default: Date.now
},
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
},
{
timestamps: true,
},
);
const Post = mongoose.model("Post", PostSchema);
module.exports = Post;
and here is my router for post lists by a specific user id:
exports.postByUser=async(req,res)=>{
try
{
const userID=async()=>{
await User.findById({ _id:req.params.id})
.then(posts=>{
res.status(200).json(posts.name)
})
}
await Post.find({creator: req.params.id})
.then(posts=>{
res.status(200).json(posts)
})
}catch(error){
res.status(500).send({error: error.message});
};
}
router.route('/post/mypost/:id').get(requireSignin,postByUser);
my target is to get a post list where every post's creator would have the user name. how can i achieve that in nodejs?
i have solved this way:
exports.postByUser=async(req,res)=>{
try
{
await Post.find({creator: req.params.id})
.populate({path:'creator', select:'name -_id'})
.then(post=>{
res.status(200).json(post)
})
}catch(error){
res.status(500).send({error: error.message});
};
}
and it worked

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

Mongoose Populate not working while querying data

I have 2 models, category and story.
Story contains reference id of the category.
In controller story, I have a function mystories which should fetch all the story records of particular user along with category information.
I am getting data from story collection but not from category collection.
The result which I receive is something like this:
category_id: "5d10978c8e0f5d5380fdb3e6"
created_at: "2019-06-25T10:02:47.637Z"
created_by: "5d1066fba920ef2ccfe68594"
image: "uploads/1561456967615_164.jpg"
published: "no"
status: "pending"
text: "<p><strong>Fashion</strong> is a popular aesthetic expression in a certain time and context, especially in clothing, footwear, lifestyle, accessories, makeup, hairstyle and body </p>"
title: "New fashion"
__v: 0
_id: "5d11f14757f8616041616217"
It should however return category collection information instead of
category id.
Category model:
const mongoose = require('mongoose');
const categorySchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
required: true,
unique: true
},
status: {
type: String,
required: true,
enum: ['active','inactive','deleted']
},
created_at: { type: Date, default: Date.now },
created_by: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true},
});
module.exports = mongoose.model('Category', categorySchema);
Story model:
const mongoose = require('mongoose');
const storySchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: {
type: String,
required: true
},
text: {
type: String,
required: true
},
category_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Category',
required: true
},
image: {
type: String,
required: true
},
status: {
type: String,
required: true,
enum: ['pending','approved','deleted']
},
published: {
type: String,
required: true,
enum: ['yes','no']
},
created_at: { type: Date, default: Date.now },
created_by: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true},
});
module.exports = mongoose.model('Story', storySchema);
Controller code:
const mongoose = require('mongoose');
const Story = require('../models/story');
const Category = require('../models/category');
exports.mystories = async (req, res, next) => {
const user_id = req.userData.id;
const all_stories = await Story
.find({ created_by: user_id})
.populate('name','category')
.sort({ created_at: -1 })
.exec();
if(all_stories.length > 0) {
return res.status(200).json({
response: all_stories
});
}else {
return res.status(200).json({
response: []
});
}
};
exports.add_story = (req, res, next) => {
console.log(req.file);
const story = new Story({
_id: new mongoose.Types.ObjectId(),
title: req.body.story_title,
text: req.body.story_text,
category_id: req.body.story_category,
image: req.file.path,
status: 'pending',
published: 'no',
created_by: req.userData.id
});
story
.save()
.then(result => {
console.log(result);
res.status(200).json({
response: 'added_story'
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
})
});
};
populate takes the field name as it is given in story schema.
it should be :
.populate({path: 'category_id', select: 'name'})

Resources