mongoose .populate not working - node.js

I'm using nodejs/express and mongoose. However, when trying to use .populate(...) on a document it doesn't work. Here is my code:
I have this schema for a User:
const UserSchema = new Schema({
...
friends: [{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
...
}],
sentRequests: [{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
...
}],
recievedRequests: [{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
...
}]
}
mongoose.model('users', UserSchema);
Then when I try to show all the recievedRequests to a page I use this code:
router.get('/friendRequests', (req, res) => {
User.findOne({
_id: req.user.id // where req.user is the currently logged in user
})
.populate('recievedRequests.user')
.then(curUser => {
res.render('friends/friendRequests', {
curUser: curUser
});
})
});
where User is the model and req.user.id is the id of the currently logged in user. However, in the webpage whenever I reference curUser.recievedRequests.user it simply returns the id of the user and not the actual user object. Does anyone know what I might be doing wrong here?
Thanks.
EDIT - Example
For a document:
user1 = {
_id: ObjectId("5ae94b0b2bb9383d4029d64b"),
...
recievedRequests: [
{"_id" : ObjectId("5ae94b5f29c86c4a343d2d0a") }
]
}
and
user2 = {
_id: ObjectId("5ae94b5f29c86c4a343d2d0a"),
...
}
Using my above code on this document:
router.get('/friendRequests', (req, res) => {
User.findOne({
_id: user1.id
})
.populate('recievedRequests.user')
.then(curUser => {
res.render('friends/friendRequests', {
curUser: curUser
});
})
});
results in curUser.recievedRequests = [{ _id : 5ae94b5f29c86c4a343d2d0a }]

Related

How to delete comment that is nested in Post schema with mongoose and nodejs?

I want to be able to delete comment that is inside my Post model.
This is my Schema for Post model:
const PostSchema = new Schema({
userID: {
type: Schema.Types.ObjectId,
ref: 'user'
},
content: {
type: String,
required: true
},
registration_date: {
type: Date,
default: Date.now
},
likes: [
{
type: Schema.Types.ObjectId,
ref: "user"
}
],
comments: [
{
text: String,
userID: {
type: Schema.Types.ObjectId,
ref: 'user'
}
}
]
})
And I have this route:
router.delete('/comment/:id/:comment_id', auth, async (req, res) => {
const postId = req.params.id
const commentId = req.params.comment_id
}
comments in post looks like this:
comments: [
{
_id: 5f1df4cf5fd7d83ec0a8afd8,
text: 'comment 1',
userID: 5efb2296ca33ba3d981398ff
},
{
_id: 5f1df4d35fd7d83ec0a8afd9,
text: 'commnet 2',
userID: 5efb2296ca33ba3d981398ff
}
]
I want to delete comment, and don't know how to do it. Does anyone have idea how to do it?
First we find the post by findByIdAndUpdate then we delete the comment using $pull from the array of comments.
router.delete("/comment/:id/:comment_/id", async function (req, res) {
try {
const post = await Post.findByIdAndUpdate(
req.params.id,
{
$pull: { comments: {_id:req.params.comment_id}},
},
{ new: true }
);
if (!post) {
return res.status(400).send("Post not found");
}
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});

Accessing a schema inside a schema using Express Router and MongoDG

I'm trying to create a route where it takes in a parameter for a username and then displays that users information. Only thing is, the username is in the user schema from when the user signs up. The profile schema references the user schema. How do I use the username parameter in the findOne call to display the users profile data?
User schema:
const UserSchema = new Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model("users", UserSchema);
Profile schema:
const ProfileSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
name: {
type: String
},
image: {
type: String
},
bio: {
type: String
},
location: {
type: String
},
website: {
type: String
},
social: {
youtube: {
type: String
},
facebook: {
type: String
},
instagram: {
type: String
},
twitter: {
type: String
}
}
});
module.exports = User = mongoose.model("profile", ProfileSchema);
Route:
router.get("/user/:username", (req, res) => {
const errors = {};
Profile.findOne({ user: req.params.user.username })
.populate("user", "username")
.then(profile => {
if (!profile) {
errors.noprofile = "There is no profile for this user";
return res.status(404).json(errors);
}
res.json(profile);
})
.catch(err => res.status(404).json(err));
});
Please try this :
router.get("/user/:username", async (req, res) => {
const errors = {};
try {
const profile = await User.aggregate([
{ $match: { username: req.params.username } },
{ $lookup: { from: "profile", localField: "_id", foreignField: "user", as: "userProfile" } },
{ $project: { userProfile: { $arrayElemAt: ["$userProfile", 0] }, username: 1, _id:0 } }
]).exec();
if (!profile.length) {
errors.noprofile = "There is no profile for this user";
return res.status(404).json(errors);
}
res.json(profile[0]);
} catch (error) {
console.log('Error in retrieving user from DB ::', error);
return res.status(404);
}
})
Try using aggregate, firstly you check-in user table for getting details of a specific username then fetch the profile details as below using lookup, if no profile found after unwind the document will not be fetched and you can check on aggregate result's length as aggregate always return an array in result :
User.aggregate([
{$match:{ username: req.params.user.username }},
{$lookup:{from:"profile",localField:"_id",foreignField:"userId",as:"profileData"}},
{$unwind:"$profileData"},
{$project:{profileData:1,username:1}}
{$limit:1}
])
.then(profile => {
if (!profile.length) {
errors.noprofile = "There is no profile for this user";
return res.status(404).json(errors);
}
res.json(profile[0]);
})
You can do it in 2 steps.
Look for users containing username in userSchema, get it's id.
Then in promise, use that id to, look for profileSchema contains.
router.get("/user/:username", (req, res) => {
users.findOne({ username: req.params.username }).then(_user=>{
profile.findOne({ user: _user._id }).populate('user').then(_profile => {
res.json(_profile);
})
})
});
This code will look for username in userSchema and look for userSchema's id in profileSchema then returns profileSchema populated with user.

nodejs/express mongoose .populate() on schema with refs to itself

I have this schema for a User:
const UserSchema = new Schema({
...
friends: [{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
}
}],
sentRequests: [{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
}
}],
recievedRequests: [{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
}
}]
}
Then when trying to show all the recievedRequests to a page I use this code:
router.get('/friendRequests', (req, res) => {
User.findOne({
_id: req.user.id
})
.populate('recievedRequests.user')
.then(curUser => {
res.render('friends/friendRequests', {
curUser: curUser
});
})
});
where User is the model and req.user.id is the id of the currently logged in user. However, in the webpage whenever I reference curUser.recievedRequests.user it simply returns the id of the user and not the actual user object. I am assuming it has to do with the nested schema reference since I can't see anything else that would cause this issue. Does anyone know of a way to resolve this?
Thanks.

