MongoDB - cannot remove multiple record from the same session - node.js

I am trying to delete two record from different database (practice log, student) within the same session, the problem is the practice log record is found successfully and shown in console.log(deletePracticeLog), but it cannot be remove from the database. anyone know what have I done wrong? I am very confused.
const deleteStudentById = async (req, res, next) => {
const sid = req.params.sid;
let deleteStudent;
let deletePracticeLog;
try {
deleteStudent = await Students.findById(sid).populate("teacher");
} catch (err) {
const error = new HttpError("Delete failed, please try again.", 500);
return next(error);
}
try {
deletePracticeLog = await PracticeLog.find({ student: sid }).exec();
console.log(deletePracticeLog); // **<-this correctly show the record needed to be deleted, it's a object within an array**
} catch (err) {
const error = new HttpError("Cannot delete, please try again.", 500);
return next(error);
}
if (!deleteStudent) {
const error = new HttpError("Could not find student for this id.", 404);
return next(error);
}
try {
const sess = await mongoose.startSession();
sess.startTransaction();
await deleteStudent.remove({ session: sess }); //delete the student from student database
deleteStudent.teacher.students.pull(deleteStudent); //delete the student id from teacher database
await deleteStudent.teacher.save({ session: sess });
await deletePracticeLog.remove({ session: sess }); // **<-this line caused error, when this line is removed, other code run successfully and other record are deleted.**
await sess.commitTransaction();
} catch (err) {
const error = new HttpError("Cannot delete student with provided id.", 500);
return next(error);
}
res.status(200).json({ message: "Deleted student." });
};

when you are using await, you don't need to exec()
delete your exec() from
deletePracticeLog = await PracticeLog.find({ student: sid }).exec();
change to
deletePracticeLog = await PracticeLog.find({ student: sid })
beacuse you use exec(), use this code for remove data
await deletePracticeLog[0].remove({ session: sess })
if the result of PracticeLog.find({ student: sid }) is one Array and you want to delete all of them, you have to use deleteMany like this:
PracticeLog.deleteMany({ student: sid }).session(sess)
it's worked for me

here is the Mongoose schema:
const mongoose = require("mongoose");
const studentsSchema = new mongoose.Schema({
name: { type: String, require: true },
email: { type: String, require: true },
contact: { type: String, require: true },
teacher: { type: mongoose.Types.ObjectId, require: true, ref: "teachers" },
practiceLogs: [
{ type: mongoose.Types.ObjectId, require: true, ref: "practice_logs" },
],
});
module.exports = mongoose.model("students", studentsSchema);
const practiceLogSchema = new mongoose.Schema({
repertoire: { type: String, require: true },
student: { type: mongoose.Types.ObjectId, require: true, ref: "students" },
teacher: { type: mongoose.Types.ObjectId, require: true, ref: "teachers" },
});
module.exports = mongoose.model("practice_logs", practiceLogSchema);
here is the mongoose.connect():
const url =
"myURL";
mongoose
.connect(url, { useUnifiedTopology: true, useNewUrlParser: true })
.then(() => {
app.listen(5000);
console.log("connected.");
})
.catch((err) => {
console.log(err);
});

Related

insert an array of object ids mongoose nodejs

