Updating a subdocument in array in Mongoose - javascript

I have two models, defined in two seperate files
//models/Projects.js
const mongoose = require('mongoose');
export const projectSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
companyBackground: {
type: String,
required: false,
trim: true,
}
});
module.exports = mongoose.models.Project || mongoose.model('Project', projectSchema);
//models/User.js
const mongoose = require('mongoose');
const { projectSchema } = require('./Project');
const userSchema = new mongoose.Schema({
projects: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Project' }]
});
module.exports = mongoose.models.User || mongoose.model('User', userSchema);
I create a new user, and then create a new project, by pushing it to the user
import dbConnect from "utils/dbConnect";
import User from "models/User";
import Project from "models/Project";
export default async (req, res) => {
await dbConnect();
const { query: { id }, method } = req;
switch (method) {
case "POST":
try {
const user = await User.findById(id);
if (!user) {
return res.status(400).json({ success: false, message: "User not found" });
}
const newProject = new Project(req.body);
console.log(newProject);
user.projects.push(newProject);
await user.save();
if (!user) {
return res.status(400).json({ success: false, message: "Project not added"
}
res.status(200).json({ data: user });
} catch (error) {
res.status(400).json({ success: false, message: error.message });
}
break;
default:
res.status(400).json({ success: false });
break;
}
};
Bu here is the question. I can easily create a new project and save it to the user, but not make any updates.
Approaches I have tried include (but are not limited to):
https://stackoverflow.com/a/26157458/2947684
https://stackoverflow.com/a/47762417/2947684
To demonstrate the problems, see
const updateString = "projects.$." + req.body.property;
Project.findOneAndUpdate(
{ _id: req.body.projectid },
{ $set: { [updateString]: req.body.value } },
function (error, success) {
if (error) {
console.log('error');
console.log(error);
} else {
console.log("success");
console.log(success); //Returns null
}
}
);
The above returns null
And when I do this, I get
User.findById(userid)
.then((user => {
const project = user.projects.id(req.body.projectid); //user.projects.id is not a function
project.set(newContent);
return user.save();
}))
.then((user) => {
res.send(user);
})
.catch((error) => {
console.log('error: ', error.message);
res.status(400).json({ success: false, message: error.message });
});
Here is says that user.projects.id is not a function...
In mongo DB Atlas, I can see that the project has indeed been created.
This is turning me crazy, can someone tell me what I am doing wrong?
Would it be better, simply to create the projects as arrays (without making them as a mongoose object?)

When pushing to user.projects you should just add the _id since the property is defined as ObjectId[]:
try {
const user = await User.findById(id);
if (!user) {
return res.status(400).json({ success: false, message: 'User not found' });
}
const newProject = await Project.create(req.body);
console.log(newProject);
user.projects.push(newProject._id);
await user.save();
if (!user) {
return res
.status(400)
.json({ success: false, message: 'Project not added' });
}
res.status(200).json({ data: user });
} catch (error) {
res.status(400).json({ success: false, message: error.message });
}
Then, to update a Project you can simply do:
Project.findByIdAndUpdate(
req.body.projectid,
{ $set: { [req.body.property]: req.body.value } },
{ new: true }, // returns updated doc
function (error, success) {
if (error) {
console.log('error');
console.log(error);
} else {
console.log("success");
console.log(success); //Returns null
}
}
);

Related

._doc in Mongoose

I have this js code
app.post('/auth', async (req, res) => {
try {
const user = UserModel.findOne({email: req.body.email}).exec()
if (!user) return res.status(404).json({
message: 'Not find user'
})
const isValidPassword = bcrypt.compare(req.body.password,user._doc.passwordHash)
if (!isValidPassword) return res.status(404).json({
message: 'Incorrect password'
})
}
catch (err) {
console.log(err)
res.status(500).json({
message: 'error'
})
}
})
And I have this Schema
import mongoose from 'mongoose'
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
passwordHash: {
type: String,
required: true
},
telegramUrl: {
type: String,
required: true
},
avatarUrl: String
},
{
timestamps: true
}
)
export default mongoose.model('User', userSchema)
In this line
const isValidPassword = bcrypt.compare(req.body.password,user._doc.passwordHash)
I have error: Cannot read properties of undefined (reading 'passwordHash'). Why am I getting an error? He writes to me that ._doc undefined but why? Help me please
use these two methods in your schema
const bcrypt = require("bcrypt");
// Create Hash Salt Password ..
userSchema.pre("save", async function (next) {
if (!this.isModified("passwordHash")) return next();
this.passwordHash = await bcrypt.hash(this.passwordHash, 12);
next();
});
// Compare Password ...
userSchema.methods.comparePassword = function (passwordHash) {
return bcrypt.compareSync(passwordHash, this.passwordHash);
};
And in your auth code
app.post('/auth', async (req, res) => {
try {
const user = await UserModel.findOne({email: req.body.email}).exec()
if (user) {
res.status(400).json(
{ message: 'User already register'})
}
else{
const newuser = New User({
name: req.body.name,
// also write other schema fields
}
const res = await newuser.save();
console.log(res)
catch (err) {
console.log(err)
res.status(500).json({
message: 'error'
})
}
})
I hope this should resolve your problem

mongoose schema method returning undefined

I want to create a method that validates the user's password by using bcrypt.compare()
here is the code below.
UserSchema.methods.validatePassword = async (data) => {
console.log(this.email); // returns undefined
console.log(this.first_name); // returns undefined
return await bcrypt.compare(data, this.password);
};
here is the UserSchema I created
const UserSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } }
);
when getting this.password in my schema .pre('save', ..) it works but shows undefined when I use schema methods. :(
here is the implementation of the method
const verifySignIn = async (req, res, next) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (!user) {
return res.status(404).json({
status: 'failed',
message: 'User Not found.',
});
}
const isValid = await user.validatePassword(password);
if (!isValid) {
return res.status(401).send({
message: 'Invalid Password!',
data: {
user: null,
},
});
}
next();
} catch (err) {
Server.serverError(res, err);
}
};
In the guide it says:
Do not declare methods using ES6 arrow functions (=>). Arrow functions explicitly prevent binding this, so your method will not have access to the document ...
So in this case, you just need to change UserSchema.methods.validatePassword = async (data) => {... to UserSchema.methods.validatePassword = async function(data) {...

Sequelize update information

I've been struggling with this issue for a day now and can't seem to figure out a way to resolve it. This is the code I'm running
Client side:
const nameInput = document.querySelector("#nameInput");
const urlInput = document.querySelector("#urlInput");
const rowAlert = document.querySelector(".alertAppend");
const divAlert = document.createElement("div");
const nameUpdate = async (e) => {
e.preventDefault();
fetch("/auth/updateName", {
method: 'POST',
headers: {
'Content-Type' : 'application/json'
},
body: JSON.stringify({
name: nameInput,
url: urlInput,
})
})
.then(function (data) {
console.log('Request success: ', data);
})
.catch(function (error) {
console.log('Request failure: ', error);
});
};
submitName.addEventListener("click", nameUpdate);
API:
router.get("/updateName", auth, async (req, res) =>{
try {
const { name, url } = req.body;
const ime = name;
const uid = req.session.passport.user;
db.User.find({ where: { id: uid } })
.on('success', function (user) {
if (user) {
user.update({
name: ime,
webhook: url
})
.success(function () {})
}
})
res.json({ message: url});
} catch (err) {
if (err) res.status(500).json({ message: "Internal Error"})
}
});
For some reason it just runs the select query and never proceeds to update the user.
Chrome console output
Debug console output
Sequelize model in case it helps:
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
},
name: {
type: DataTypes.STRING
}
})
return User;
}
The issue was in the API, it's supposed to be router.post
router.post("/updateName", auth, async (req, res) =>{
const { ime, url } = req.body;
const uid = req.session.passport.user;
console.log(ime);
db.User.findOne({where: {id: uid}})
.then(record => {
let values = {
name: ime,
webhook: url
}
record.update(values).then( updatedRecord => {
console.log(`updated record ${JSON.stringify(updatedRecord,null,2)}`)
res.status(200).json({ message: "success"});
})
}
})
.catch((error) => {
// do seomthing with the error
throw new Error(error)
})
});
You can try the following code
await db.User.update({
name: ime,
webhook: url
}, { where: { id: uid } });
When defining your model I don't see the webhook field

