how to properly cast ObjectId in mongoose - javascript

I'm running into some issues with casting ObjectId in express.js using mongoose.
In my route i have tried both casting before as well as just using the req.params.id directly. Nothing seems to work. I'm 100% certain the id is correct. I have tried creating a new post and directly copying the id multiple times.
Any ideas why my ObjectId is not recognized?
My Schema:
let PostSchema = new Schema({
_id: {type: mongoose.Schema.Types.ObjectId, auto: true},
title: String,
author: String,
date: { type: Date, default: Date.now()},
body: String,
comments: [CommentSchema],
upvotes: Number,
downvotes: Number,
});
My route:
app.post('/api/post/:id/comment', (req, res) => {
let comment = new PostComment({
author: req.body.author,
body: req.body.body,
date: req.body.date,
upvotes: 0,
downvotes: 0,
})
const id = mongoose.ObjectId.cast(req.params.id)
Post.findOneAndUpdate(
{_id: id},
{ $push: {comments: comment}}
)
.then(result => {
if(!result) {
res.sendStatus(404).send({
success: 'false',
message: 'Comment not added',
});
} else {
res.status(200).json(result);
}
})
.catch(err => console.log(err));
});
The Error message:
Cast to ObjectId failed for value "{ id: \'5cc3632db9e2405960e3ed0e\' }" at path "_id" for model "Post"
Extra route with same issue:
// get single post by id
app.get("/api/post/:id", (req, res) => {
const id = req.params;
Post.findById(id)
.exec()
.then(result => {
if(!result) {
res.sendStatus(404).send({
success: 'false',
message: 'No post found',
});
} else {
res.status(200).json(result);
}
})
.catch(err => console.log(err));
});

It seems as soon as you post on SO you will find the answer yourself, so here goes.
This function will effectively cast a string to an ObjectId: mongoose.Types.ObjectId(req.params.id);

Related

Multiple async queries in nodejs (mongoose)

I am a nodejs newbie. I have two simple models, User and Story. Here is what I want to do:
I want to retrieve all stories that have {status:"public"} and store it in an array called retrievedStories.
Then for each story I want to use its "user" field (which contains the object id of the user) to lookup the name of the user from User
Then add a new key in each element of retrievedStories called authorName with the name of the user.
Here are the models:
const UserSchema = new mongoose.Schema({
googleId: {
type: String,
required: true
},
displayName: {
type: String,
required: true
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
image: {
type: String,
},
createdAt: {
type:Date,
default: Date.now()
}
})
const StorySchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true
},
body: {
type: String,
required: true
},
status: {
type: String,
default: 'public',
enum: ['public', 'private']
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
createdAt: {
type:Date,
default: Date.now()
}
})
And here is what I tried, but doesn't work. The stories are retrieved but the authorName is not added. Any help (possibly a better way to do this?) will be highly appreciated!
router.get('/',async (req,res)=>{
try {
const retrievedStories = await Story.find(
{status: "public"}
)
await Promise.all(retrievedStories.map(async (story) =>{
const author = await User.findById(story.user)
story.authorName = author.displayName
}))
return res.json(retrievedStories)
} catch (error) {
console.log(error)
}
})
You can simplify your query by using populate to retrieve User's data:
router.get('/', async (req, res) => {
try {
const retrievedStories = await Story.find({ status: 'public' })
.populate('user')
.exec();
return res.json(retrievedStories);
} catch (error) {
console.log(error);
}
});
You can then access User's displayName data on each Story by accessing story.user.displayName.
For more information on query population see the official docs.

Mongoose is not creating a collection on save() function

I have below schema.
const mongoose = require('mongoose');
const JobSchema = new mongoose.Schema({
posterId: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
location: {
type: String,
required: true,
},
}, { timestamp: true });
module.exports = mongoose.model('Job', JobSchema);
And I use below code to save new data to this collection.
const Job = require('../../models/Job');
new Job({
posterId,
title,
location,
})
.save()
.then(job => {
console.log('JOB: ', job) // consoles the newly added job to collection.
if (job) {
response.json({
message: 'Job is saved successfully.',
});
} else {
response.json({
message: 'Job can not be saved at this time. Please try again later.',
});
}
})
.catch(error => {
response.json(error);
});
The above code successfully saves new information to job collection but when I check that in mongoDB using mongodb CLI. I do not see any job collection there so it is not creating the collection. What am I doing wrong and how can I fix this?

findOneandReplace keeps giving error: "Error: The replacement document must not contain atomic operators."?

