Mongoose Delete SubDocument in Array - javascript

i am using mongoose to make database for my project, my schema Class has structure like this:
const mongoose = require('mongoose')
const classSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
consultant: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Consultant',
required: true
},
startYear: {
type: String,
required: true
},
classname: {
type: String,
required: true,
unique: true
},
studentList: [
{
code: {
type: String,
required: true
},
fullname: {
type: String,
required: true
}
}
]
})
const Class = mongoose.model('Class', classSchema)
module.exports = Class
as you can see, i have a property called studentList is an array and this is how it was store in mongoose Atlas
so now i want to delete a subdocument in array studentList so this is my route:
http://localhost:5000/users/:code
and this is how i have done:
exports.delele_student_from_user = (req, res, next) => {
var { code } = req.params;
var { classname } = req.body;
User.findOneAndDelete({
classname,
studentList: {
$pull: {
code,
},
},
})
.exec()
.then((doc) => {
console.log(`deleted user with code ${code} from colection User`);
next();
})
.catch((err) => {
console.log(err);
return res.status(500).json({ err });
});
};
and i got this Error:
{ MongoError: unknown operator: $pull
at MessageStream.messageHandler (C:\Users\ASUS\OneDrive\Desktop\uet-backend\node_modules\mongoose\node_modules\mongodb\lib\cmap\connection.js:266:20)
at MessageStream.emit (events.js:198:13)
at processIncomingData (C:\Users\ASUS\OneDrive\Desktop\uet-backend\node_modules\mongoose\node_modules\mongodb\lib\cmap\message_stream.js:144:12)
at MessageStream._write (C:\Users\ASUS\OneDrive\Desktop\uet-backend\node_modules\mongoose\node_modules\mongodb\lib\cmap\message_stream.js:42:5)
at doWrite (_stream_writable.js:415:12)
at writeOrBuffer (_stream_writable.js:399:5)
at MessageStream.Writable.write (_stream_writable.js:299:11)
at TLSSocket.ondata (_stream_readable.js:709:20)
at TLSSocket.emit (events.js:198:13)
at addChunk (_stream_readable.js:288:12)
at readableAddChunk (_stream_readable.js:269:11)
at TLSSocket.Readable.push (_stream_readable.js:224:10)
at TLSWrap.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
operationTime:
Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1603874496 },
ok: 0,
code: 2,
codeName: 'BadValue',
'$clusterTime':
{ clusterTime:
Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1603874496 },
signature: { hash: [Binary], keyId: [Long] } },
name: 'MongoError' }
DELETE /students/16022267 500 785.904 ms - 240
please show me how to fix this, thank you so much and have a good day

Use findOneAndUpdate()\
You can also do the update directly in MongoDB without having to load the document and modify it using code. Use the $pull or $pullAll operators to remove the item from the array.
https://docs.mongodb.com/manual/reference/operator/update/pull/#up._S_pull

You need to use update
exports.delele_student_from_user = async (req, res, next) => {
var { code } = req.params;
var { classname } = req.body;
try {
await User.update(
{ classname },
{
$pull: {
studentList: { code }
}
}
);
return res.status(200).json({ message: "Student deleted" });
} catch (err) {
return res.status(500).json({ err });
}
};
I have also used async / await so it looks cleaner.

Related

Pull element from array in mongodb

I'm using this schema for the USERS Collection
const usersSchema = new Schema({
username: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
select: false,
},
phone: {
type: Number,
required: true,
},
books: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "books",
},
],
});
and when the user want to delete a book in books collection, it should be delete in the user's books document (The above array) as well.
and I use this query but i get an error
const deleteBook = async (req, res, next) => {
try {
const { id } = req.query;
const deletedProduct = await books.findByIdAndDelete(id).populate(
"author"
);
// IT WORKS AND DELETE THE BOOK IN BOOKS COLLECTION
if (!deletedProduct) {
return next(new ErrorHandler("Product Was Not Found", 404));
}
await Users.findByIdAndUpdate(
deletedProduct.author._id,
{
$pull: { books: deletedProduct._id },
},
(err, docs) => {
// ERROR IS NULL IN HERE
console.log(err);
console.log(docs);
}
);
res.status(200).json("Success");
} catch (err) {
next(new ErrorHandler(err.message, 500));
}
};
Its My query but i get this error.
"Query was already executed: users.findOneAndUpdate({ _id: new ObjectId("THE USER'S ID"
i want to say. The book will be deleted from the books collection, but stay as same in the User's books array.

mongoose UnCaught TypeError: foundProduct.greet is not a function

