How to pass an ObjectId into a POST request? - node.js

I am trying to make a One-To-Many relationship between two tables(Group and Movement tables) using node js (Express) and mongo DB. I already have a group Id coming from the Group table on my side, my question is, how can I save a movement( see point 3 ) with that group Id I have. I tried passing groupId: req.body.group._id and
groupId: req.body.group but I am never able to populate that variable
This are the two entities I've created:
1) GROUP ENTITY
const mongoose = require("mongoose")
const GroupSchema = mongoose.Schema({
name: {
type: String,
required: true
},
limit: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
movement: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Movement' }],
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
})
module.exports = mongoose.model("Group", GroupSchema)
2) MOVEMENT ENTITY
const mongoose = require("mongoose")
const MovementSchema = mongoose.Schema({
description: {
type: String,
required: true
},
value: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
group: { type: mongoose.Schema.Types.ObjectId, ref: 'Group' }
})
module.exports = mongoose.model("Movement", MovementSchema)
This is my movement router where I make the endpoints (Actual problem is here)
3) MovementRoute
const router = require('express').Router();
const verify = require('./verifyToken');
const User = require('../model/User');
const Group = require('../model/Group');
const Movement = require('../model/Movement');
// Create Movement
router.post('/', verify, async (req, res) => {
const post = new Movement({
description: req.body.description,
value: req.body.value,
groupId: req.body.group._id //**tried this**
});
try {
const savedMovement = await post.save()
res.status(200).send(res.json({ data: savedMovement }));
} catch (error) {
res.status(400).send(res.json({ message: error }));
}
});
module.exports = router;
Request sent
{
"description":"group1",
"value":"233",
"group":"5e506f3c56233d08f79bc8f3"
}

If console.log(req.body) gives you this:
{
description: 'group1',
value: '233',
group: '5e506f3c56233d08f79bc8f3'
}
..you should be able to do this:
router.post('/', verify, async (req, res) => {
console.log(req.body) // --> { description: 'group1', value: '233', group: '5e506f3c56233d08f79bc8f3' }
const post = new Movement({
description: req.body.description,
value: req.body.value,
groupId: req.body.group
// groupId: req.body.group._id //**tried this**
});
try {
const savedMovement = await post.save()
res.status(200).send(res.json({ data: savedMovement }));
} catch (error) {
res.status(400).send(res.json({ message: error }));
}
});

Related

Mongoose - Selecting and Sending Virtuals from Populated Fields

I am having a little trouble getting my mongoose virtuals to show up from deep populated fields. Here is the code of the backend function that is not behaving as I'd like it to:
exports.get_user_feed = async (req, res, next) => {
const options = { sort: { date: -1 } };
const user = await User.find(
{ username: req.params.user },
"username posts avatar followers following"
)
.populate({
path: "posts",
options,
populate: [
{
path: "author",
},
{ path: "comments", populate: { path: "author" } },
],
})
.sort({ "posts.date": 1 });
res.json({ ...user });
};
And here is the comment schema:
const mongoose = require("mongoose");
const { DateTime } = require("luxon");
const Schema = mongoose.Schema;
const CommentSchema = new Schema({
targetPost: { type: Schema.Types.ObjectId, ref: "Post", required: true },
author: { type: Schema.Types.ObjectId, ref: "User", required: true },
date: { type: Date, required: true },
content: { type: String, maxlength: 400 },
comments: [{ type: Schema.Types.ObjectId, ref: "Comment" }],
stars: [{ type: Schema.Types.ObjectId, ref: "User" }],
});
// Virtual for post's URL
CommentSchema.virtual("url").get(function () {
return "/" + this.targetPost.url + this._id;
});
// Virtual for formatted date.
CommentSchema.virtual("formatted_date").get(function () {
return (
DateTime.fromJSDate(this.date).toLocaleString(DateTime.DATE_MED) +
" at " +
DateTime.fromJSDate(this.date).toLocaleString(DateTime.TIME_SIMPLE)
);
});
//Export model
module.exports = mongoose.model("Comment", CommentSchema);
My goal is to get the comments from each post to also include the formatted_date of the comment, but this virtual is not getting included in the response that is sent - all the regular properties are being sent but not the virtual. Any help here would be appreciated.
Add this code in your Commnet Schema file before module.exports.
CommentSchema.method('toJSON', function () {
const {
...object
} = this.toObject({ virtuals:true });
return object;
});

Router.post an array of Object ID'S using Mongoose and NodeJs router.post

