Moongoose populate method returning an empty array - node.js

I am trying to fetch the blogs that a user has posted using mongoose populate method but not able to do so but when I try to look for the user who posted a blog I am able to do so. Tell me what I am doing wrong.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema({
username: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
blogPosts: [{ type: mongoose.Types.ObjectId, ref: "Blogs", required: true }],
});
const Users = mongoose.model("Users", userSchema);
module.exports = Users;
This is my user model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const blogSchema = new Schema({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
image: {
type: String,
required: true,
},
user: {
type: mongoose.Types.ObjectId,
ref: "Users",
required: true,
},
});
const blogs = mongoose.model("Blogs", blogSchema);
module.exports = { blogs, blogSchema };
this is my blogs model
router.get("/users/:id", async (req, res) => {
let userBlogs;
try {
userBlogs = await users.find({ _id: req.params.id }).populate("blogPosts");
if (!userBlogs) {
return res.status(404).json({ msg: "No user found" });
}
return res.status(200).json(userBlogs);
} catch (err) {
return res.status(500).json({ err });
}
});
this is the route that I use for fetching all the blogs that a user has posted.This is just sending me back an empty posts array.
router.post("/newBlog/:id", async (req, res) => {
let blogAuthor;
try {
blogAuthor = await users.findOne({ _id: req.params.id });
} catch (err) {
return res.status(404).json({ message: "user not found" });
}
const newBlog = blogs({
title: req.body.title,
description: req.body.description,
image: req.body.imageUrl,
user: blogAuthor._id,
});
try {
await blogAuthor.blogPosts.push(newBlog);
await newBlog.save();
return res.status(200).json(blogAuthor.blogPosts);
} catch (err) {
return res.status(500).json(err);
}
});
the above is the route that I use to add new blog to the db.
router.get("/allBlogs", async (req, res) => {
let allBlogs;
try {
allBlogs = await blogs.find({}).populate("user");
} catch (err) {
return res.status(404).json(err);
}
res.status(200).json(allBlogs);
});
this is the route that sends all the blogs posted by all the users.
Interesting thing is that when I try to populate the user of a blogpost that is working exactly as expected it is populating the user of the blog from the users model however when I try to do the reverse that's not working. It is not populating all the blogs that a user has posted.

Related

How to Join model in Mongodb(Mongoose) and express?

I have 3 models 'User' , 'Doctor', 'Appointment', I want to let the user make an appointment then when he get his appointment I want to return the doctor name, also when the doctor get the appointment I want t return the user Name.
User Model :
const mongoose = require('mongoose');
const User = mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
})
const User = mongoose.model('User', User);
module.exports = { User };
Doctor Model :
const mongoose = require('mongoose');
const Doctor = mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
})
const Doctor = mongoose.model('Doctor', Doctor);
module.exports = { Doctor };
Appointment Model :
const mongoose = require('mongoose');
const Appointment = mongoose.Schema({
date: {
type: Date,
},
time: {
type: Date
},
_userID: {
type: mongoose.Types.ObjectId,
ref: 'User'
},
_doctorID: {
type: mongoose.Types.ObjectId,
ref: 'Doctor'
}
})
const Appoitment = mongoose.model('Appoitment', Appointment);
module.exports = { Appoitment };
Make and Get Appointment :
const express = require('express');
const { Appointment } = require('../DataBase/Models/appointment.model');
const router = express.Router();
router.get("/appointment/:id", async (req, res) => {
try {
const appointment = await Appointment.find({
user: req.params.id,
}).populate({
path: "doctor",
model: "Doctor",
});
res.send({
status: 200,
message: "SuccessFull",
Appointments: appointment,
});
} catch (error) {
res.send({
status: 400,
message: `Error: ${error}`,
});
}
});
router.post("/appointment", async (req, res) => {
try {
const makeAppointment = new Appointment(req.body);
const result = await makeAppointment.save();
res.send({
status: 200,
message: "SuccessFull",
Appointment: result,
});
} catch (error) {
res.send({
status: 404,
message: `Error : ${error}`,
});
}
});
My Question is How I can return the Appointment with the doctor Name the same with the User Name ??
in the .populate method, the path param is the name of the attribute in the model that you're trying to retrieve, so instead of path: 'doctor', you should be using '_doctorID' because you used this as the attribute name in the Appointment Model.
The same works for your query in the .find, you're querying the 'user' attribute, but you have _userID in your appointment model.
So, you have to 2 options:
Change _userID and _doctorID to user and doctor, this way should be better;
Or change user and doctor in your controller to _userID and _doctorID;
If you follow the first option, your code now should be something like:
Appointment Model:
const mongoose = require('mongoose');
const Appointment = mongoose.Schema({
date: {
type: Date,
},
time: {
type: Date
},
user: {
type: mongoose.Types.ObjectId,
ref: 'User'
},
doctor: {
type: mongoose.Types.ObjectId,
ref: 'Doctor'
}
})
const Appoitment = mongoose.model('Appoitment', Appointment);
module.exports = { Appoitment };
Appointment Controller:
router.get("/appointment/:id", async (req, res) => {
try {
const appointment = await Appointment.find({
user: req.params.id,
})
.populate({
path: "doctor",
select: "_id name",
});
res.send({
status: 200,
message: "SuccessFull",
Appointments: appointment,
});
} catch (error) {
res.send({
status: 400,
message: `Error: ${error}`,
});
}
});
if you want to select specific column .
it will look like .populate('author', 'name'). // only return the Author name

