I hope someone can help me with this issue. I am trying to populate a subSchema but I have no luck.
I have a replySchema, commentSchema and a UserSchema.
replySchema is subDocument to commentSchema and I want to populate both of them with user's details from userSchema
My issue is that with the code below it does not populate my replySchema:
REPLY-SCHEMA and COMMENT-SCHEMA
const replySchema = new mongoose.Schema(
{
replyComment: {
type: String,
required: [true, 'Reply comment cannot be empty'],
},
createdAt: {
type: Date,
default: Date.now(),
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [true, 'Comment must belong to a user'],
},
},
{
timestamps: true,
}
);
const commentSchema = new mongoose.Schema(
{
comment: {
type: String,
required: [true, 'Comment cannot be empty'],
},
createdAt: {
type: Date,
default: Date.now(),
},
// createdAt: Number,
// updatedAt: Number,
post: {
type: mongoose.Schema.ObjectId,
ref: 'Post',
required: [true, 'Comment must belong to a Post'],
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [true, 'Comment must belong to a user'],
},
replies: [replySchema], //replySchema sub-document - is this the right way?
},
{
// timestamps: { currentTime: () => Math.floor(Date.now() / 1000) },
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
replySchema.pre(/^find/, function (next) {
this.populate({
path: 'user',
select: 'name role avatar',
});
next();
});
commentSchema.pre(/^find/, function (next) {
this.populate({
path: 'user',
select: 'name role avatar',
});
next();
});
const Comment = mongoose.model('Comment', commentSchema);
module.exports = Comment;
USER-SCHEMA
const userSchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
required: [true, 'Please insert your name'],
},
avatar: {
type: Object,
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
const User = mongoose.model('User', userSchema);
module.exports = User;
Thank you very much for your time!
To populate user's data in replies:
this.populate([
'user', // To populate commentSchema's user field
{
path: 'replies',
populate: 'user'
} // To populate replySchema's user field
]);
Edit:
To populate specific fields:
this.populate([
{
path: 'user',
select: 'name role avatar'
},
{
path: 'replies',
populate: {
path: 'user',
select: 'name role avatar'
}
}
]);
Related
I have three collections which are User, Profile and Userpost and all referenced accordingly. The challenge I am facing is that when I use the .populate(), instead of fetching the Profile information of the logged in user, it fetches the data of the first profile on the profile collections and it does so for any user that is logged in. Kindly help me resolve. Thanks
How I populate
router.get('/getpost/:id', (req, res) => {
const id = req.params.id;
Userpost.find({User:id}).populate('Profile').populate('User', {password: 0}).exec((err,docs) => {
if(err) throw(err);
res.json(docs);
})
});
UserpostSchema
const UserpostSchema = new Schema({
post: {
type: String,
required: true
},
User: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
required: true,
},
Profile: {
type: mongoose.Schema.Types.ObjectId,
ref: 'profile',
required: true,
}
});
const Userpost = mongoose.model('userpost', UserpostSchema);
module.exports = Userpost;
Profile
const ProfileSchema = new Schema({
lastname: {
type: String,
required: true,
},
firstname: {
type: String,
required: true,
},
othernames: {
type: String,
required: true,
},
countries: {
type: String,
required: true,
},
phones: {
type: String,
required: true,
},
User: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
required: true,
},
});
const Profile = mongoose.model('profile', ProfileSchema);
module.exports = Profile;
and User
const userSchema = new Schema({
username: {
type: String,
required: true
},
roles: {
User: {
type: Number,
default: 2001
},
Mentor: Number,
Admin: Number
},
password: {
type: String,
required: true
},
userID: {
type: String,
required: true
},
refreshToken: String
});
const User = mongoose.model('user', userSchema);
module.exports = User;
I have 2 schemas User and Restaurant, and user has an array of restaurants, am trying to reach when deleting the restaurant delete its reference from user automatically, am trying to reach it with the model.pre('remove')..but when I delete a restaurant the reference id it still exist in User.
Here is my User Schema:
const userSchema = new Schema(
{
email: {
type: String,
trim: true,
// required: true,
unique: true,
},
password: {
type: String,
// required: true,
min: 5,
},
stripeCustomerId: {
type: String,
// unique: true,
},
linkedAffiliateUser: {
type: String, //mongoose.Schema.Types.ObjectId,
},
restaurants: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Restaurant",
},
],
role: {
roleId: { type: Number, minlength: 1, maxlength: 1, required: true },
roleName: { type: String, trim: true, required: true },
},
// seperated schema
},
{ timestamps: true }
);
export default mongoose.model("User", userSchema);
and here is my Restaurant Schema:
const restaurantSchema = new Schema({
restaurantOwner: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
Name: {
type: String,
trim: true,
},
server: { type: mongoose.Schema.Types.ObjectId, ref: "ServerUser" },
restaurantLinkAccess: {
type: String,
required: true,
trim: true,
unique: true,
},
});
restaurantSchema.pre("remove", function (next) {
this.model("User")
.update(
{ restaurantSchema: this },
{ $pull: { comments: this._id } },
{ multi: true }
)
.exec(next);
});
export default mongoose.model("Restaurant", restaurantSchema);
I also tried to solve it like this:
restaurantSchema.pre("remove", function (next) {
this.model("User").remove({ $pull: { restaurants: this._id } }, next);
});
Please help.
here's the booking schema
const bookingSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
routeId:{
type: mongoose.Schema.Types.ObjectId,
ref: "Route",
required: true,
}
})
In this table the routeId(route schema) contains the Bus table(referenced).
const routeSchema = new mongoose.Schema({
location:{
type: mongoose.Schema.Types.ObjectId,
ref: 'Location',
required: true
},
duration: {
type: Number,
required: true
},
Bus:{
type: mongoose.Schema.Types.ObjectId,
ref:"Bus",
required: true
},
date: {
type:String,
required: true
},
},
{
timestamps: true,
});
and then finally the bus model
const busSchema = new mongoose.Schema(
{
busNumber: {
type: String,
unique: true,
required: true,
},
seats: {
type: Number,
},
},
{
timestamps: true,
}
);
Now I want to show only bus number and not whole bus data. this is the response I am getting. https://i.stack.imgur.com/S1qyZ.png
this is the query that I applied.
router.get("/bus/myBooking/one/:id", auth, async (req, res) => {
const _id = req.params.id;
const myBooking = await Booking.findById({ _id })
.populate({path: 'routeId', populate: 'Bus'})
.select(["-_id","-userId","-createdAt","-updatedAt","-__v","-passengers._id","-departureDetails._id","-arrivalDetails._id"]);
try {
return !myBooking
? res.status(404).send("No booking found")
: res.status(200).send(myBooking);
} catch (e) {
res.status(500).send();
}
});
This method works on some versions of mongoose, So you can try this:
await Booking.findById({ _id })
.populate({
path: 'routeId',
populate: {
path: 'Bus',
model: Bus,
select:"-_id busNumber"
}
})
I'm having a issue with a mongoose find() query, which I cannot figure out. the error I receive is "TypeError: Cannot read property 'find' of undefined" which I suspect is an export/import problem. Any help would be greatly appreciated.
here is my scheme model file:
const mongoose = require('mongoose');
const RoleSchema = new mongoose.Schema({
pageGroup: {
type: String,
required: true,
},
level: {
type: String,
required: true,
}
})
const OfficeSchema = new mongoose.Schema({
officeId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Office",
required: true,
},
roleId: {
type: [mongoose.Schema.Types.ObjectId],
required: false,
},
})
const InstanceSchema = new mongoose.Schema({
instanceId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Instance",
required: true,
},
offices: {
type: [OfficeSchema],
required: false,
},
})
const UserSchema = new mongoose.Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
email: {
type: String,
required: false
},
password: {
type: String,
required: false
},
access: {
type: [InstanceSchema],
required: false,
},
permissions: {
type: [RoleSchema],
required: false,
},
activationToken: {
type: String,
required: false,
},
roleId: { // new
type: mongoose.Schema.Types.ObjectId,
// index: true,
ref: 'Role',
// default: null
},
employeeId: {
type: String,
required: false
},
instanceId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Instance',
required: true
},
officeId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Office',
required: true
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
})
UserSchema.virtual('instances', {
ref: 'Instance',
localField: 'access.instanceId',
foreignField: '_id',
});
UserSchema.virtual('instances.offices', {
ref: 'Office',
localField: 'access.offices.officeId',
foreignField: '_id',
});
UserSchema.virtual('office', {
ref: 'Office',
localField: 'officeId',
foreignField: '_id',
justOne: true,
});
UserSchema.virtual('name').get(function() {
return this.firstName + " " + this.lastName
});
const User = mongoose.model('User', UserSchema);
module.exports = { User }
here is my function in my controller file:
const { User } = require('./user.model');
async getEmployees(){
const employees = await User.find({
instanceId: this._id,
}, '-password -activationToken -__v -activated')
.populate('office')
.sort([['firstName', 1]])
.exec()
return employees
},
The error points to User being undefined, which can happen when your project has cyclic dependencies (where file A.js depends on file B.js, which in turn depends on file A.js again, either directly or indirectly through another file).
A quick fix is to delay loading the User model until the moment it's actually needed, by moving the require() into getEmployees():
async getEmployees(){
const { User } = require('./user.model');
const employees = await User.find({
instanceId: this._id,
}, '-password -activationToken -__v -activated')
.populate('office')
.sort([['firstName', 1]])
.exec()
return employees
}
But ideally, you should get rid of the cyclic dependency altogether.
I had the same problem on my project. You can fix it by replacing
const { User } = require('./user.model');
of your controller by :
const User = require('./user.model');
Just by removing the brackets made it for me. So I guess you should use destructuring.
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