Mongoose populate in array

I try to play with populate but without success ...
It's possible to do this?
I have 2 shema :
- User
import mongoose, { Schema } from 'mongoose'
const userSchema = new Schema({
email: { type: String, unique: true },
password: String,
passwordResetToken: String,
passwordResetExpires: Date,
products: [{
productId: { type: Schema.Types.ObjectId, ref: 'Product' },
dateAdd: { type: Date, default: Date.now }
}]
}, { timestamps: true })
const User = mongoose.model('User', userSchema)
export default User
And Product :
import mongoose, { Schema } from 'mongoose'
const productSchema = new Schema({
domain: String,
originUrl: { type: String },
price: Number,
img: String,
userFollow: [{ type: Schema.Types.ObjectId, ref: 'User' }]
})
const Product = mongoose.model('Product', productSchema)
export default Product
So I want to retrieve all the info for each of my prodcutId
I try this way (and many others without success):
User.findOne({ _id: userId }).populate({
path: 'products.productId',
populate: { path: 'products.productId', model: 'Products' }
}).exec(function (err, products) {
if (err) {
console.log('errors :' + err)
}
console.log('Product => ' + util.inspect(products))
})
Populate has no effect, same result with just the findOne()
I think User.findOne({ _id: userId }).populate('products.productId') should work.
Try using aggregate function of MongoDB and $lookup.
Users.aggregate([
{
"$match":
{
_id: user.id
}
},
{
"$lookup":
{
from: "Product",
localField: "products",
foreignField: "_id",
as: "products"
}
}
])
.exec()
.then((result) => {
//your result
})
.catch((err) => {
// if any error
});

Reference not populated

In a User schema, I have a simple reference to a Customer schema.
const UserSchema = new Schema({
customer: { type: Schema.Types.ObjectId, ref: Customer }, // Customer is the compiled CustomerSchema
...
});
const CustomerSchema = new Schema({
name: String,
...
});
In an Express controller, I'm fetching an user and I'm trying to embed the customer in the returned JSON:
export function me(req, res, next) {
User
.findOne({ _id: req.user._id }, '-salt -hashedPassword')
.populate('customer')
.exec((err, user) => {
if(err) return next(err);
if(!user) return res.json(401);
res.json(user);
});
}
But in the response, customer is null.
The test data I use:
A user document:
{
"_id" : ObjectId("570d1f0938f7da5151b815d2"),
"customer" : ObjectId("570d1f0838f7da5151b815d0"),
...
}
The related customer document:
{
"_id" : ObjectId("570d1f0838f7da5151b815d0"),
...
}
Probably a noob question, but I don't see what I don't see what I could forget =)
I think ref must be a string:
customer: { type: Schema.Types.ObjectId, ref: 'Customer' },

Resources