Reference not populated - node.js

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' },

Related

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.

Refer user data in post collection using Mongoose

I created login & registration page and users data is being stored in user collection. Also did functionality for adding & viewing new post created by loggedin user. So whenever a user is creating the post, I need to refer the user data (username) in post collection. I have checked some forums, but not able to understand populate method. I need to know about referencing data in schema and how to fetch the data from user collection and use it in post collection.
Here is the User Model
var UserSchema = new mongoose.Schema({
username: {
type: String,
index: true
},
password: {
type: String
},
email: {
type: String
},
name: {
type: String
},
profileimage: {
type: String
},
uposts: [{ type: Schema.Types.ObjectId, ref: 'Post'}]
});
var User = module.exports = mongoose.model('User', UserSchema);
Here is the Post Model
var PostSchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
postimage: {
type: String
},
author: {
type: Schema.Types.ObjectId,
ref: "User"
// username: String
},
});
var Post = module.exports = mongoose.model('Post', PostSchema);
Route for adding new post
router.post('/add', upload.single('postimage'), (req, res, next) => {
if(req.file) {
console.log('Uploading File..');
var postimage = req.file.filename;
} else {
console.log('No File Uploaded');
var postimage = 'noimage.jpg';
}
var newPost = new Post({
postimage: postimage
});
Post.createPost(newPost, (err, post) => {
if(err) throw err;
console.log(post);
});
req.flash('success', 'Successfully Created Posts');
res.location('/');
res.redirect('/');
});
Router for displaying Posts
router.get('/view', ensureAuthenticated, (req, res, next) => {
// res.render('viewpost', { user: req.user });
Post.find({_id: {$ne: req.user._id}}, (err, posts) => {
if(err) {
console.log(err);
} else {
res.render('viewpost', {currentUser: req.user, posts: posts});
}
});
});
Also I need to display the post details of other users except the loggedin user.
It would be really helpful, if you could provide a suitable method for the same.
Before Apply please check mongoDB version,
router.get('/view', ensureAuthenticated, async(req, res, next) => {
try {
const posts = await Post.aggregate([
{
$match: { userId: { $ne: req.user._id }}
},
{
$lookup: {
from: "users",
localField: "userId",
foreignField: "_id",
as: "user"
}
},{
$project: {
postimage: "$postimage",
user: { $arrayElemAt: ["$user", 0] }
}
}
])
res.render('viewpost', {currentUser: req.user, posts: posts, error: ''});
} catch(error) {
res.render('viewpost', { currentUser: req.user, posts: [], error: error});
}
})
If no result found please make req.user._id as ObjectId using mongoose.Types.ObjectId(req.user._id)

mongoose .populate not working

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 }]

Populate in Mongoose not working