username on posts not updated after profile information update in Node.js/mongoosedb

I am using Mongoose with express.js and will like to update user information across all instances.
When user updates their profile information, it gets updated on their profile document in the mongoose database. However, their username has refused to be updated on their posts. I need their username updated across all instances including their post username. Thank you for the help
I know I am not doing something right.
Here are my codes:
//user updates profile information
router.put("/:id", async (req, res) =>{
if(req.body.userId === req.params.id){//we checked if the user id matched
if(req.body.password){
const salt = await bcrypt.genSalt(10);
req.body.password = await bcrypt.hash(req.body.password, salt);
}
try{
const updatedUser = await User.findByIdAndUpdate(req.params.id,{
$set: req.body,
}, {new: true}); //findbyidandupdate is an inbuilt method
res.status(200).json(updatedUser)
} catch(err){
res.status(500).json(err) //this handles the error if there is one from the server
}
} else{
res.status(401).json("You can only update your account!")
}
});
User updates post codes
router.put("/:id", async (req, res) =>{
//find post that will be updated
try{
const post = await Post.findById(req.params.id);
if(post.username == req.body.username )
try{
const updatedPost = await Post.findByIdAndUpdate(req.params.id,
$set: req.body
}, {new: true})//this makes it possible to see the updated post
res.status(200).json(updatedPost)
}catch(err){
res.status(401).json(err)
}
} else{
res.status(401).json("you can only update your posts")
}
}catch(err){
res.status(500).json(err)
}
});
User model
const UserSchema = new mongoose.Schema({
username:{
type: String,
required: true,
unique: true
},
email:{
type: String,
required: true,
unique: true
},
password:{
type: String,
required: true
},
profilePicture:{
type: String,
default: "",
},
}, {timestamps: true}
);
//exporting this schema
module.exports = mongoose.model("User", UserSchema); //the module name is "User"
Post model
const PostSchema = new mongoose.Schema(
{
title:{
type: String,
required: true,
unique: true
},
description:{
type: String,
required: true,
},
postPhoto:{
type: String,
required:false,
},
username:{
type: String,
required: true,
},
categories:{
type: Array,
required: false
},
}, {timestamps: true}
);
/ /exporting this schema
module.exports = mongoose.model("Post", PostSchema); //the module name is "Post"
where I used it in React.js for profile update
const handleUpdate = async (e) =>{
e.preventDefault();
dispatch({type: "UPDATE_START"})
const updatedUser = {
userId: user._id,
username,
email,
password
};
try{
const response = await axios.put("/users/" + user._id, updatedUser)
dispatch({type: "UPDATE_SUCCESS", payload: response.data})
setUpdated(true)
}catch(err){
}
}
Where I used it in React.js for post update
//this function handles the update of the post by the user
const handleUpdate = async () =>{
try{
await axios.put(`/posts/${post._id}`, {
username: user.username,
title:title,
description: description
});
// window.location.reload("/")
setUpdateMode(false)
}catch(err){
}
}