I am currently developing a Pokemon Team Builder app with a React frontend and an Express backend with MongoDB for the database.
As far as I can tell my TeamSchema has no such atomic operators? Here is my TeamSchema:
const mongoose = require('mongoose');
const TeamSchema = new mongoose.Schema({
name: {
type: 'String',
required: true,
unique: true,
},
team: [
{
name: { type: String },
types: [{ type: String }],
sprite: { type: String },
},
],
username: {
type: String,
required: true,
},
userId: {
type: String,
required: true,
},
});
const TeamModel = mongoose.model('Team', TeamSchema);
module.exports = TeamModel;
And the error gets thrown in this method when I attempt to call the findOneAndReplace method by finding a team that has a name and userId match.
const replaceTeam = async (req, res) => {
const { teamName: name, filteredTeam: team } = req.body;
const { username, _id: userId } = req.user;
const newTeam = new Team({ name, team, username, userId });
try {
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam);
console.log(replacedTeam);
res.status(200).json({ message: 'Team was successfully overwritten!' });
} catch (err) {
console.log(err);
res.status(500).json({ message: 'An error occurred while updating the team.' });
}
};
This has been a real headscratcher here and I am not sure what is going wrong here. I have only started using mongoose a couple of weeks ago, so I wonder if it's something fundamental I am misunderstanding here.
The Mongoose function findOneAndReplace expects a document object passed in. See the below code.
details.findOneAndReplace(
{ location: "New York" },
{ name: "Sunny", age: 292, location: "Detroit" },
function(err, result) {
if (err) {
res.send(err);
} else {
res.send(result);
}
}
);
Change
const newTeam = new Team({ name, team, username, userId })
to
const newTeam = {name, team, username, userId}
Also as in the other poster's code, add the new: true option to the call as follows by changing
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam);
to
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam, { new: true });
otherwise the original document will be returned into replacedTeam
You can just use findOneAndUpdate and update all the fields with new data. You can do it like this:
const replaceTeam = async (req, res) => {
const { teamName: name, filteredTeam: team } = req.body;
const { username, _id: userId } = req.user;
try {
const replacedTeam = await Team.findOneAndUpdate({ name, userId }, { name, team, username, userId }, {new: true});
console.log(replacedTeam);
res.status(200).json({ message: 'Team was successfully overwritten!' });
} catch (err) {
console.log(err);
res.status(500).json({ message: 'An error occurred while updating the team.' });
}
};

How can I solve this referencing Problem in mongoose/Node JS

I have route and model for User and then another for Loan. I'm trying to reference the user inside the Loan route but I get this error anytime I test on PostMan:
TypeError: Cannot read property '_id' of undefined
at C:\Users\Micho\Documents\GBENGA\BE\src\routes\loans\index.js:38:47
Loan Model code is:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const loanSchema = new Schema({
customerName: {
type: String,
required: true
},
gender: {
type: String
},
address: {
city: String,
state: String,
},
amount: {
type: Number
},
loanTenure: {
type: Number
},
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
loanStatus: {
type: String,
default: "created"
}
}, {
timestamps: true
})
My route is this:
router.post("/", async (req, res) => {
try {
let loan = await new Loan({...req.body});
loan.save();
await User.findByIdAndUpdate(req.user._id, { $push: { loans: loan._id } })
console.log(req.user)
loan = await Loan.findById(loan._id).populate("user");
res.send(loan);
} catch (error) {
console.log(error)
res.status(500).send(error);
}
});
Kindly help. Thanks

Mongoose Object returns undefined when populating

I'm struggling with a little kinda of problem. What I wanna do is populating users in comments.
User schema:
const userSchema = mongoose.Schema({
username: {
type: String,
required: true,
},
password: {
type: String,
required: true
}
});
Comment schema:
const commentSchema = mongoose.Schema({
comment:{
type: String
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
});
I had already created user and comment. Everything is fine when I'm trying to find both objects.
Comment:
Comment.find({}).exec((err, comments) => {
console.log(comments);
});
Output:
[
{
_id: 5e62472d5f593f3c642ee1e5,
comment: 'something',
user: 5e624522366d8c4150278a64,
__v: 0
}
]
User:
User.find({}).exec((err, users) => {
console.log(users);
});
Output:
[
{
_id: 5e624522366d8c4150278a64,
username: "SomeBodY",
password: "$2a$10$nm5BJ7zeI1tet3UEzcakf.8xoTgV/Yti5l1EKNg5inxiehevUlGRm"
}
]
The problem is when I'm using .populate('user') to Comment model it returns the comment as undefined in the console. I've tried different methods and even dropping the database but without success.
Here's the route when this happens
// Route To Single Project
router.get('/:projectId', (req, res) => {
const requestedProjectId = req.params.projectId;
Project.findById({_id: requestedProjectId}).populate('image_file').exec((err, project) => {
Rating.find({projectId: requestedProjectId}, (err, ratings) => {
Rating.countDocuments({projectId: requestedProjectId}, (err, count) => {
Comment.find({projectId: requestedProjectId}).populate('user').exec((err, comments) => {
console.log(comments)
if(err) return next(err);
res.render('project', { ... });
});
});
});
});
});
Actually your populate code is true.
The reason to get empty comments is because this Comment.find({projectId: requestedProjectId}) seems to return empty. So just check your request param.
Also to get rid of callback hell, you can rewrite your route using async/await like this.
router.get("/:projectId", async (req, res) => {
const requestedProjectId = req.params.projectId;
try {
const project = await Project.findById({ _id: requestedProjectId }).populate("image_file");
if (!project) {
return res.status(400).send("Project not found, check your projectId");
}
const comments = await Comment.find({ projectId: requestedProjectId }).populate("user");
console.log(comments);
const ratings = await Rating.find({ projectId: requestedProjectId });
const count = await Rating.countDocuments({ projectId: requestedProjectId });
res.render("project", {
project,
comments,
ratings,
count
});
} catch (err) {
console.log("Error: ", err);
res.status(500).send("Something went wrong");
}
});

Categories