I am trying to perform associations by referencing method. There are 2 models:
1. User
2. Product
I have established one-to-one relationship of 1 user can have multiple products. User creation is successful
Product creation is successful
Code Inputs
var mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/product_demo_x9");
Product Schema
var productSchema = new mongoose.Schema({
category : String,
Brand: String
});
var Product = mongoose.model("product", productSchema);
User Schema
var userSchema = new mongoose.Schema({
email: String,
name: String,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Product"
}
]
});`
var User = mongoose.model("user", userSchema);
User Creation
User.create({
email: "madhur#google.com",
name: "Maddy"
},function(err,newUser){
if(err){
console.log(err);
}
else {
console.log(newUser);
}
});
Product Creation
Product.create({
category: "Smartwatches",
Brand: "Samsung and Google"
},
function(err,product){
console.log(product);
User.findOne({name : "Maddy"},function(err,foundUser){
if(err) {
console.log(err);
}
else {
foundUser.products.push(product);
foundUser.save(function(err,updatedUser){
if(err){
console.log(err);
}
else {
console.log(updatedUser);
}
});
}
});
});
Display of associated Data on the console
User.find({email: "madhur#google.com"}).
populate("products").
exec(function(err,user){
if(err){
console.log(err);
}
else {
console.log(user);
}
});
Code Outputs
User Creation (Success)
[{
products: [],
_id: 5a47acb0317d4e3c2081b8ce,
email: 'madhur#google.com',
name: 'Maddy',
__v: 0
}]
Product Creation and associating (Success)
{
_id: 5a47acd53c771123b4018ff1,
category: 'Smartwatches_2',
Brand: 'Samsung and Google',
__v: 0
}
{
products: [ 5a47acd53c771123b4018ff1 ],
_id: 5a47acb0317d4e3c2081b8ce,
email: 'madhur#google.com',
name: 'Maddy',
__v: 1
}
Display of embedded data using populate - Failure!!
{ MissingSchemaError: Schema hasn't been registered for model "products".
Use mongoose.model(name, schema)
at new MissingSchemaError
Can anyone please explain me how to do it correctly?
Thanks in Advance
Model Name is Case-sensitive
'Product' is not equal to 'product'
and when u create a model as 'product' (singular) it converts it into plural, i.e. 'products', this is default mongoose behavior, can be overridden.
so change the following:
var userSchema = new mongoose.Schema({
email: String,
name: String,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "products" //<---- changed 'Product' to 'products'
}
]
});`
var User = mongoose.model("user", userSchema);
Try this
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/product_demo_x9');
var productSchema = new mongoose.Schema({
category: String,
Brand: String
});
var Product = mongoose.model('Product', productSchema);
var userSchema = new mongoose.Schema({
email: String,
name: String,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Product'
}
]
});
var User = mongoose.model('User', userSchema);
User.create({
email: 'madhur#google.com',
name: 'Maddy'
}, function(err, newUser) {
if (err) {
console.log(err);
} else {
console.log(newUser);
}
});
Product.create({
category: 'Smartwatches',
Brand: 'Samsung and Google'
},
function(err, product) {
console.log(product);
User.findOne({name: 'Maddy'}, function(err, foundUser) {
if (err) {
console.log(err);
} else {
foundUser.products.push(product);
foundUser.save(function(err, updatedUser) {
if (err) {
console.log(err);
} else {
console.log(updatedUser);
}
});
}
});
});
User.find({email: 'madhur#google.com'})
.populate('products')
.exec(function(err, user) {
if (err) {
console.log(err);
} else {
console.log(user);
}
});
Solved
Did the following
Downgraded my Mongoose version from 5.00x to 4.10.8 using the following command npm remove mongoose then npm install mongoose#4.10.8 --save
Made the following change in app.js file
var userSchema = new mongoose.Schema({
email: String,
name: String,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "product" //<---- changed 'Product' to 'product'
}
]
});`
var User = mongoose.model("user", userSchema);
Thanks to the Stack community for giving a try!

node js mongoose delete a from an id in document array

I've two collection of reference both collection together. One of the collection is user and the other collection is project.
So, a user can add project to the projects collection, then one of the user type called supervisor can add staff to the project and the project id saved to the user collection which referred to staff document on the project collection.
So actually i need to do when admin deletes a supervisor from the user collection it deletes all the projects created by supervisor users's id that equal to addedBy documents which deleted from the users collection.
So my problems is when i do this process i need to delete all the project id is equal to the users collection projectId. it's an array and I tried to do this to many times but i couldn't find a solution. I'll provide all of the source code. That i created for this project.
Users collection
const userSchema = new Schema({
firstName: {
type: String
},
lastName: {
type: String
},
email: {
type: String
},
username: {
type: String
},
password: {
type: String
},
usertype: {
type: Schema.ObjectId,
ref: 'usertypes'
},
projectId: [{
type: Schema.ObjectId,
ref: 'projects'
}]
});
Project collection
const proSchema = new Schema({
projectName: {
type: String
},
description: {
type: String
},
addedBy: {
type: Schema.ObjectId,
ref: 'users'
},
staff: [{
type: Schema.ObjectId,
ref: 'users'
}]
});
Here is the query that i tried to do the process that i mentioned in above
Users
.findByIdAndRemove({
_id: req.params.id
})
.then(function(){
Projects
.remove({
userId: req.params.id
})
.then(function(err, project){
if(err) throw err;
console.log(project.id)
Users
.update({}, {
$pull: {
projectId: project.id
}
}, function(){
res.json({
success: true,
message: "Deleted"
});
});
});
});
I think the problems are
(1) Model.findByIdAndRemove only expects the ID (not the condition) i.e. Users.findByIdAndRemove(req.params.id) instead of Users.findByIdAndRemove({ _id: req.params.id })
(2) Model.remove's callback does not have a second argument in Projects.remove({ userId: req.params.id }).then(function (err, project) {. As well, you don't have a userId field in your ProjectSchema.
I would do
// delete user
Users.findByIdAndRemove(req.params.id, function (err, user) {
console.log('deleting user', user);
if (err)
throw err;
// delete user's projects
Projects.remove({ addedBy: user._id }, function (err) {
console.log('deleting projects');
if (err)
throw err;
// delete project references
Users.update({}, { $pull: { projectId: { $in: user.projectId }}}, function (err) {
console.log('deleting project references');
if (err)
throw err;
res.json({ success: true, message: "Deleted" });
});
});
});
(3) user.projectId is an array of ObjectIDs, so you need to use $in (see first example).
Aside: projects is a better name than projectId. The latter is ambiguous because a user has multiple projects not projectIds.
User.findByIdAndUpdate(req.params.idRec,
{ $pull: { comments: { _id: comm._id } } },
function (err, doc) {
if (!err) {
res.status(200).send()
} else {
res.render('error', { error: err })
}
})

Resources