mongoose .save() doesn't work on a specific collection - node.js

so I'm trying to create a party with creator field with id of a user, and at the same time adding a party id to users parties using mongoose sessions. Here's the code of a request:
const createParty = async (req, res, next) => {
const {title, description, address, creator} = req.body;
const createdParty = new Party({
title,
description,
image: 'https://media-cdn.tripadvisor.com/media/photo-s/14/03/b3/4e/tlv.jpg',
address,
creator,
savedBy: []
});
let user;
try {
user = await User.findById(creator);
} catch (err) {
let error = new HttpError('Fetching user failed', 500);
return next(error);
}
if (!user) {
return next(new HttpError('Could not find user for providen id', 404));
}
try {
const sess = await mongoose.startSession();
sess.startTransaction();
await createdParty.save({ session: sess });
user.parties.push(createdParty);
console.log(user);
await user.save({ session: sess });
await sess.commitTransaction();
} catch (err) {
let error = new HttpError('Creating party failed', 500);
return next(error);
}
res.status(201).json({party: createdParty});
};
And my user and parties schemas:
const userSchema = new Schema({
username: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true, minlength: 6 },
image: { type: String, required: true },
parties: [{ type: mongoose.Types.ObjectId, required: true, ref: 'Party' }],
savedParties: [{ type: mongoose.Types.ObjectId, required: true, ref: 'Party' }]
});
const partySchema = new Schema({
title: { type: String, required: true },
description: { type: String, required: true },
image: { type: String, required: true },
address: { type: String, required: true },
creator: { type: mongoose.Types.ObjectId, required: true, ref: 'User' },
savedBy: [{ type: mongoose.Types.ObjectId, required: true, ref: 'User' }]
});
The problem is I can't save a user with new party id, only this line fails:
await user.save({ session: sess });. Tried to move this line to a separate try/catch, tried to add user.markModified('parties'); didn't help. Please help those who may know the solution.🙏🏻
UPDATE ON THE PROBLEM
So I did some testing and found out that if I delete everything from the database, and I'll create a user I will be able to add parties, and it'll work as it should. But if I'll create another user and afterward will try to add a party to one of the users it won't work.

when you session it won't create the collection if it doesn't exist and you need to do it manually in the data

Related

How to make options (public or private) on Post? in reactjs, nodejs

