Mongoose deleting subdocuments from arrays using $pull - arrays

Hi I have a comment and post model that has a one-to-many relationship. It successfully creates a comments array and pushes the object id correctly, but when I try to delete a comment using $pull It doesn't delete or show errors just returns the object back unchanged and also I'm not sure how to update subdocuments either , it doesn't work
In comment.js
const mongoose = require("mongoose");
const AutoIncrement = require('mongoose-sequence')(mongoose);
const CommentSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
content:{
type: String,
required: true
},
createdAt:{
type: Date,
default: new Date()
},
})
CommentSchema.plugin(AutoIncrement, {inc_field: 'commentID'});
module.exports = mongoose.model("Comment", CommentSchema);
In post.js
const mongoose = require("mongoose");
const AutoIncrement = require('mongoose-sequence')(mongoose);
const PostsSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
createdAt:{
type: Date,
default: new Date()
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
PostsSchema.plugin(AutoIncrement, {inc_field: 'postID'});
module.exports = mongoose.model("Post", PostsSchema);
For the routes
router.post("/:id/comments", CommentController.createComment)
router.get("/:id/comments", CommentController.viewComments)
router.delete("/:id/comments/:commentID", CommentController.deleteComment)
router.patch("/:id/comments/:commentID", CommentController.updateComment)
And the controllers
const commentsModel = require("../models/comments")
const PostsModel = require("../models/posts");
const mongoose = require("mongoose")
const objID = mongoose.Types.ObjectId;
async function createComment(req, res){
try{
const comment = await commentsModel.create(req.body)
const data = await PostsModel.findOneAndUpdate(
{postID: req.params.id},
{$push: {comments: objID(comment._id)}},
{ new: true, useFindAndModify: false }
)
res.status(200).json({data})
}catch (e) {
res.status(404).json(e.message)
}
}
async function viewComments(req, res){
try{
const data = await PostsModel.findOne({postID: req.params.id}).populate("comments");
if(!data){
return res.status(404).json({msg: "No data found"});
}
res.status(200).json({comments: data.comments});
}catch (e) {
res.status(500).json(e.message);
}
}
async function deleteComment(req, res){
try{
const post = await PostsModel.update({ postID: req.params.id },
{
'$pull': {
comments: {
_id: objID(req.params.commentID)
}
}
}, {new: true});
if (!post){
return res.status(404).json({msg: "No post found"});
}
res.status(200).json({post});
}catch (e) {
res.status(500).json(e.message);
}
}
async function updateComment(req, res){
try{
const post = await PostsModel.findOneAndUpdate(
{"postID": req.params.id, "comments._id": objID(req.params.commentID)},
{
"$set":{
"comments.$.username": req.body.username,
"comments.$.content": req.body.content
}
}
)
if (!post){
return res.status(404).json({msg: "No post found"});
}
res.status(200).json({post});
}catch (e) {
res.status(500).json(e.message);
}
}

Related

Document not creating using MongoDB Session

I am trying to implement MongoDB session but not able to achieve it because of less references available on the internet. Also when I run the below code it stuck at the create user method inside try catch block and because of that it is not saving a document inside collection. Help with this.
const mongoose = require("mongoose");
// Declare the Schema of the Mongo model
const userSchema = new mongoose.Schema({
userName: {
type: String,
required: true,
},
});
// Declare the Schema of the Mongo model
const productSchema = new mongoose.Schema({
productName: {
type: String,
required: true,
},
});
//Export the model
const UserModel = mongoose.model("User", userSchema);
const ProductModel = mongoose.model(`Product`, productSchema);
(async () => {
// Connecting
try {
await mongoose.connect(`mongodb://127.0.0.1:27017/Mongo_Transaction`, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log(`Connected`);
// Initiating Mongodb Session
const session = await mongoose.startSession();
session.startTransaction();
try {
// Creating User
const user = await UserModel.create(
[
{
userName: "Bhavesh",
},
],
{ session: session }
);
console.log(user);
// Creating Product
const product = await ProductModel.create(
[
{
productName: "Rahul",
},
],
{ session }
);
// Commit Session
await session.commitTransaction();
session.endSession();
console.log(`completed`);
} catch (err) {
await session.abortTransaction();
session.endSession();
}
} catch (err) {
console.log(err.stack);
}
})();

How to make the mongoose update array work properly?

this is my schema.it has a user field and an array of object which contains a text message and direction will hold a either sent or received value.
const MessageSchema = new mongoose.Schema({
user:{type: String,required: true},
textArray:[
{
text:{type: String,required: true},
direction:{ type: String,required: true }
}]
});
This is the router code.btw should there be double quotes on the keys in var textmessage?
router.post('/',async(req,res)=>{
const {user,text,direction}=req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
var textmessage={
"text":text,
"direction":direction
}
const doc = await Message.updateOne(
{ user: user },
{ $set: { user:user,$push:{textArray:textmessage}} },
{ upsert: true },
function(err,result){
if (err){
console.log(err);
}
else{
console.log(result)
res.send(result)
}// Make this update into an upsert
});
})
this is how it showsup in mongodb.the user is working ok but when i click theres is nothing under textArray.What am I doing wrong exactly?
you are using await with callback function , which is wrong :
const filter = { user: user };
const update = { $push:{textArray:textmessage}};
let doc = await Message.findOneAndUpdate(filter, update, {
new: true,
upsert: true
});
console.log('doc=',doc);

Update query timing out after 10 seconds despite successfully submitting in React app

I have the following Schema:
const SubmitDebtSchema = new Schema ({
balance: [{
balanceDate: Date,
newBalance: Number
}]
});
And I have the following function which appends new objects into the array:
module.exports = async (req, res) => {
let newDateObject = {
balanceDate: req.body.balanceDate,
newBalance: req.body.newBalance
};
await SubmitDebt.findOneAndUpdate(
{ _id: req.query.id },
{ $push: { balance: newDateObject } },
{new: true, useFindAndModify: false}
);
};
However, despite the database successfully updating, it times out after 10 seconds with the following error message:
2020-11-23T16:51:57.138Z 25fa69c2-91a1-4a74-8034-af132d4d8eb3 Task
timed out after 10.01 seconds
Does anyone have any feedback for how to resolve this? It also doesn't push to my 'dashboard' upon successful submission.
Here's my Axios front-end call:
onSubmit = async (e) => {
e.preventDefault();
let newBalanceDate = new Date();
this.calculateUpdatedBalance()
await axios.post("/api/edit/editDebtBalance",
{
balanceDate: newBalanceDate,
newBalance: this.calculateUpdatedBalance(),
},
{
params: {
id: this.props.match.params.id
}
}
)
this.props.history.push('/dashboard');
}
EDIT: My full serverless function file:
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const SubmitDebt = require("../submit/submitDebtSchema");
require("dotenv").config();
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
mongoose.connect(process.env.MONGO_URI);
module.exports = async (req, res) => {
let newDateObject = {
balanceDate: req.body.balanceDate,
newBalance: req.body.newBalance
};
return await SubmitDebt.findOneAndUpdate(
{ _id: req.query.id },
{ $push: { balance: newDateObject } },
{new: true, useFindAndModify: false}
);
};
How about you try returning the value?
A lambda function stops only when you return something, try this.
return await SubmitDebt.findOneAndUpdate(
{ _id: req.query.id },
{ $push: { balance: newDateObject } },
{new: true, useFindAndModify: false}
);
Notice that I only added return before the await statement, that's it.
Edit: Ahh.... I think this should work
let response = await SubmitDebt.findOneAndUpdate(
{ _id: req.query.id },
{ $push: { balance: newDateObject } },
{new: true, useFindAndModify: false}
);
return res.send(response)
since you're using an express app. I'm sorry I overlooked it previously.

TypeError: Cannot read property 'upvotes' of null

I am working on basic Stack Overflow clone using Node Js and MongoDB. When I was testing upvote I fall in error TypeError: Cannot read property 'upvotes' of null. I tried everything on the internet but didn't get anything working. I am getting an error for the code below
if (question.upvotes.filter(upvote => upvote.user.toString() === req.user.id.toString()).length > 0)
You can see the above syntax in API Code for upvote route.
This is my model for upvotes:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const QuestionSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "myPerson"
},
textone: {
type: String,
required: true
},
texttwo: {
type: String,
required: true
},
name: {
type: String
},
upvotes: [
{
user: {
type: Schema.Types.ObjectId,
ref: "myPerson"
}
}
],
answers: [
{
user: {
type: Schema.Types.ObjectId,
ref: "myPerson"
},
text: {
type: String,
required: true
},
name: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Question = mongoose.model("myQuestion", QuestionSchema);
Below i have pasted my API for upvote
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const passport = require("passport");
const jwt = require("../../strategies/jsonwtStrategy")(passport);
//Load person model
const Person = require("../../models/Person");
//Load profile model
const Profile = require("../../models/Profile");
//Load Question model
const Question = require("../../models/Question");
//#type GET
//#route /api/questions
// #desc route for getting questions
// #access PUBLIC
router.get("/", (req, res) => {
Question.find()
.sort("-date")
.then(questions => res.json(questions))
.catch(err => res.json({ noquestions: "No questions to display" }));
});
//#type POST
//#route /api/questions/
// #desc route for submitting question
// #access PRIVATE
router.post(
"/",
passport.authenticate("jwt", { session: false }),
(req, res) => {
const newQuestion = new Question({
textone: req.body.textone,
texttwo: req.body.texttwo,
user: req.user.id,
name: req.body.name
});
newQuestion
.save()
.then(question => res.json(question))
.catch(err => console.log("unable to post question" + err));
}
);
//#type POST
//#route /api/questions/answers/:id
// #desc route for submitting answers to questions
// #access PRIVATE
router.post(
"/answers/:id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
Question.findById(req.params.id)
.then(question => {
const newAnswer = {
user: req.body.id,
name: req.body.name,
text: req.body.text
};
question.answers.unshift(newAnswer);
question
.save()
.then(question => res.json(question))
.catch(err => console.log(err));
})
.catch(err => console.log(err));
}
);
//#type POST
//#route /api/questions/upvote/:_id
// #desc route for upvoting
// #access PRIVATE
router.post(
"/upvote/:id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
Profile.findOne({ user: req.user.id })
.then(profile => {
Question.findById(req.params.id)
.then(question => {
if (
question.upvotes.filter(
upvote => upvote.user.toString() === req.user.id.toString()
).length > 0
) {
return res.status(400).json({ noupvote: "User already upvoted" });
}
question.upvotes.unshift({ user: req.user.id });
question
.save()
.then(question => res.json(question))
.catch(err => console.log(err));
})
.catch(err => console.log(err));
})
.catch(err => console.log(err));
}
);
module.exports = router;
Error i'm getting..
TypeError: Cannot read property 'upvotes' of null
at C:\Users\Subhash Chaudhary\Desktop\NodeJsLearning\09bigstack\routes\api\questions.js:87:24
at processTicksAndRejections (internal/process/task_queues.js:93:5)
you can do this using mongoose schema like ;
var mongoose = require('mongoose');
var id = mongoose.Types.ObjectId(req.params.id);
Question.findById(id);
here, we convert the id in to the '_id' or we can say that 'ObjectId' of mongoose which is the schema type object id.
it happened because in the tutorial instructor was using MLab which returns id and I was using MongoDB which returns _id. Question.findById(req.params.id) returned nothing then i changed it to Question.findById(req.params._id) and it worked. The suggestion i got in comment.

Need help saving a game to a user's favorited games

I'm receiving an error when trying to associate a saved game to the user that saves it. The error says "cannot read property push of undefined"
The user, and game can be read in the console. I think it may have something to do with the user model during the initial creation of the user, however I can't be sure. I did notice if I try to console.log(user.favGames) it will be returned undefined.
I've tried everything I can think of, I've re-written the controller roughly 10 times, to no avail.
user model
const mongoose = require('mongoose')
const bcrypt = require('bcrypt')
const SALT_ROUNDS = 6
const Schema = mongoose.Schema
const userSchema = new Schema(
{
username: { type: String, unique: true },
email: { type: String, unique: true, unique: true },
password: { type: String, required: true },
avatar: { type: String },
favGames: { type: Schema.Types.ObjectId, ref: 'Game', default: null },
comments: { type: Schema.Types.ObjectId, ref: 'Comment', default: null }
},
{
timestamps: true
}
)
userSchema.set('toJSON', {
transform: function(doc, ret) {
delete ret.password
return ret
}
})
userSchema.pre('save', function(next) {
const user = this
if (!user.isModified('password')) return next()
bcrypt.hash(user.password, SALT_ROUNDS, function(err, hash) {
if (err) return next()
user.password = hash
next()
})
})
userSchema.methods.comparePassword = function(tryPassword, cb) {
bcrypt.compare(tryPassword, this.password, cb)
}
module.exports = mongoose.model('User', userSchema)
game model
const mongoose = require('mongoose')
const Schema = mongoose.Schema
let gameSchema = new Schema({
name: { type: String, required: true },
boxArtUrl: { type: String, required: true },
twitchID: { type: String, required: true },
comments: { type: Schema.Types.ObjectId, ref: "Comment"}
})
module.exports = mongoose.model('Game', gameSchema)
game router
const express = require('express')
const router = express.Router()
const gamesCtrl = require('../../controllers/gameCtrl')
function isAuthed(req, res, next) {
if (req.user) return next()
return res.status(401).json({ msg: 'Unauthorized ' })
}
router.get('/')
router.post('/', isAuthed, gamesCtrl.addGame)
module.exports = router
game controller
const User = require('../models/user')
const Game = require('../models/Game')
function addGame(req, res) {
Game.create({
name: req.body.name,
twitchID: req.body.id,
boxArtUrl: req.body.box_art_url
})
.then(game => {
User.findById(req.user._id)
.then(user => {
console.log(game)
console.log(user.favGames)
// user.favGames.push(game)
// user.save()
})
.catch(err =>
console.log('error when updating user with new game', err)
)
})
.catch(err => console.log('error saving game', err))
}
module.exports = {
addGame
}
the error is flagged in my controller at user.favGames.push(game). Note that when a user creates a profile there are no games associated with their profile. I'm pretty sure I'm calling on the actual data instance of the model, not the model itself. Thanks in advance for your assistance.
Your favGames (and also comments) must be defined as array in user model like this.
const userSchema = new Schema(
{
username: { type: String, unique: true },
email: { type: String, unique: true, unique: true },
password: { type: String, required: true },
avatar: { type: String },
favGames: [{ type: Schema.Types.ObjectId, ref: 'Game', default: null }],
comments: [{ type: Schema.Types.ObjectId, ref: 'Comment', default: null }]
},
{
timestamps: true
}
)
Also user.save() returns a promise, so you need use then block, or await.
So the addGame function must be like this (I converted the code to async/await)
async function addGame(req, res) {
try {
let game = await Game.create({
name: req.body.name,
twitchID: req.body.id,
boxArtUrl: req.body.box_art_url
});
let user = await User.findById(req.user._id);
if (user) {
user.favGames.push(game);
await user.save();
res.status(200).send("game and user saved");
} else {
console.log("user not found");
res.status(404).send("user not found");
}
} catch (err) {
console.log("Err: ", err);
res.status(500).send("Something went wrong");
}
}
Looks like it's a matter of checking to see if it exists:
User.findById(req.user._id)
.then(user => {
if (!Array.isArray(user.favGames)) {
user.favGames = [];
}
user.favGames.push(game);
user.save();
})

Resources