I'm running into an issue using Mongoose, Express where I want to save a sub document to my user by pushing it into the sub document array, which I can do. However the issues arise when I want to delete a gamesession that is stored in the users "sessions" attribute and also delete the gamesession globally. I think the issue arises because I'm saving two seperate instances of a gamesession. Here is the code for creating a new sub document called "gamesession" and pushing it onto the users "session" attribute
//POST /posts
// Route for creating gamesessions for specific user
router.post("/gamesessions/:uID/", function(req, res, next) {
var gamesession = new GameSession(req.body);
req.user.sessions.push(gamesession);
gamesession.postedBy = req.user._id;
req.user.save(function(err, user) {
if(err) return next(err);
gamesession.save(function(err, gamesession){
if(err) return next(err);
res.json(gamesession);
res.status(201);
});
});
});
Here is my UserSchema
var UserSchema = new Schema({
posts: [PostSchema],
sessions: [GameSessionSchema],
email: {
type: String,
unique: true,
required: true,
trim: true
},
username: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
}
});
And my GameSessionSchema
var GameSessionSchema = new Schema({
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
region: {
type: String,
required: true
},
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
game: {
type: String,
required: true
},
age: String,
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now},
platform: {
type: [String],
enum: ["Xbox One", "PS4", "PC"],
required: true
}
});
Edit: Adding my delete route to see if that helps
//DELETE /posts/:id/comments/:id
//Delete a specific comment
router.delete("/gamesessions/:uID/sessions/:gID", function(req, res) {
var gamesession = new GameSession(req.body);
gamesession.remove(function(err) {
req.user.save(function(err, user) {
if(err) return next(err);
res.json(user);
});
});
});
Then, when I want to delete a gamesession with a route, it only deletes the instance saved in user.sessions and when I want to query all gamesessions, it's still there, but deleted in my User document. Any ideas? I think it's because I'm saving the document twice, and if so, what's the best way to save it in user.sessions while also being able to delete from user.sessions and querying a global session.
Possibly not saving the removed gamesession from the GameSession document?
router.delete("/gamesessions/:uID/sessions/:gID", function(req, res) {
var gamesession = new GameSession(req.body);
gamesession.remove(function(err) {
req.user.save(function(err, user) {
if(err) return next(err);
gamesession.save(function(err, gamesession){
if(err) return next(err);
res.json({message: 'Updated GameSession Doc'}, gamesession)
})
res.json(user);
});
});
});
Related
So I have some users/posts/comments and I want to make sure that when I post a comment on a post, that user's comment array is updated to contain the comment they just made. I tried searching around and it seemed like the best way of doing this was to use mongoose's populate, but it doesn't seem to be working. I'm still a beginner with Mongoose so any help or direction would be appreciated. Thanks!
I tried something like this:
comment.save((err) => { //save the comment
if (err) return next(err);
res.status(200).json(comment);
});
User.find({username: username})
.exec((err, user) => {
user.comments.push(comment); // says comments is undefined, but should be []
user.comment_count++; // also, is there a way to set comment_count equal to the length of the comments array? Should I use .pre()?
user.save(() => {
if (err) return next(err);
});
});
This gives me a error like cannot push into users.comments (undefined).
Here are my Schemas:
const PostSchema = new Schema({
postedBy: {
type: String,
required: true
},
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
createdAt: {type: Date, default: Date.now},
comments: [{ type: Schema.Types.ObjectId, ref: 'CommentSchema' }],
likedBy: [User]
});
const CommentSchema = new Schema({
parentID: { type: String,
required: true,
},
postedBy: {
type: User,
required: true
},
content: {
type: String,
required: true
},
createdAt: {type: Date, default: Date.now},
editedAt: {type: Date, default: Date.now},
likedBy: [User],
});
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
email: {
comments: {
type: [{ type: Schema.Types.ObjectId, ref: 'CommentSchema' }],
default: [],
},
comment_count: { // how do I set this equal to the length of the comments array?
type: Number,
default: 0,
},
posts: {
type: [{ type: Schema.Types.ObjectId, ref: 'PostSchema' }],
default: [],
},
post_count: { // how do I set this equal to the length of the posts array?
type: Number,
default: 0,
},
});
I also tried population:
const comment = new Comment({
parentID,
postedBy,
content,
});
comment.save((err) => { //save the comment
if (err) return next(err);
res.status(200).json(comment);
});
User.find({ username: username })
.populate('comments')
.exec((err, user) => {
if(err) next(err);
console.log(user); // returns undefined for some reason
// also not sure what to do in this function...
});
This gives me an error like "cannot set headers after they are sent to the client."
first read more about how promises and call back worked.
quick fix would be.
comment.save((err) => { //save the comment
if (err) return next(err);
User.find({username: username})
.exec((err1, user) => {
if (err1) return next(err1);
res.status(200).json(user);
});
});
"cannot set headers after they are sent to the client." error occurred as you already send response.
res.status(200).json(comment);
Working with Mongoose "Populate" - So far I'm unable to successfully get the "Food" model to populate the "User" model.
The goal is to be able to save a "Food" to a user.
USER MODEL:
var UserSchema = new mongoose.Schema({
username: String,
password: String,
foods: [{ type: mongoose.Schema.Types.ObjectId}],
easy: {type: Boolean, default: false},
});
UserSchema.plugin(passportLocalMongoose)
module.exports = mongoose.model("User", UserSchema);
FOOD MODEL:
var foodSchema = new mongoose.Schema({
name: { type: String, required: false, unique: true },
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
}
});
module.exports = mongoose.model("Food", foodSchema);
GET ROUTE
router.get("/dashboard", function (req, res) {
User.find({currentUser: req.user})
.populate({path: 'foods'}).
exec(function (err, foods) {
if (err) return (err);
console.log('The food is:', req.user.foods.name);
});
});
POST ROUTE:
router.post("/dashboard", function(req, res, next) {
User.update({ id: req.session.passport.user }, {
}, function(err, user) {
if (err) return next(err);
User.findById(req.user._id, function(err, user) {
var newFood = new Food({
name: req.body.currentBreakfast,
image: 'test',
});
user.foods = newFood
user.save();
});
});
res.redirect('/dashboard');
});
You need to add the ref field in your user schema for foods to be populated while querying user.
var UserSchema = new mongoose.Schema({
username: String,
password: String,
foods: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Food' }],
easy: {type: Boolean, default: false},
});
You can user this query.
await User.find({currentUser: req.user}).populate('foods')
Try this it will auto-populate data
var UserSchema = new mongoose.Schema({
username: String,
password: String,
foods: [{ type: mongoose.Schema.Types.ObjectId,ref: 'Food'}}],
easy: {type: Boolean, default: false},
});
UserSchema.pre('find', prepopulate)
function prepopulate(){
return this.populate('foods')
}
I want to create a DB with Users which also have a reference to another DB called "Library" which has "favourites" and "likes". I will show the idea here:
User Model
const userSchema = Schema({
username: {type: String, minlength: 4, maxlength: 10, required: true, unique: true},
email: {type: String, required: true, unique: true},
password: {type: String, required: true},
isVerified: { type: Boolean, default: false },
library: {type: Schema.Types.ObjectId, ref: 'Library'}
}, { timestamps: true});
Library Model
const librarySchema = new Schema({
likes: [{
likeId: {type: String},
mediaType: {type: String}
}],
favourites: [{
favId: {type: String},
mediaType: {type: String}
}],
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
Can you please tell me if this is the right way to implement these models or if there is a better way?
At the moment if I try to call
User.findOne({email: 'xxx#xxx.com'}).populate('library').exec(function (err, library)
it doesn't find anything...
Library POST request
router.post('/favourites', passport.authenticate('jwt', {session: false}), function (req, res) {
const favouritesFields = {};
if (req.body.favId) favouritesFields.favId = req.body.favId;
if (req.body.mediaType) favouritesFields.mediaType = req.body.mediaType;
Library.findOne({user: req.user._id}).then(library => {
if (library) {
Library.update({user: req.user._id}, {$push: {favourites: favouritesFields}})
.then(library => res.json(library));
} else {
new Library({user: req.user._id, favourites: favouritesFields}).save().then(library => res.json(library));
}
});
});
User POST request
router.post('/signup', function (req, res) {
const {errors, isValid} = validateSignupInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
// Check if email already exists
User.findOne({email: req.body.email}, function (user) {
if (user) {
return res.status(400).json({
title: 'Email already exists'
});
}
});
// Create and save the new user
let user = new User({
username: req.body.username.toLowerCase(),
email: req.body.email.toLowerCase(),
password: bcrypt.hashSync(req.body.password, 10)
});
user.save(function (err, result) {
if (err) {
return res.status(500).json({
title: 'An error occurred during the signup',
error: err
});
}
res.status(201).json({
title: 'User created',
obj: result
});
Your problem is not with the query you're making. there is no foundUser.library because one was never added.
You're adding users to libraries, but you're not adding libraries to your users. if you run the following code in your app:
Library.find({}).populate("user").exec(function(err, foundLibraries){
if (err){
console.log(err);
} else {
console.log(foundLibraries);
}
});
You would see that the libraries have their "user" properties, that when populated contain the entire user document as an object. But, the reason that isn't working for foundUser.library when you query for users is that foundUser.library was never assigned. you know how you're assigning the email, username and password when creating users, you have to do the same for the library property. Or, in your case, since a library is only created after the user, you can just set the value of user.library in the callback of creating/saving the library.
I have two schemas one is user schema in user.js file and other is product schema in product.js file
My user schema in user.js file as follows:
var userSchema = new Schema({
firstname: {
type: String,
required: true
},
lastname: {
type: String,
required: true
},
password: {
type: String,
required: true
},
mobileno: {
type: Number,
unique: true,
required: true
},
facebookid: {
type: String
},
userimage: {
type: String
}
});
and I am overriding automatically generated _id using mongoose-auto-increment module to get automatically incremented userId in user collection.
And my product schema in product.js file as follows:
var productSchema = new Schema({
userId: {
type: String,
required: true
},
productName: {
type: String,
required: true
},
productId: {
type: String,
required: true
},
price: {
type: Number,
unique: true,
required: true
},
prodcutImage: {
type: String
}
});
When user will add new products in collection he will fill all the fields mentioned in product schema. I want to verify that entered userId is exists in user collection or not when new product is added by user in product collection.
I tried to access userSchema.find method in productSchema pre save hook
productSchema.pre('save', function (next) {
userSchema.findOne({'_id': userId}, function(err, user)
{
console.log(user);
});
}
But It returns an error. Can somebody help me in this issue.
You can do like this
app.get('/user/:id', function (req, res, next) {
userSchema.findOne({'_id': userId}, function(err, user)
{
if(user){
next()
}else{
res.json("user id is not valid");
}
});
}, function (req, res, next) {
// code to add your product in product schema
})
more better way is to use Router-level middleware of express
Hello so I am making a basic app with users and posts.
I followed the mongoose documentation on population (http://mongoosejs.com/docs/2.7.x/docs/populate.html) and setup my Schemas so that the users and be connected to posts
var userSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: String,
created_at: Date,
updated_at: Date,
admin: Boolean,
posts: [{ type: mongoose.Schema.ObjectId, ref: 'Post' }]
});
var postSchema = new mongoose.Schema({
_user : [{ type: mongoose.Schema.ObjectId, ref: 'User' }],
audioFile: { type: String, required: true },
imageFile: { type: String },
title: { type: String, required: true },
artist: { type: String, required: true },
start: { type: String, required: true },
stop: { type: String, required: true },
genre: { type: String, required: true },
tags: [{ type: String }]
});
app.get('/', function (req, res){
Post.find({}, function(err, allPosts){
if(!err){
res.render('main.njk', {
posts : allPosts,
title : 'Title',
isLogged : req.session.isLogged,
user : req.session.user,
messages : req.flash('alert')
});
} else { return done(err); }
});
});
Thats all fine and gravy and I can run a foreach loop on allPosts to pull each one in my HTML, but when I try to think of how I am going to display all the posts with their respective users attached to each post I am unsure of how to connect the two since all the examples in the mongoose doc is just mainly for findOne.
I was thinking something like this
app.get('/', function (req, res){
Post.find({}, function(err, allPosts){
if(!err){
allPosts.populate('_user', ['username']);
allPosts.exec(function (err, users){
if(err) console.log(err);
console.log(users);
});
res.render('main.njk', {
posts : allPosts,
title : 'Spaurk.net',
isLogged : req.session.isLogged,
user : req.session.user,
messages : req.flash('alert')
});
} else { return done(err); }
});
});
but that doesn't work of course.
So I was wondering if anyone with experience with this situation would be able to help me solve this.
Thanks a lot for any input.
EDIT, thanks to Daves help I was able to get the populate to work properly, I just cant pull the fields I want correctly with
Post.find({}).populate('_user').exec(function(err, allPosts){
In my loop {% for post in posts %}
, when I do post._user it shows the whole user schema, but when I do post._user.username it doesn't return anything. I am unsure as to why this is.
The proper way to structure a populate on a query is like this:
Post.find({})
.populate('_user')
.exec((err, allposts){...})
Then you will have an array of your Posts with the _user array populated. If you need to access a property of a user, you will need to do another loop through the _user array or specify with use you want to use _user[0].<property>