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

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

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.

CastError: Cast to [undefined] failed for value "[]" (type string) at path "comments.undefined"

I'm quite new to node and mongoose. I'm trying to do a project using them, but i'm running into an error while trying to populate. The comment is saved to the Comment schema perfectly, but throws an error when i reference it Organization Schema.Please advise me on what i'm doing wrong. Any form of assistance will be appreciated.
// Post route for comment(on the Organization's profile page)
router.post('/comment/:id', ensureAuthenticated,(req, res) =>{
let id = req.params.id;
console.log(mongoose.Types.ObjectId.isValid(id))
const commentObject = new Comment({
sender: 'Fred kimani',
commentBody: req.body.commentBody
})
console.log(commentObject);
commentObject.save((err, result) =>{
if(err){console.log(err)}
else{
Organization.findByIdAndUpdate(id, {$push: {comments: result}}, {upsert: true}, (err, organization) =>{
if(err){console.log(err)}
else{
console.log('======Comments====')
}
})
res.redirect('/users/organizationprofilepage/:id')
}
})
});
//Organization Schema
const mongoose = require('mongoose');
const OrganizationSchema = new mongoose.Schema({
organization_name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
category: {
type: String,
required: true
},
isApproved: {
type: Boolean,
default: false
},
image:{
type:String,
required:true
},
description: {
type: String,
required: true,
},
comments: [{
type: mongoose.Types.ObjectId,
ref: 'Comment'
}],
},
//{ typeKey: '$type' }
);
OrganizationSchema.statics.getOrganizations = async function () {
try {
const organizations = await this.find();
return organizations;
} catch (error) {
throw error;
}
}
//defines the layout of the db schema
const Organization = mongoose.model('0rganization', OrganizationSchema);
module.exports = Organization;
//Comment schema
const mongoose = require('mongoose');
const CommentSchema = mongoose.Schema({
sender: {
type: String,
},
commentBody: {
type: String,
required: false,
},
date: {
type: Date,
default: Date.now
},
})
CommentSchema.statics.getComments= async function () {
try {
const comments = await this.find();
return comments ;
} catch (error) {
throw error;
}
}
const Comment= mongoose.model('Comment', CommentSchema);
module.exports = Comment;
Try to change the comments type to mongoose.Schema.Types.ObjectId:
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment',
},
],
Try to push the new commend _id into the Organization object after its creation, not the whole object:
commentObject.save((err, result) => {
if (err) {
console.log(err);
} else {
Organization.findByIdAndUpdate(
id,
{ $push: { comments: result._id } }, // <- Change this line
{ upsert: true },
(err, organization) => { }
);
...
}
});
If you just updated the schema you will need to make sure all of the comments are following the new form you created, when you save it will attempt to validate them, that is why an updateOne will work but not await save()

How can I use function getPublicFields with populate mongoose?

Here is the function getPublicFields in User Schema
User Schema
UserSchema.methods.getPublicFields = function () {
var returnObject = {
firstName: this.firstName,
lastName: this.lastName,
email: this.email,
_id: this._id,
};
return returnObject;
};
here just I connect the User Schema with the product and they gave me all the user Data watch I don't want
productController.Js
exports.getProducts = async (req, res, next) => {
try {
const products = await Product.find().populate("owner");
res.status(200).send(products);
} catch (e) {
next(e);
}
};
Product Schema
var mongoose = require("mongoose");
var { Schema } = mongoose;
const ProductSchema = new Schema({
title: {
type: String,
},
category: {
type: String,
},
price: {
type: Number,
},
completed: {
type: Boolean,
default: false,
},
owner: {
ref: "User",
type: mongoose.Schema.Types.ObjectId
},
img: {
type : Array,
}
});
module.exports = mongoose.model("Product", ProductSchema);
populate will give you a plain object, not a Mongoose instance. What you can do is construct a User instance from it:
const user = new User(product.owner);
product.owner = user.getPublicFields();

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

Relationship mongoose

I'm have two schemas where one depends of the other to save.
const OrderSchema = new moongose.Schema({
product: {
type: moongose.Schema.Types.ObjectId,
ref: 'Product',
required: true
},
quantity: {
type: Number,
required: true,
default: 1,
},
total_price: {
type: Number,
}
})
OrderSchema.pre('save', async function(next) {
this.total_price = product.price * quantity
next()
})
const Order = moongose.model('Order', OrderSchema)
And the other:
const ProductSchema = new moongose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String
},
photo: {
data: Buffer,
contentType: String
}
})
const Product = moongose.model('Product', ProductSchema)
But when I try save one Order with one Product exiting in data base:
{
"product":"5cae6ff5d478882ed8725911",
"quantity":3
}
Show error: Error: ReferenceError: product is not defined
This is my controller to save a new Order:
router.post('/register', async (req, res) => {
try {
const order = await Order.create(req.body)
return res.send({ order })
}
catch (error) {
console.error('Error:', error)
}
})
I usually use
    idproduct: {
         type: moongose.Schema.ObjectId,
         required: true
     },
This way the post works correctly
LOL, I found the ERROR:
OrderSchema.pre('save', async function(next) {
this.total_price = product.price * quantity
next()
})
I forgot to use 'THIS', correct:
OrderSchema.pre('save', async function(next) {
this.total_price = this.product.price * this.quantity
next()
})
Hehehe, sorry guys...

Categories