So the title says it all. Seems like for some reason I can't access the greet function, and I really don't understand why. I'm following a teacher froma course and everything seems to be working fine on is side
this is the .js :
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:27017/shopApp")
.then(() => { //check if you succesfully connect to mongodb
console.log("Connection Open !")
})
.catch(err => {
console.log("Oh No Error")
console.log(err)
})
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true,
maxLength: 20
},
price: {
type: Number,
required: true,
min: [0, "Price must be positive !"]
},
onSale: {
type: Boolean,
default: false
},
categories: [String],
qty: {
online: {
type: Number,
default: 0
},
inStore: {
type: Number,
default: 0
}
},
size: {
type: String,
enum: ["S", "M", "L"]//these are the only string that'll be accepted
}
})
const Product = mongoose.model("Product", productSchema)
productSchema.methods.greet = function () {
console.log("hello ! ")
console.log(`- from ${this.name}`)
}
const findProduct = async () => {
const foundProduct = await Product.findOne({ name: "Tire pump" });
foundProduct.greet();
}
findProduct()
I know the problem is from const Product and below but cant find anything wrong
and here is the reponse a get when I .load product.js with node :
Uncaught TypeError: foundProduct.greet is not a function

Node.js, MongoDB error - "message": "Schema hasn't been registered for model \"Category\".\nUse mongoose.model(name, schema)"

I'm in process of an app based on Node.js, MongoDB and Express. My goal is to get a fetch API system to work.
When using Postman to check out my status, the GET for "article.js" model file (in my localhost:3000/articles) shows the following error:
{
"error": {
"message": "Schema hasn't been registered for model \"Category\".\nUse mongoose.model(name, schema)",
"name": "MissingSchemaError"
}
}
This error disables the display of my articles or categories in Postman, as they are saved in my MongoDB project area at mongodb cloud.
The model file code "article.js" is the following:
const mongoose = require('mongoose');
const articleSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: { type: String, required: true },
description: { type: String, required: true },
content: { type: String, required: true },
categoryId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'Category' }
});
module.exports = mongoose.model('Article', articleSchema);
This file connects with the controller named "articles.js", with the following relevant code:
const mongoose = require('mongoose');
const Article = require('../models/article');
const Category = require('../models/category');
module.exports = {
getAllArticles: (req, res) => {
Article.find().populate('categoryId', 'title').then((articles) => {
res.status(200).json({
articles
})
}).catch(error => {
res.status(500).json({
error
})
});
},
createArticle: (req, res) => {
const { title, description, content, categoryId } = req.body;
Category.findById(categoryId).then((category) => {
if (!category) {
return res.status(404).json({
message: 'Category not found'
})
}
const article = new Article({
_id: new mongoose.Types.ObjectId(),
title,
description,
content,
categoryId
});
return article.save();
}).then(() => {
res.status(200).json({
message: 'Created article'
})
}).catch(error => {
res.status(500).json({
error
})
});
},
}
The model file "category.js" code in the app looks like this:
const mongoose = require('mongoose');
const categorySchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: { type: String, required: true },
description: { type: String, required: true }
});
module.exports = mongoose.model('Category', categorySchema);
I looked up topics from the past here, such as this one - but it didn't solve my problem.
What should I do in order to fix my code?
Is it a syntax mistake or something else?
the code seems to be OK
I don't see here any particular error's

Mongoose & Express: How to properly Remove, Create & Store data that are reference

