Mongoose delete document field instead of whole document - node.js

Instead of deleting the otp attribute the Query is deleting whole document
Schema. I want to delete otp after verified
const LoginSchema = new mongoose.Schema({
email: String,
password:String,
verified: Boolean,
otp:Number
});
const Model = mongoose.model("Users", LoginSchema);
Query in Post Request
app.post("/verify/:id", async(req, res) => {
var User = await Model.findOne({ _id: req.params.id, otp:req.body.otp });
Model.deleteOne({ otp: User.otp }, (err, res) => {
if (err) console.log(err)
else console.log("succesful");
})
})

That because you deleting a document when you using 'deleteOne'. You need to $unset the otp attribute.
Try this one:
Model.findOneAndUpdate({ otp: User.otp }, {$unset: {otp: 1 }},(err, res) => {
if (err) console.log(err)
else console.log("succesful");
})
})

Related

why is findOneAndUpdate not updating in the DB?

I'm creating a project using the mern stack. I'm trying to update a project from my frontend to my backend. When I update it it will return success but when I check the database nothing is updated? I'm trying to update the product with the prodID that is entered in the frontend
This is my post route
router.post("/updateStock", (req, res) => {
const prodID = req.body.prodID
const product = new Product(req.body)
Product.findOneAndUpdate(prodID, { new: true }, {returnOriginal: false}, function(err, products) {
if (err) {
console.log("err", err);
res.status(500).send(err);
} else {
console.log("success");
res.send(product);
}
});
});
This is my schema
const mongoose = require("mongoose");
const Product = mongoose.model(
"Product",
new mongoose.Schema({
title: String,
manufacturer: String,
price: String,
catergory: String,
quantity: String,
prodID: String,
images: Array
})
);
module.exports = Product;
Following Mongoose Docs here
1st:
You added filter as a string, however it should've been an object like below
const filter = {prodID: req.body.prodID}
2nd:
no need to instantiate the Product schema, use the update object
const update = req.body
3rd:
You used the same option, also new:true took the place of the update object
returnOriginal: false is equivalent to new: true
4th:
Use promise not callbacks, however you have a typo in the callback you called products and you sent product
Product.findOneAndUpdate(filter, update, {new: true}).then((product) => {
console.log("success");
res.send(product);
}).catch(err => {
console.log("err", err);
res.status(500).send(err);
})
You are not passing the new updated body to findOneAndUpdate. The findOneAndUpdate is expecting db.collection.findOneAndUpdate( filter, update, options ). The code should be like this:-
router.post("/updateStock", (req, res) => {
const product = new Product(req.body);
const filter = {prodID: req.body.prodID}
Product.findOneAndUpdate(filter, product, { new: true, returnOriginal: false}, function(err, products) {
if (err) {
console.log("err", err);
res.status(500).send(err);
} else {
console.log("success");
res.send(product);
}
});
Follow this Schema =>
db.collection.findOneAndUpdate(filter, update, options)
Pass Update object

.populate() returning empty array?

I'm working on project and I am trying to get the users reviews to display on the page. However whenever I run my application it returns an empty array and I'm not sure why, I have no issue with the getReviews function as it returns everything correctly, but getUserReviews just returns an empty array with no error. I've tried multiple methods and just can't seem to get it
Review Model
const mongoose = require("mongoose");
const Review = mongoose.model(
"Review",
new mongoose.Schema({
movieId: String,
reviewId: String,
content: String,
sentimentScore: String,
author: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
],
reponseTo: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
]
})
);
module.exports = Review;
User Model
const mongoose = require("mongoose");
const User = mongoose.model(
"User",
new mongoose.Schema({
username: String,
email: String,
password: String,
roles: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Role"
}
]
})
);
module.exports = User;
Review Routes
const express = require('express');
const router = express.Router();
const {authJwt} = require("../middlewares");
const Review = require("../models/review.model")
router.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
router.post("/addReview", [authJwt.verifyToken], (req, res) => {
const review = new Review(req.body)
review.save((err, review) => {
if(err) return res.json({success:false, err})
Review.find({'_id': review._id})
.populate('author')
.exec((err, result) => {
if(err) return res.json({success: false, err})
return res.status(200).json({success: true, result})
})
})
})
router.post("/getReviews", [authJwt.verifyToken], (req, res) => {
Review.find({"movieId": req.body.data})
// console.log("ID ", req.body.data)
.populate('author')
.exec((err, reviews) => {
if(err) return res.status(400).send(err)
res.status(200).json({success: true, reviews})
})
})
router.post("/getUserReviews", [authJwt.verifyToken], (req, res) => {
Review.find({"userId": req.body.data})
.populate({
path: 'author.user',
model: 'Review'})
.exec((err, reviews) => {
if(err) return res.status(400).send(err)
res.status(200).json({success: true, reviews})
})
})
You try to query Review collection with userId field, and Review collection does not have userId field in its model definition.
Maybe you wanted to query author array? In that case, try this:
router.post("/getUserReviews", [authJwt.verifyToken], (req, res) => {
Review.find({ author: req.body.data })
.populate('author')
.exec((err, reviews) => {
if(err) return res.status(400).send(err)
res.status(200).json({success: true, reviews})
})
})

