Refer user data in post collection using Mongoose - node.js

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)

Related

Saving an item that is specific to a user schema but not able to retrieve it back - mongoose

I have creared two schemas, user and medicine.
If a user adds medicines it should show up only in his/her account.
I am able to save the medicine ids to that specific user but i'm not able to get those medicines back i.e: medicines show for all the other users as well.
Here's the code snippet that saves meds to specific user:
const {userId, medName, medDescription, dose, medType, date, time} = req.body;
try {
const newMed = new MedsSchema({
userId,
medName,
medDescription,
dose,
medType,
date,
time,
});
await newMed.save().then(() => res.send({response: 'ok'}));
const specificUser = await User.findById({_id: userId});
specificUser.medicines.push(newMed);
await specificUser.save().then(
User.findOne(specificUser)
.populate('medicines')
.exec(function (err, docs) {
if (err) return handleError(err);
console.log(docs);
}),
);
Here's the userSchema:
const userSchema = new mongoose.Schema(
{
username: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
phone: {
type: Number,
required: true,
},
email: {
type: String,
unique: true,
required: true,
},
medicines: [{type: mongoose.Schema.Types.ObjectId, ref: 'MedsSchema'}],
},
{
toJSON: {
virtuals: true,
},
},
);
router.get('/getMeds/:Id', (req, res) => {
console.log(req.params.Id);
MedsSchema.find({userId: req.params.Id}, function (err, result) {
if (err) {
res.send(err);
} else {
res.send(result);
}
});
});
what do i add to this that will make me get only specific medicines for that specific user instead of getting all medicines?
Can you edit your first code snippet to
const { userId, medName, medDescription, dose, medType, date, time } = req.body;
try {
const user = await User.findOone({ _id: userId });
const newMed = await MedsSchema.create({
userId: user,
medName,
medDescription,
dose,
medType,
date,
time,
});
const specificUser = await User.findByIdAndUpdate({ _id: userId }, { $push: { medecines: newMed } });
return res.json({ newMed, specificUser })
};
and in the router
router.get('/getMeds/:Id', async (req, res) => {
console.log(req.params.Id);
const user = await User.findOne({ _id: req.params.Id }).populate({ path: 'medicines' })
console.log(user.medicines)
return res.json({meds: user.medicines})
});
also check the console results to see if everything is working

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 with Mongoose - pragmatically add url to image retrieved with find()

I have the following Mongoose schema:
var userSchema = new Schema({
username: {
type: String,
},
password: {
type: String,
},
email: {
type: String,
},
_imageId: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'files'
}]
});
When I retrieve data from the database using the function findOne() in this way
getById: function (req, res) {
User.findOne({ _id: req.params.id }, function (err, user) {
getProfileImage(req, user, function(user) {
return res.status(200).send({
msg: 'User retrieved successfully',
data: user
});
});
});
}
I get an array of image, but only with their id, so I have to manually add the URL using the function getProfileImage.
Is there a way using Mongoose to pragmatically add the URL?
EDIT:
I add the implementation of getProfileImage
var getProfileImage = function(req, user, callback) {
if(user && user._imageId.length > 0) {
user.set('profileImage', req.protocol + '://' + req.get('host') + '/image/get/' + user._imageId[user._imageId.length - 1], { strict: false });
}
callback(user);
};
Yes there is a way, and it doesn't involve storing _imageId in the userSchema. It's the other way around. You need to create a imageSchema that stores _userId and fetch all images from there.
Schemas
var userSchema = new Schema({
username: {
type: String,
},
password: {
type: String,
},
email: {
type: String,
}
});
var imageSchema = new Schema({
url: {
type: String,
},
userId: {
type: mongoose.Schema.Types.ObjectId,
}
});
Controller
getById: function (req, res) {
User.findOne({ _id: req.params.id }, function (err, user) {
// Get images related to the user
Image.find({userId: req.params.id}, function(err, images) {
user.images = images;
return res.status(200).send({
msg: 'User retrieved successfully',
data: user
});
});
});
}

Many to one relationships in mongoose. How to access and output the referred to object ID's?

I'm trying to create a "wishlist" feature for users on my node / mongo application. I've assumed the best way to model the scheme would be to reference the items they like. So far my reading has brought me to this point (I'm not very familiar with the Types.ObjectID):
Schema Model
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
},
wishlist: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Wishlist",
required: true
}]
});
I've managed to write some code which pushes the relevant _id into the "Likes" array:
Product.findById(productID).exec(function (err, user) {
User.updateOne({ _id: req.session.userId }, { "$push": { "wishlist": productID } }, function (err, user) {
if (err) {
console.log("Failed to add")
} else {
console.log(productID + " has been added")
}
});
});
This outputs in the database like so:
{
"_id" : ObjectId("5c3f7e1f1268203b1f31cb17"),
"email" : "email",
"password" : "password",
"__v" : 0,
"wishlist" : [
ObjectId("5c41f4b42f82b14798d5c7fc"),
ObjectId("5c41f4b42f82b14798d5c7ff")
]
}
I'm stuck on how I'd output these wishlist items in my template. My assumption was to get the data like this:
router.get('/wishlist', middleware.requiresLogin, function(req, res, next) {
User.findOne({ _id: req.session.userId }, function(err, user) {
res.render('wishlist', {
title: 'Wishlist',
template: 'wishlist',
saved: user.wishlist,
header: true,
footer: true
});
});
});
And the loop through the items like this:
{{#each saved }} Code goes here {{/each }}
Am I approaching this correctly?
you'll need to populate the wishlist field, try this,
User.findOne({ _id: req.session.userId }).
populate('wishlist').
exec(function (err, user) {
res.render('wishlist', {
title: 'Wishlist',
template: 'wishlist',
saved: user.wishlist,
header: true,
footer: true
});
});
You can refer to the Populate (mongoose documentation).
//User_controller.js
exports.getUser = (req, res) => {
User.findOne({ _id: req.session.userId })
.populate('wishlist')
.then((user) => { res.json(user) })
.catch((error) => { res.status(500).json({ error })
});
};
// UserRoute.js
const express = require("express");
const router = express.Router();
const userCtrl = require('./user_controller');
router.get('/:id', userCtrl.getUser);
module.exports = router;
//server.js
//...
const userRoute = require("./UserRoute");
app.use("/user", userRoute);
//...

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