The first problem I am having is that whenever I try to delete the Comment, I also try to find the index of that specific comment inside post.comments as well as inside user.comments, it consistently returns -1, the reason why I am trying to find it, is so that I can splice it from the comments array that user and post do have.
The second problem I am having is that whenever I create a comment, I try to store it in the comments array that user and post have, but it stores it only as a string, although I think it is supposed to be stored as an object right?, So I can access it later by populating?
I have been struggling for days now being very frustrated why it does not work. Please help me!
Below will be my two routes, for deleting and creating comments, and my Schemas, Thank You for all the help!
Creating Comments
// #route POST api/posts/comment/:id
// #desc Comment on a post
// #access Private
router.post(
'/comment/:id',
[
auth,
[
check('text', 'Text is required')
.not()
.isEmpty()
]
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const post = await Post.findById(req.params.id);
const user = await User.findById(req.user.id)
const newComment = {
text: req.body.text,
post: post._id,
user: req.user.id
};
const comment = new Comment(newComment);
post.comments.unshift(comment._id);
user.comments.unshift(comment._id)
console.log(user.comments);
console.log(post.comments);
console.log(comment)
await post.save();
await comment.save();
await user.save();
res.json(comment);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
}
);
Deleting comments
// #route DELETE api/posts/comment/:id/:comment_id
// #desc Delete comment
// #access Private
router.delete('/comment/:id/:comment_id', auth, async (req, res) => {
try {
const post = await Post.findById(req.params.id);
const user = await User.findById(req.user.id);
// Pull out comment by finding it through its id
const comment = await Comment.findById(req.params.comment_id);
// Make sure comment exists
if (!comment) {
return res.status(404).json({ msg: 'Post do not have this comment' });
}
// Check user
if (comment.user.toString() !== req.user.id) {
return res.status(401).json({ msg: 'User not authorized' });
}
// Get The value to be removed
const postCommentIndex = post.comments.findIndex(postComment => postComment === comment._id);
const userCommentIndex = user.comments.findIndex(userComment => userComment === comment._id);
console.log(`This is the post comment index ${postCommentIndex}`);
console.log(`This is the user comment index ${userCommentIndex}`);
post.comments.splice(postCommentIndex, 1);
user.comments.splice(userCommentIndex, 1);
// save user and post
await post.save();
await user.save();
await comment.remove();
// resend the comments that belongs to that post
res.json(post.comments);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
Schemas:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
avatar: {
type: String
},
posts: [{type: mongoose.Schema.Types.ObjectId, ref: "Post"}],
comments: [{type: mongoose.Schema.Types.ObjectId, ref: "Comment"}],
date: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('User', UserSchema);
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
text: {
type: String,
required: true
},
likes: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'User'
}
}
],
dislikes: [
{
user: {
type: Schema.Types.ObjectId,
ref: "User"
}
}
],
comments: [{type: Schema.Types.ObjectId, ref: "Comment"}],
date: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Post', PostSchema);
const mongoose = require("mongoose")
const Schema = mongoose.Schema;
const CommentSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
post: {
type: Schema.Types.ObjectId,
ref: "Post"
},
text: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
})
module.exports = mongoose.model("Comment", CommentSchema);
I think you need to redesign your schemas in a simpler way, there are too many references between the models, and this causes issues, for example you have 5 db access when you want to create a comment, and 6 db access when you want to delete a comment.
I would create the user schema like this removing the posts and comment references, but later when we want to access the posts from users, I set up virtual populate.
const UserSchema = new Schema(
{
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
avatar: {
type: String
},
date: {
type: Date,
default: Date.now
}
},
{
toJSON: { virtuals: true }
}
);
UserSchema.virtual("posts", {
ref: "Post",
localField: "_id",
foreignField: "user"
});
And in the posts schema, I removed the comments references.
(For simplicity I removed likes and dislikes fields.)
const PostSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: "User"
},
text: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
},
{
toJSON: { virtuals: true }
}
);
PostSchema.virtual("comments", {
ref: "Comment",
localField: "_id",
foreignField: "post"
});
Comment schema can stay as it is.
Now to add a comment to a post, we only need 2 db access, one for checking if post exists, and one for creating the post.
router.post(
"/comment/:id",
[
auth,
[
check("text", "Text is required")
.not()
.isEmpty()
]
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const post = await Post.findById(req.params.id);
if (!post) {
return res.status(404).json({ msg: "Post not found" });
}
let comment = new Comment({
text: req.body.text,
post: req.params.id,
user: req.user.id
});
comment = await comment.save();
res.json(comment);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
Let's say we have these 2 users:
{
"_id" : ObjectId("5e216d74e7138b638cac040d"),
"name" : "user1"
}
{
"_id" : ObjectId("5e217192d204a26834d013e8"),
"name" : "user2"
}
User1 with _id:"5e216d74e7138b638cac040d" has this post.
{
"_id": "5e2170e7d204a26834d013e6",
"user": "5e216d74e7138b638cac040d",
"text": "Post 1",
"date": "2020-01-17T08:31:35.699Z",
"__v": 0,
"id": "5e2170e7d204a26834d013e6"
}
Let's say user2 with _id:"5e217192d204a26834d013e8" commented on this post two times like this:
{
"_id" : ObjectId("5e2172a4957c02689c9840d6"),
"text" : "User2 commented on user1 post1",
"post" : ObjectId("5e2170e7d204a26834d013e6"),
"user" : ObjectId("5e217192d204a26834d013e8"),
"date" : ISODate("2020-01-17T11:39:00.396+03:00"),
"__v" : 0
},
{
"_id": "5e21730d468bbb7ce8060ace",
"text": "User2 commented again on user1 post1",
"post": "5e2170e7d204a26834d013e6",
"user": "5e217192d204a26834d013e8",
"date": "2020-01-17T08:40:45.997Z",
"__v": 0
}
To remove a comment we can use the following route, as you see we decreased the db access from 6 to 3, and code is shorter and cleaner.
router.delete("/comment/:id/:comment_id", auth, async (req, res) => {
try {
const comment = await Comment.findById(req.params.comment_id);
if (!comment) {
return res.status(404).json({ msg: "Post do not have this comment" });
}
if (comment.user.toString() !== req.user.id) {
return res.status(401).json({ msg: "User not authorized" });
}
await comment.remove();
// resend the comments that belongs to that post
const postComments = await Comment.find({ post: req.params.id });
res.json(postComments);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
Now you may ask, how will access the posts from an user? Since we setup virtual populate in our user schema, we can populate the posts like this:
router.get("/users/:id/posts", async (req, res) => {
const result = await User.findById(req.params.id).populate("posts");
res.send(result);
});
You can try this code snipet :
Comment.deleteOne({
_id: comment.id
}, function (err) {
if (err) {
console.log(err);
return res.send(err.message);
}
res.send('success');
});

Cast to ObjectId failed for value "1" at path "visits"

I keep getting the error Cast to ObjectId failed for value '1' at path 'visits'. I will show below my schemas.
User Schema:
const mongoose = require('mongoose');
const uniqueVal = require('mongoose-unique-validator');
const userSchema = mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
role: {
type: String,
required: true
},
answers: {
type: String
},
visits: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'UserClicks', // Reference to your Visit model
}]
});
userSchema.plugin(uniqueVal);
module.exports = mongoose.model('User', userSchema);
Then I have the UserClicks Schema which connects them.
The UserClicks Schema:
const mongoose = require('mongoose');
const visitCountSchema = mongoose.Schema({
postId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post', // Reference to your Post model
},
visitCount: {
type: Number,
default: 1
}
})
module.exports = mongoose.model('UserClicks', visitCountSchema);
Those are the two Schemas and then I have got the function which is what is failing which I will show below:
exports.pageVisitCount = (req, res, next) => {
User.findById({
_id: req.userData.userId
}, 'visits', function (err) {
if (err) {
res.status(401).json({
message: "Error Occured!"
})
} else {
User.findOneAndUpdate({
"visits.postId" : mongoose.Types.ObjectId(req.body.postId),
},
{
$inc : { "visits.$.visitCount" : 1 }
}, (err, doc) => {
if (err) {
console.log(err);
} else {
if (doc === null ) {
const postToAdd = {
postId : mongoose.Types.ObjectId(req.body.postId)
};
User.findByIdAndUpdate({
_id: mongoose.Types.ObjectId(req.userData.userId)
},
{$push: { visits : postToAdd }},
(err, doc) => {
if(err) {
res.status(401).json({
message: "Error Occured!"
})
} else {
res.status(200).json({
message: "Success!"
})
}
}
);
} else {
res.status(200).json({
message: "Success!"
})
}
}
});
}
});
}
It seems to fail when it gets to this:
$inc : { "visits.$.visitCount" : 1 }
}, (err, doc) => {
I had this function working previously but now I have changed the models and put them separately but it is not working. Any ideas? Thanks
This is the error log below:
{ CastError: Cast to ObjectId failed for value "1" at path "visits"
at new CastError (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/error/cast.js:29:11)
at ObjectId.cast (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/schema/objectid.js:153:13)
at ObjectId.SchemaType.applySetters (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/schematype.js:755:12)
at ObjectId.SchemaType._castForQuery (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/schematype.js:1141:15)
at ObjectId.SchemaType.castForQuery (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/schematype.js:1131:15)
at ObjectId.SchemaType.castForQueryWrapper (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/schematype.js:1110:15)
at castUpdateVal (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/helpers/query/castUpdate.js:383:21)
at walkUpdatePath (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/helpers/query/castUpdate.js:256:22)
at castUpdate (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/helpers/query/castUpdate.js:79:18)
at model.Query._castUpdate (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/query.js:3714:10)
at castDoc (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/query.js:3741:18)
at model.Query.Query._findAndModify (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/query.js:2965:19)
at model.Query.Query._findOneAndUpdate (/Users/andrewpeliza/Desktop/ctas/node_modules/mongoose/lib/query.js:2674:8)
at process.nextTick (/Users/andrewpeliza/Desktop/ctas/node_modules/kareem/index.js:333:33)
at process._tickCallback (internal/process/next_tick.js:61:11)
message: 'Cast to ObjectId failed for value "1" at path "visits"',
name: 'CastError',
stringValue: '"1"',
kind: 'ObjectId',
value: 1,
path: 'visits',
reason: undefined }

Categories

Resources