Mongoose Update

I can't update the status field in the document. can somebody please help? Thanks!
changing the default value is the issue.
i want the admin to be able to update the status of the applicant from pending to whatever option is chosen from the select options in the ejs form
this is in the controller:
exports.postName = async(req, res, next)=>{
const name = req.body.name;
console.log(name)
const pupil = new Pupil({
name: name,
});
await pupil.save().then(updates=>{
console.log(updates)
res.redirect('/name');
}).catch(error=>{
console.log(error);
});
}
this also is from the controller:
exports.postUpdatePupil = async (req, res, next)=>{
const status = req.body.status
Pupil.updateOne(
{_id: req.params.id},
{
$set: {'Pupil.$.status': status}
},(err, result)=>{
if(err){
return next(err)
}console.log(result);
}
)
this is the model:
const UpdateSchema = new mongoose.Schema({
name: String,
status: {type: String, default: "Pending"}
});
const update = mongoose.model('update', UpdateSchema);
module.exports = update;
Model name should be "Pupil" and not "Update".
const PupilSchema = new mongoose.Schema({
name: String,
status: {type: String, default: "Pending"}
});
module.exports = mongoose.model('Pupil', PupilSchema);
And you update like:
Pupil.updateOne(
{ _id: req.params.id },
status // same as { status: status }
(err, result) => {
if (err) return next(err);
console.log(result);
},
);

Finding _id of user just created in a route

I am making a rapid prototype of a MERN application, I have a backend question: I have a User model and a Category model, when a user sign up, I need to fill the category model with some basic informations exported from an object the user can edit later. I would like to assign every category a ref to the just created account id. The problem is I don't understand how I can retrieve the just created user id.
Here is my route (yes is not refactored, sorry):
// #route POST api/users/register
// #desc Register user
// #access Public
router.post('/register', (req, res)=>{
//Validate req.body
const {errors, isValid} = validateRegisterInput(req.body);
//Check validation
if(!isValid){
return res.status(400).json(errors); 
}
User.findOne({ email: req.body.email })
.then(user => {
if(user){
errors.email = 'Email already exists';
return res.status(400).json(errors)
} else {
const avatar = gravatar.url(req.body.email, {
s: '200', //Size
r: 'pg', //Rating
d: 'mm' //Default
});
const newUser = new User({
name: req.body.name,
email: req.body.email,
avatar,
password: req.body.password
});
//Hash the password and save
bcrypt.genSalt(10, (err, salt)=>{
bcrypt.hash(newUser.password, salt, (err, hash)=>{
if(err) throw err;
newUser.password = hash;
newUser.save()
.then(user => res.json(user))
.catch(err => console.log(err))
})
});
//Fill user categories with default categories
defaultIcons.map((icon) => {
const newCategory = new Category ({
name: icon.name,
type: icon.type,
icon: icon.icon
})
newCategory.save();
});
}
})
});
And this is the Category Schema:
//Create Schema
const CategorySchema = new Schema({
//Every account is associated with actual User schema by id
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
name: {
type: String,
required: true
},
type: {
type: String,
required: true
},
icon: {
type: String,
required: true
}
})
What would be the best solution for this? Is it better to have a separated schema for the categories like I am doing or I can implement an array of objects field in the User schema?
The part where you do
if(err) throw err;
newUser.password = hash;
newUser.save()
.then(user => res.json(user))
.catch(err => console.log(err))
you have the user in your resolved promise. you can just
const newCreatedUserID = user._id
to get the just created user ID.

MongoDB/Mongoose search and create of specific sub documents

I'm building a REST API in nodejs/express/mongodb/mongoose. I've started using MongoDB/Mongoose recently and I still have some doubts.
What I'm trying to achieve is to access a specific user bag (a user can have multiple bags) and also I want to be able to add to that bags participants/payers. (a user bag can have multiple participants/payers)
My mongoose user modal contains the rest of the schemas. I created a schema for each one because I believe it would be easier to find a given bag or participant directly because of the ObjectId (not sure if this is correct).
Mongoose Modal/Schemas:
const PayerSchema = new Schema({
name: {
type: String
},
amount: {
type: Number
}
});
const BagSchema = new Schema({
name: {
type: String
},
type: {
type: String
},
payers: [PayerSchema]
});
const UserSchema = new Schema({
name: {
type: String,
required: [true, 'User name field is required']
},
bags: [BagSchema]
});
module.exports = mongoose.model('User', UserSchema);
I was able to create the CRUD controller methods for a new user, but I still not sure on:
Creating a new bag for a specific user (I was able to do this but not sure if it's the right way)
Creating a new participant in a specific bag for a specific user. (addPayer method is wrong need help here)
Check out my controller user/bags/participants methods:
const User = require('../models/userModel');
getAllUserBags: (req, res, next) => {
User.findById({ _id: req.params.id }).then((user) => {
res.send(user.bags);
})
.catch(next);
},
getOneUserBag: (req, res, next) => {
console.log(req.params.bagid);
User.find({ 'bags._id': req.params.bagid}, {"bags.$" : 1}).then((obj) => {
res.send(obj);
})
.catch(next);
},
createBag: (req, res, next) => {
let bag = req.body.bag;
User.findOneAndUpdate(
{_id: req.body.id},
{$push: {bags: bag}
}).then(() => {
//Unnecessary - just to return update user with new bag.
User.findOne({_id: req.body.id}).then((user) => {
res.send(user);
})
}).catch(next);
},
addPayer: (req, res, next) => {
let payer = req.body.payer;
User.find(
{'bags._id': req.params.bagid},
{"bags.$" : 1},
{$push: {payers: payer}
}).then((obj) => {
console.log(obj);
//Unnecessary - just to return update user with new bag.
// User.findOne({_id: req.body.id}).then((user) => {
// res.send(user);
// })
}).catch(next);
}
Thanks for the help
Base on what we discuss, your User schema is good enough for your requirements, as long as making sure that one User document does not exceed the 16MB limit of MongoDB document.
Creating a new bag for a specific user (I was able to do this but not sure if it's the right way)
Yours is fine. However, there are some improvements:
createBag: (req, res, next) => {
User.findByIdAndUpdate(req.body.id, {
$push: { bags: req.body.bag }
}, {
new: true // this will make the query getting the updated document
})
.then(user => {
res.json(user);
})
.catch(next);
})
Creating a new participant in a specific bag for a specific user. (addPayer method is wrong need help here)
Since you decided to nest the 'bags', the bag.id might be duplicated among User documents. See this to understand the possibility. Thus, I recommend using an userId along with bagId:
getOneUserBag: (req, res, next) => {
User.findOne({
_id: req.params.userId,
bags._id: req.params.bagId
})
.then(user => {
if (!user) res.status(404).end();
let bag = user.bags.id(req.params.bagId);
res.json(bag);
})
.catch(next);
}
addPayer: (req, res, next) => {
User.findOneAndUpdate({
_id: req.params.userId,
bags: $elemMatch: {
_id: req.params.bagId
}
}, {
$push: { 'bags.$.payers': req.body.payer } // Use 'positional $' operator along with $elemMatch in the query to update only the matched bag
}, {
new: true // Do not forget the 'new' options to get the updated document
})
.then(user => {
if (!user) res.status(404).end();
res.json(user);
})
.catch(next);
}
and in the router
router.get('/users/:userId/bags/:bagId', getOneUserBag);
router.post('/users/:userId/bags/:bagId/payers', addPayer);
In the getAllUserBags(), you use the wrong syntax for User.findById():
getAllUserBags: (req, res, next) => {
User.findById(req.params.id) // Not { _id: req.params.id }
.then((user) => {
res.json(user.bags);
})
.catch(next);
}

Resources