I almost have the desired functionality but it's not exactly what I wanted to approach.
I have two model schema, Control and SubControl. The SubControl is referenced in the Control model. I want to post the control model + a reference of the SubControl.
My post method:
router.post(
'/add',
auth,
role.checkRole(role.ROLES.Admin, role.ROLES.Regulator),
async (req, res) => {
try {
const subControl = new SubControl({...req.body});
const subControlDoc = await subControl.save();
const control = new Control({...req.body});
control.subControl.push(subControlDoc._id);
const savedControl = await control.save();
res.status(200).json({
success: true,
message: `Control has been added successfully!`,
control: savedControl
});
} catch (error) {
return res.status(400).json({
error
// error: 'Your request could not be processed. Please try again.'
});
}
}
);
My Control Schema:
const ControlSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
mainControl: {
type: String
},
subControl: [{
subControlNo: {type: Mongoose.Schema.Types.String, ref: 'SubControl'}
}],
controlDescription: {
type: String,
trim: true
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('Control', ControlSchema);
My SubControl schema:
const SubControlSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
subControlNo: {
type: String
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('SubControl', SubControlSchema);
Postman:
{
"mainControl": "nn",
"controlDescription": "controldescription",
"subControl":
[
{
"subControlNo": "1-2"
},
{
"subControlNo": "1-2-1"
}
]
}
Result I'm getting:
Question: Why am I getting 3 object id's although I inserted 2 and why only the last object ID is saved in my SubControl database? I this the way to add array of object id's or not?

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

I am getting this error: Cannot read property 'likes' of null

I am getting error on console that : Cannot read property 'likes' of null
I am using postman for getting requests and putting response and response.
The array 'likes' is empty and here I am trying to insert the user id inside it but unable to insert it through unshift() method.
This is schema defined in a file Posts.js
const { text } = require('express');
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String
},
avatar: {
type: String
},
likes: [
{
users: {
type: Schema.Types.ObjectId,
ref: 'users'
}
}
],
comment: [
{
users: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String,
},
avatar: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Post = mongoose.model('post', PostSchema);
This is a express code for put request in file posts.js
const express = require('express');
const router = express.Router();
const { check, validationResult } = require('express-validator/check');
const auth = require('../../middleware/auth');
const Posts = require('../../models/Posts');
const User = require('../../models/User');
const { route } = require('./profile');
router.put('/like/:id', auth, async(req, res) => {
try {
const post = await Post.findById(req.params.id);
// Check if the post has already been liked
if(post.likes.filter(like => like.user.toString() === req.user.id).length > 0) {
return res.status(400).json({ msg: 'Post already liked' });
}
post.likes.unshift({ user: req.user.id });
await post.save();
res.json(post.likes);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
Here is the typo error I made. In the schema Posts.js in likes and comment array I wrote users instead of user.
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String
},
avatar: {
type: String
},
likes: [
{
users: { // Here it has to be user
type: Schema.Types.ObjectId,
ref: 'users'
}
}
],
comment: [
{
users: { //Here it has to be user
type: Schema.Types.ObjectId,
ref: 'users'
},

Unable to populate nested schema reference in Mongoose

I have multiple Schemas and I'm trying to query the data using populate. I'm using mongoose(5.9.7) and express js.
First Schema - ProfileSchema
const ProfileSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "User"
},
jobs: [
{
type: Schema.Types.ObjectId,
ref: "Job"
}
],
resumes: [
{
type: Schema.Types.ObjectId,
ref: "Resume"
}
],
name: {
type: String,
required: true
},
});
module.exports = Profile = mongoose.model("Profile", ProfileSchema);
Second Schema - Resume Schema
const ResumeSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "User"
},
fileLink: { type: String, required: true },
fileName: { type: String, required: true },
description: { type: String, required: true }
});
module.exports = Resume = mongoose.model("Resume", ResumeSchema);
Route
router.get(
"/",
passport.authenticate("jwt", { session: false }),
async (req, res) => {
try {
const profile = await Profile.findOne({
user: req.user.id
})
.populate("resumes")
.exec();
res.json(profile);
} catch (error) {
return res.status(400).json(error);
}
}
);
This returns an empty array for both resume and jobs. I have tried different variations of populate but it doesn't work. Tried deeppopulate as well. Though User gets populated fine.
EDIT: Adding resume upload route
Route
router.post(
"/upload",
passport.authenticate("jwt", { session: false }),
upload.single("resume"),
async (req, res) => {
try {
// upload to s3 code here
if (data) {
const resumeData = {
description: req.body.description,
fileName: params.Key,
fileLink: data.Location,
user: req.user.id
};
const resume = new Resume(resumeData);
const fileSavedToSchema = await resume.save();
return res.json(fileSavedToSchema);
}
} catch (error) {
console.log(error);
}
}
);
I'm able to view the resumes using a different route.

Resources