Getting error while saving documents using mongoose in node express.js project

I am getting error even after saving document to the mongodb using mongoose in node express.js project.
Here's my code:
exports.storeJob = async (req, res, next) => {
const { name, email, password, title, location, descriptionUrl, tags, company, companyLogo, coupon, showLogo, highlightWithColor, customColor, makeSticky } = req.body;
const { error } = userRegisterValidation(req.body);
if (error) return res.status(400).json({ success: false, message: error.details[0].message });
const emailExists = await User.findOne({ email: email });
if (emailExists) return res.status(400).json({ success: false, message: "User already exits. Please Login" });
const salt = await bcrypt.genSalt(10);
const hashPassword = await bcrypt.hash(password, salt);
const user = new User({
name: name,
email: email,
password: hashPassword
});
// try{
const savedUser = await user.save();
const job = new Job({
title,
location,
descriptionUrl,
tags,
company,
companyLogo,
coupon,
showLogo,
highlightWithColor,
customColor,
makeSticky,
status: 'open',
user: savedUser
});
try {
const createdJob = await job.save();
// try {
user.jobs.push(createdJob);
user.save();
res.status(201).json({ success: true, data: savedUser });
// } catch {
// res.status(400).json({ success: false, message: "Some error occured" });
// }
} catch (err) {
res.status(400).json({ success: false, message: "Error while creating job.", error: err });
}
// } catch(err) {
// res.status(400).json({ success: false, message: "Error while creating user" });
// }
}
I have 2 questions:
I have register method in userController. Is there any way to use that method inside storeJob method?
In the above code even after saving user and job to the database and linking them api response is
{ success: false, message: "Error while creating job.", error: {} }
user.jobs.push(createdJob);
user.save();
In that case, this two lines creates a new user, because user defines the User schema.
Instead of these two lines try this
var push = {
jobs:createdJob
}
var update = {
"$addToSet":push
}
await User.findOneAndUpdate({ "_id": savedUser._id },update).exec();
Hope it will work fine to you. Thanks

How to create complex nested documents using mongoose?

I want to create a complex nested document which can store values like this
category: {
"fish": ["Sardines", "Goldfish"],
"dogs": ["German Shepherd", "Dobberman"]
}
Here's what I tried
export const CategorySchema = new mongoose.Schema(
{
category: {
type: Map,
of: [String],
},
},
{ timestamps: true }
);
I passed data like this (from console)
this is how the passed data looks like
Nothing is being created in the database. Also no error.
export default async (req, res) => {
const { method } = req;
switch (method) {
case "GET":
try {
const categories = await Category.find({});
res.json({ success: true, data: categories });
} catch (error) {
res.json({ success: false });
}
break;
case "POST":
try {
let data = req.body;
data = JSON.parse(data);
const category = new Category(data);
const doc = await category.save();
console.log("Doc from Categories API", doc);
res.json({ success: true, data: doc });
} catch (error) {
res.json({ success: false });
}
break;
default:
res.status(400).json({ success: false });
break;
}
};
Can anyone advise me, please?

Categories

Resources