How to save data in mongodb express by referring another document

I have created the following User schema
const UserSchema = mongoose.Schema(
{
fullName: {
type: String,
required: true,
index: true,
},
department: {
type: mongoose.Schema.Types.ObjectId,
ref: "Department",
required: true,
},
},
});
and my Department schema looks like the following
const DepartmentSchema = mongoose.Schema(
{
name: {
type: String,
unique: true,
required: true,
},
description: {
desc: "Description.",
type: String,
},
},
{
strict: true,
versionKey: false,
timestamps: { createdAt: "createdAt", updatedAt: "updatedAt" },
});
Now I'm able to create departments and all is working well in Postman and also my Angular application. But while creating User it only saves the department ID that I've selected or added in my form. This is the data that I've tried to create
user = {
department: "5f806be7c9a3c02c7c61f9f1"
fullName: "John Joshua"
}
and my create route controller looks like this
exports.create = async (req, res) => {
const newUser = new User({
fullName: req.body.fullName,
department: req.body.department,
});
try {
const errors = validationResult(req); // I've used express validator here
if (!errors.isEmpty()) {
res.status(400).json({ errors: errors.array() });
return;
}
const savedUser = await newUser.save();
res.json(savedUser); // Here I was expecting to return the new saved user with Department details
} catch (err) {
res.status(500).json({ message: err });
}
};
So my response is all good and working well but department only contains the ID with nothing more.
How can I send a response data with Department details? Any help is appreciated.

How to add user's name as well as Id to users field in a chat?