I want to be able to post several Object id's into the array,, I have two models control and subcontrol is referenced in the control model as an array. The idea is a control number might have sub control number under it
My post method:
router.post(
'/add',
auth,
role.checkRole(role.ROLES.Admin, role.ROLES.Regulator),
async (req, res) => {
try {
const subControls = []
for(const subControl of req.body.subControls){
const tableSubControl ={
subControlNo: subControl.subControlNo
};
const newSubControls = new SubControl(tableSubControl);
const subControlDoc = await newSubControls.save();
const control = new Control({...req.body, subControl: subControlDoc._id});
const savedControl = await control.save();
subControls.push(newSubControls)
}
res.status(200).json({
success: true,
message: `Control has been added successfully!`,
control: savedControl
});
} catch (error) {
return res.status(400).json({
error
// error: 'Your request could not be processed. Please try again.'
});
}
}
);
Control Schema:
const ControlSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
mainControl: {
type: String
},
subControl: [
{
type: Mongoose.Schema.Types.ObjectId,
ref: 'SubControl'
}
],
controlDescription: {
type: String,
trim: true
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('Control', ControlSchema);
My subControl schema:
const SubControlSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
subControlNo: {
type: String
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('SubControl', SubControlSchema);
Postman:
{
"mainControl": "1-1",
"subControls":
[
{
"subControlNo": "1-2-1"
},
{
"subControlNo": "1-2-2"
}
],
"controlDescription": "controldescription"
}
I'm not getting any clear error,, any idea what I need to do?
Well I am guessing when you create new Control object from req.body then don't set subControl:subcontrol._id there. Instead a object subcontrol should be assigned to Control object.
const subControlDoc = await newSubControls.save();
const control = new Control({...req.body});
control.subcontrol = subControlDoc
const subControlDoc = await newSubControls.save();
const savedControl = await control.save();
subControls.push(newSubControls)
We can manage this using Population :
Consider the following changes in the code, I have tried adding comments too.
router.post(
"/add",
auth,
role.checkRole(role.ROLES.Admin, role.ROLES.Regulator),
async (req, res) => {
try {
//first create a single control document
const control = new Control({
mainControl: req.body.mainControl,
controlDescription: req.body.controlDescription,
});
//nitpick: the for loop below can be made async.
for (const subControl of req.body.subControls) {
const tableSubControl = {
subControlNo: subControl.subControlNo,
};
const newSubControls = new SubControl(tableSubControl);
const subControlDoc = await newSubControls.save();
//save the looped subControl document
control.subControls.push(subControlDoc);
//push the association to the control document.
}
//save the control document, moved outside the loop
const savedControl = await control.save();
res.status(200).json({
success: true,
message: `Control has been added successfully!`,
control: savedControl,
});
} catch (error) {
return res.status(400).json({
error,
// error: 'Your request could not be processed. Please try again.'
});
}
}
);

How to delete comment that is inside of Post schema?

I'm working on social network app where user can make post and comment. I'm trying to delete comment that is inside of a post. I work with MERN (mongoose, express, react, nodejs). I can successfully delete post, but don't know how to delete its comment.
This is my Mongo connection:
const db = config.get('mongoURI') mongoose.connect(db,{useNewUrlParser: true,useUnifiedTopology: true})
.then(() => console.log('Connected to MongoDB.'))
.catch(err => console.log('Fail to connect.', err))
this is Post Schema
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const PostSchema = new Schema({
userID: {
type: Schema.Types.ObjectId,
ref: 'user'
},
content: {
type: String,
required: true
},
registration_date: {
type: Date,
default: Date.now
},
likes: [
{
type: Schema.Types.ObjectId,
ref: "user"
}
],
comments: [
{
text: String,
userID: {
type: Schema.Types.ObjectId,
ref: 'user'
}
}
]
})
module.exports = User = mongoose.model('posts', PostSchema)
and here is where i tried to delete it:
router.delete("/comment/:postId/:commentId", auth, function (req, res) {
Post.findByIdAndUpdate(
(req.params.postId),
{ $pull: { comments: req.params.commentId } },
{ new: true }
)
.then(post => console.log(post)
.then(() => {
res.json({ success_delete: true })
})
.catch(() => res.json({ success_delete: false })))
});
Well, I think you are creating an app named DevConnector. So I wrote code for the same in the past.
router.delete('/comment/:id/:comment_id', auth, async (req, res) => {
try {
const post = await Post.findById(req.params.id);
// Pull out comment
const comment = post.comments.find(
comment => comment.id === req.params.comment_id
);
// Make sure comment exists
if (!comment) {
return res.status(404).json({ msg: 'Comment does not exist' });
}
// Check user
if (comment.user.toString() !== req.user.id) {
return res.status(401).json({ msg: 'User not authorized' });
}
// Get remove index
const removeIndex = post.comments
.map(comment => comment.user.toString())
.indexOf(req.user.id);
post.comments.splice(removeIndex, 1);
await post.save();
res.json(post.comments);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});

Trying to add new user to database (MongoDB) getting this error

I am trying to save a user to MongoDB database using post request as follow, but I got the error TypeError: User is not a function. I can't figure out anything wrong with it. The output says that error is present on the line " const user = new User(req.body);"
postman output nodejs output
is my userschema wrong or the export method is wrong.
const User = require("../models/user");
exports.signup = (req, res) => {
const user = new User(req.body);
user.save((err, user) => {
if (err) {
return res.status(400).json({
err: "NOT able to save user in DB"
});
}
res.json({
name: user.name,
email: user.email,
id: user._id
});
});
};
exports.signout = (req, res) => {
res.json({
message: "User signout"
});
};
//user schema
var mongoose = require("mongoose");
const crypto = require('crypto');
const uuidv1 = require('uuid/v1');
var userSchema = new mongoose.Schema({
name:{
type:String,
required:true,
maxlenght:32,
trim: true
},
lastname:{
type: String,
maxlenght:32,
trim: true
},
email:{
type: String,
trim: true,
required: true,
unique:true
},
userinfo:{
type:String,
trim:true
},
encry_password:{
type:String,
required: true,
},
salt:String,
role:{
type:Number,
default:0,
},
purchases :{
type:Array,
default:[]
}
} ,{ timestamps: true } );
userSchema.virtual("password")
.set(function(password){
this._password = password;
this.salt = uuidv1();
this.encry_password = this.securePassword(password);
})
.get(function(){
return this._password;
})
userSchema.methods = {
authenticate: function(plainpassword){
return this.securePassword(plainpassword) === this.encry_password;
},
securePassword: function (plainpassword){
if(!plainpassword) return "";
try {
return crypto.createHmac('sha256',this.salt)
.update(plainpassword)
.digest('hex');
} catch (err) {
return "";
}
}
};
module.export = mongoose.model("User",userSchema)
Here is the issue with the code.
Line 1: const user = new User(req.body);
Line 2: user.save((err, user) => {
JS is now confused about user which is a constant variable to that of the user which is the return value of the save action.
So, to get rid of this, rename the return value of save action to something else like responseUserObj. Thus your above two lines of code should now be
const user = new User(req.body);
user.save((err, responseUserObj) => {
Happy coding.

Updating DB Shema in Express JS with Mongoose library

I have created a Mongo DB schema with Mongoose in Express.js and I am building the REST API. However when I try to update existing records the values that I do not update from the schema automatically become null. I understand why this happens just not sure exactly how it should be coded.
This is the route:
router.patch("/:projectId", async (req, res) => {
try {
const updatedProject = await Project.updateOne(
{ _id: req.params.projectId },
{
$set: {
title: req.body.title,
project_alias: req.body.project_alias,
description: req.body.description
}
}
);
res.json(updatedProject);
} catch (err) {
res.json({ message: err });
}
});
also here is the schema:
const ProjectsSchema = mongoose.Schema({
title: {
type: String,
required: true,
unique: true
},
project_alias: {
type: String,
unique: true,
required: true
},
description: String,
allowed_hours: Number,
hours_recorded: {
type: Number,
default: 0
},
date_added: {
type: Date,
default: Date.now
}
});
My problem is that when I want to update just the title:
{
"title" : "Title Updated33"
}
description and alias become null. Should I implement a check?
Just use req.body for the update object like this:
router.patch("/:projectId", async (req, res) => {
try {
const updatedProject = await Project.updateOne(
{ _id: req.params.projectId },
req.body
);
res.json(updatedProject);
} catch (err) {
res.json({ message: err });
}
});
Or even better, create a helper function like this so that we can exclude the fields in the body that doesn't exist in the model:
const filterObj = (obj, ...allowedFields) => {
const newObj = {};
Object.keys(obj).forEach(el => {
if (allowedFields.includes(el)) newObj[el] = obj[el];
});
return newObj;
};
router.patch("/:projectId", async (req, res) => {
const filteredBody = filterObj(
req.body,
"title",
"project_alias",
"description",
"allowed_hours",
"hours_recorded"
);
try {
const updatedProject = await Project.updateOne(
{ _id: req.params.projectId },
filteredBody
);
res.json(updatedProject);
} catch (err) {
res.json({ message: err });
}
});

Updating a field in MondoDB

I am writing a multi-user online dictionary. I want to implement a leadership board, e.i. "score" attribute increases, as soon as a user adds a word. I have a rough idea on how to do it, and tried one solution, however it does not work. Could you please guide me?
Word API route
const express = require('express');
const router = express.Router();
const Word = require('../../models/Word');
const User = require('../../models/User');
const validateWordInput = require('../../validation/word');
const passport = require('passport');
// #route POST api/words
// #desc Add words to profile
// #access Private
router.post(
'/',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateWordInput(req.body);
// Check validation
if (!isValid) {
// Return any errors
return res.status(400).json(errors);
}
Word.find({}).then(word => {
if (
word.filter(
wrd =>
wrd.ugrWordCyr.toString().toLowerCase() ===
req.body.ugrWordCyr.toLowerCase()
).length !== 0
) {
return res
.status(404)
.json({ wordalreadyexists: 'Word already exists' });
} else {
const newWord = new Word({
user: req.user.id,
ugrWordCyr: req.body.ugrWordCyr,
rusTranslation: req.body.rusTranslation,
example: req.body.example,
exampleTranslation: req.body.exampleTranslation,
origin: req.body.origin,
sphere: req.body.sphere,
lexis: req.body.lexis,
grammar: req.body.grammar,
partOfSpeech: req.body.partOfSpeech,
style: req.body.style
});
newWord.save().then(word => res.json(word));
User.update(
{ _id: '5cf0cb78b3105d1ba8e30331' },
{ $inc: { score: 1 } }
);
}
});
}
);
User model
This is where a score attribute is located
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create schema
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
score: {
type: Number,
default: 0
},
avatar: {
type: String
},
date: {
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model('users', userSchema);
After successfully saving the word, we should update the user count
To update the respective user's score you can do the following:
newWord.save().then((word) => {
//now update user model
User.findOne({ _id: req.user.id }) <-- or an id you would like
.then((foundUser) => {
foundUser.score = foundUser.score + 1
foundUser.save()
.then((savedUser) => {
res.json({ word, savedUser })
})
.catch((err) => {
return res.status(400).json({ error: "could not add score"})
})
})
.catch((err) => {
return res.status(400).json({ error: "could not find user"})
})
})

Resources