I'm creating Social Network app using MERN. I have made it so far that users can create posts and have followers and followings. My task is to make an option on the post so it can be public or private. I don't know how to do that. Has anybody idea or example of code how to do it? Thanks!
This is my post model:
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema({
caption: String,
image: {
public_id: String,
url: String,
},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
createdAt: {
type: Date,
default: Date.now,
},
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
comments: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
comment: {
type: String,
required: true,
},
},
],
visibility: {
type: String,
enum : ["public", "private"],
default: "public"
},
});
module.exports = mongoose.model("Post", postSchema);
This is my user model:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Please enter a name"],
},
avatar: {
public_id: String,
url: String,
},
email: {
type: String,
required: [true, "Please enter an email"],
unique: [true, "Email already exists"],
},
password: {
type: String,
required: [true, "Please enter a password"],
minlength: [6, "Password must be at least 6 characters"],
select: false,
},
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Post",
},
],
followers: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
following: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
resetPasswordToken: String,
resetPasswordExpire: Date,
});
userSchema.pre("save", async function (next) {
if (this.isModified("password")) {
this.password = await bcrypt.hash(this.password, 10);
}
next();
});
userSchema.methods.matchPassword = async function (password) {
return await bcrypt.compare(password, this.password);
};
userSchema.methods.generateToken = function () {
return jwt.sign({ _id: this._id }, process.env.JWT_SECRET);
};
userSchema.methods.getResetPasswordToken = function () {
const resetToken = crypto.randomBytes(20).toString("hex");
this.resetPasswordToken = crypto
.createHash("sha256")
.update(resetToken)
.digest("hex");
this.resetPasswordExpire = Date.now() + 10 * 60 * 1000;
return resetToken;
};
module.exports = mongoose.model("User", userSchema);
and here is the logic how of creating post:
exports.createPost = async (req, res) => {
try {
const myCloud = await cloudinary.v2.uploader.upload(req.body.image, {
folder: "posts",
});
const newPostData = {
caption: req.body.caption,
image: {
public_id: myCloud.public_id,
url: myCloud.secure_url,
},
owner: req.user._id,
};
const post = await Post.create(newPostData);
const user = await User.findById(req.user._id);
user.posts.unshift(post._id);
await user.save();
res.status(201).json({
success: true,
message: "Post created",
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
In your post's model, you can add a "visibility" field with private & public as possible values (one of them default, obviously). Then every time you are querying for that post or list of posts, use that visibility column to decide if the user should be served that post or a 403 response page.
Check this for enum validation.

MongoDB finding first user instead of the specified Email user

Im trying to find a user by their email in my MongoDB call through express and mongoose. Im getting it through a request body but at the moment it's only returning the first user in the collection or all the users in the collection, how do I find ONE user by their email address? I would obviously also like to then check their passwords...
User Schema looks like this
const users = mongoose.Schema({
Role: {
type: String,
default: 'Customer'
},
name: {
type: String,
required: true
},
password: {
type: String,
required: true
},
birthday:{
type: String,
required: true
},
displayName: String,
createdAt: {
type: Date,
default: Date.now
},
contact:{
email:{
type: String,
required: true
},
cellphone: String,
},
shippingAd:{
house:{
type: Number,
required: true,
},
road:{
type: String,
required: true,
},
complex: String,
city: {
type: String,
required: true,
},
province:{
type: String,
required: true,
},
postalCode:{
type: String,
required: true,
},
Country:{
type: String,
required: true,
},
},
newsletter:{
type: Boolean,
default: false
},
wishlist: [
{ type: mongoose.Schema.Types.ObjectId, ref: 'products'}
]
});
users.pre('save', async function(next){
try {
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(this.password, salt);
this.password = hashedPassword;
next();
} catch (error) {
next(error);
}
})
Express setup for the call
userRouter.post('/api/loginuser',async (req, res) =>{
const findUser = await userSchema.findOne({
email: req.body.email
});
if(findUser){
return res.json(findUser)
} else{
res.json(false)
}
});
Rest API call
const loginUser = (e) =>{
let payload = {
email: formValues.email,
password: formValues.password
}
axios.post('http://localhost:5001/api/loginuser', payload)
.then(res =>{
if(!res.data){
alert('There was no response from the database.')
} else{
if(res.data){
sessionStorage.setItem('user', res.data.user)
// navigate('/')
console.log(res.data)
}else{
alert('Something is wrong in the backend')
}
}
})
.catch(err =>{
console.log(err);
})
}
Your email field is nested within your contact info so in order to make a query to find a user by the email you have to search for that nest value like this.
const findUser = await userSchema.findOne({
"contact.email": req.body.email
});

Linking models in MongoDB

I have a model to create an article, and I want to save the id of the user that created that article also. I am doing this, but I have an error. I really appreciate any help!
let articleSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
},
markdown: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
},
user: {type: Schema.ObjectId, ref: 'User'}
});
module.exports = mongoose.model('Article', articleSchema);
let userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
role: {
type: String,
default: 'USER_ROLE',
enum: validRoles
}
});
userSchema.plugin(uniqueValidator, {message: '{PATH} must be unique'});
module.exports = mongoose.model('User', userSchema);
And in the post method of the article I have this:
How can I access the user??
const article = new Article({
title: body.title,
description: body.description,
markdown: body.markdown,
user: req.user._id
});
I think that there's a problem in your article schema. Try to change
user: {type: Schema.ObjectId, ref: 'User'}
to
user: {type: Schema.Types.ObjectId, ref: 'User'}
Then, to display the author information try this in your routes file:
app.get('your-get-article-route', async (req, res) => {
try {
const articles = await Article.find().populate('user');
// other pieces of code if needed
res.status(200).json({ articles });
} catch (error) {
res.status(400).json({ message: 'an error occured!'});
)
If you're not familiar with async/await, you can do it with promises:
app.get('your-get-article-route', (req, res) => {
const articles = Article.find().populate('user').then((articles) => {
// other pieces of code if needed
res.status(200).json({ articles });
}).catch((error) => {
res.status(400).json({message: 'an error occured'}
);
)

Mongoose do not populate objectid in an objectid of array

THIS PROBLEM IS A LITTLE LONGER. SO I TYPED BOLD THE CRITICAL INFORMATIONS FOR YOU.
I develop a project like stackoverflow. I have 4 databases which are:
problems
users
solutions
comments
I referrenced these schemas each other. Here is the Schemas:
Problem Schema
const problemSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'You have to enter a title']
},
content: {
type: String,
required: [true, 'You have to enter a content']
},
createdAt: {
type: Date,
default: Date.now()
},
slug: {
type: String
},
solution: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Solution'
},
],
comment: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
],
votes: {
type: Number,
default: 0
},
views: {
type: Number,
default: 0
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
})
module.exports = mongoose.model('Problem', problemSchema)
User Schema:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: [true, 'You have to enter an email'],
unique: true,
match: [
/^([\w-\.]+#([\w-]+\.)+[\w-]{2,4})?$/,
'Please provide a valid email address.'
]
},
password: {
type: String,
required: [true, 'You have to enter a password'],
minlength: [6, 'Your password cannot be less than 6 character.'],
select: false
},
role: {
type: String,
default: 'user',
enum: ['user', 'admin']
},
createdAt: {
type: Date,
default: Date.now()
},
about: {
type: String
},
place: {
type: String
},
age: {
type: Number
},
blocked: {
type: Boolean,
default: false
},
problem: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Problem'
},
],
solution: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Solution'
}
],
comment: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
})
and Comments Schema:
const commentSchema = new mongoose.Schema({
content: {
type: String,
required: [true, 'You have to enter a content']
},
createdAt: {
type: Date,
default: Date.now()
},
isFunctional: {
type: Boolean,
default: false
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
problem: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Problem'
},
})
module.exports = mongoose.model('Comment', commentSchema)
In my project, I send problems into MongoDB. Then I send comment. After save comments, I add these comments into problems and user DB with a function.
function that comments are saved in DB:
const Comment = require('../models/comment/Comment')
const Problem = require('../models/problem/Problem')
const User = require('../models/user/User')
const asyncErrorWrapper = require('express-async-handler')
const addCommentToProblem = asyncErrorWrapper(async (req, res, next) => {
const {content, problemId} = req.body
const newComment = await Comment.create({
content: content,
problem: problemId,
user: req.user.id,
})
const problemOfComment = await Problem.findByIdAndUpdate(problemId, {
$push: { comment: newComment._id }
})
const userOfComment = await User.findByIdAndUpdate(req.user.id, {
$push: { comment: newComment._id }
})
})
Okey everything is so far so good. The problem comes here. When I try to get a problem, I populate some fields for example user fields. So I can add user information in this detail of problem. When populate user and comment in problem schema, it sends me the data. Still, we're ok. But when I try to get user field in comments, it doesn't populate user. It turns just objectId of user information.
Here is the function that I get problem:
const getAProblem = asyncErrorWrapper(async (req, res, next) => {
const {id} = req.params
const problems = null
await Problem.findByIdAndUpdate(id, {
$inc: { views: 1 }
}, { new: true })
.populate('user') ==> THIS LINE WORKS
.populate('comment') ==> THIS LINE WORKS
.populate('comment.user') ==> THIS LINE DOES NOT WORK
.exec(function(err, post) {
if(err) {
console.log(err)
}
res
.status(200)
.json({
success: true,
data: post
})
});
})
Thanks for reading and your patience. Any help will be appreciated.
See doc at https://mongoosejs.com/docs/populate.html
And try this way.
const getAProblem = asyncErrorWrapper(async (req, res, next) => {
const {id} = req.params
const problems = null
await Problem.findByIdAndUpdate(id, {
$inc: { views: 1 }
}, { new: true })
.populate('user') ==> THIS LINE WORKS
.populate({
'path': 'comment',
'populate': {
'path':'user'
}
})
.exec(function(err, post) {
if(err) {
console.log(err)
}
res
.status(200)
.json({
success: true,
data: post
})
});
})