I'm creating a web application that has chats, and users can join the chat. Once the user joins the chat, I want to add the user's ID as well as their name to the users field in the Chat schema. So far, I'm able to add their ID, but I am finding it difficult to add their name. Below, I have attached my Chat mongoose model, as well as my route to add a user to a chat. Also, I have attached my User mongoose model. Any help is greatly appreciated. Thank you!
Chat model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ChatSchema = new Schema({
title: {
type: String,
required: true
},
password: {
type: String,
required: true
},
creator: {
type: Schema.Types.ObjectId,
ref: 'user'
},
users: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
name: {
type: String,
required: true
}
}
],
code: {
type: String,
required: true
},
posts: [
{
text: {
type: String,
required: true
},
title: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Chat = mongoose.model('chat', ChatSchema);
route to add user to chat:
// #route Put api/chats
// #desc Add a user to a chat
// #access Private
router.put('/', [auth,
[
check(
'code',
'Please include the code for the chat')
.not()
.isEmpty(),
check(
'password',
'Please include the password for the chat'
).not()
.isEmpty()
]
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const chat = await Chat.findOne({ code: req.body.code });
//const user = await User.findOne({ user: req.user.id });
if (!chat) {
return res.status(400).json({ msg: 'Invalid Credentials' });
}
// Check if the chat has already been joined by the user
if (chat.users.filter(member => member.user.toString() === req.user.id).length > 0) {
return res.status(400).json({ msg: 'Chat already joined' });
}
//console.log(chat.password);
const isMatch = await bcrypt.compare(req.body.password, chat.password);
if (!isMatch) {
return res.status(400).json({ errors: [{ msg: 'Invalid Credentials' }] });
}
const newUser = {
user: req.user.id,
text: req.user.name
}
chat.users.unshift(newUser);
await chat.save();
res.json(chat.users);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
User model:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model('user', UserSchema);
in this part, it seems you are assigning the user's name to a text property, which I think it should be name not text.
const newUser = {
user: req.user.id,
text: req.user.name
}
The code should be:
const newUser = {
user: req.user.id,
name: req.user.name //Property should be name
}
I hope this works!

Handle schema references on mongoose

I'm trying to define a simple RESTful API using Node.js, mongoose and restify. The goal is to have users which can comment on profiles of others users. For this I have a comment endpoint that receives a text, the author and the target of the comment (other user).
I want to reference users so I defined next schemas:
User schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
"username": { type: String, unique: true, required: true },
"password": { type: String, required: true },
"comments": [{ type: Schema.Types.ObjectId, ref: 'Comment' }]
});
mongoose.model('User', UserSchema);
Comment schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CommentSchema = new Schema({
date: { type: Date, default: Date.now },
text: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
target: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }
});
mongoose.model('Comment', CommentSchema);
I also have this controller (just showing createComment function):
exports.createComment = function(req, res, next) {
var authorId, targetId;
User.findOne({ _id: req.params.authorId}, function(err, author) {
if (author) {
User.findOne({ _id: req.params.targetId}, function(err, target) {
if (target) {
var comment = new Comment();
comment.text = req.params.text;
comment.author = author._id;
comment.target = target._id;
comment.save(function(err, comment) {
if (err) {
res.status(500);
res.json({
type: false,
data: 'Error occurred: ' + err
});
} else {
res.json({
type: true,
data: comment
});
}
});
} else {
res.json({
type: false,
data: 'User ' + req.params.authorId + ' not found'
});
}
});
} else {
res.json({
type: false,
data: 'User ' + req.params.targetId + ' not found'
});
}
});
};
So, I have three questions:
Why do I need to check if the user received exists? I would like to receive only the id and store it but I have to do two more queries to check it myself.
What I have to do to store in User only comments where that user is the target? solved in the edited code
How can I simplify this code? Is a pain to have async queries executed in order. I would like to have generic errors and not to have to handle each one.
EDIT: I've simplified the code using validations on the schema:
Comment schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = mongoose.model('User');
var CommentSchema = new Schema({
date: { type: Date, default: Date.now },
text: { type: String, required: true },
author: { type: Schema.Types.ObjectId, ref: "User", required: true },
target: { type: Schema.Types.ObjectId, ref: "User", required: true }
});
CommentSchema.path('author').validate(function(value, respond) {
User.findOne({ _id: value}, function(err, user) {
respond(!err && user);
});
}, 'Author doesn\'t exists');
CommentSchema.path('target').validate(function(value, respond) {
User.findOne({ _id: value}, function(err, user) {
respond(!err && user);
});
}, 'Target user doesn\'t exists');
mongoose.model('Comment', CommentSchema);
Controller:
exports.createComment = function(req, res, next) {
var comment = new Comment(req.body);
comment.save(function(err, comment) {
if (err) {
res.status(500);
res.json({
type: false,
data: 'Error occurred: ' + err
});
} else {
User.findOne({ _id: comment.target }, function(err, user) {
user.comments.push(comment);
user.save();
});
res.json({
type: true,
data: comment
});
}
});
};
The problem with this is that now I have to use _id on queries (I would like to use a custom id) and I'm doing three queries every time I want save a comment (2 for validation and one more to store the comment). Is there a better way to to this?
You can use the select option of query in mongo to select only the _id field of the user, like this:
User.findOne({_id:req.params.authorId}).select({_id:1}).exec(function(err,user) {})
After you pull the userTarget from mongo, you need to add the comment._id to his list of comments, and save him:
target.comments.push(comment._id);
target.save(function(err, targetAfterSaved) {})
read about async or q, they are my favorites Libraries to handle with async functions. For handle with errors like you want, you can add some listeners - here is the documentation from restify site.
Hope you understand, if you need any help let me know

Resources