Referencing Object Id not working in Mongoose 4.11.6

I have this problem. Basically, I have 2 schemas - a User schema and a Document schema. The Document schema has an owner which references the _id field of documents in the User collection.
The problem is that I am still able to save documents in the Document collection with owner ids that do not exist in the User collection which should not be so.
Here is my User schema and Document schema respectively
const UserSchema = new Schema({
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
email: {
type: String,
validate: [{ validator: value => isEmail(value), msg: 'Invalid email.'
}],
unique: true,
required: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
default: false,
},
}, {
timestamps: true,
});
const User = mongoose.model('User', UserSchema);
And the Document Schema
const DocumentSchema = new Schema({
title: {
type: String,
required: true,
},
text: {
type: String,
},
access: {
type: String,
enum: ['public', 'private'],
default: 'public',
},
owner: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
}, {
timestamps: true,
});
const Document = mongoose.model('Document', DocumentSchema);
Any help will be appreciated thanks.
For that situation you can add pre save function in your Document schema that will call before save your Document.
const DocumentSchema = new Schema({
// ...
}, {
timestamps: true,
});
DocumentSchema .pre("save",function(next) {
var self = this;
if (self.owner) {
mongoose.models['User'].findOne({_id : self.owner }, function(err, existUser){
if(err){
return next(false, err);
}
if(!existUser)
return next(false, "Invalid user reference");
else
return next(true);
});
} else {
next(false, "Owner is required");
}
});
const Document = mongoose.model('Document', DocumentSchema);

Resources