How to remove an array element from Mongodb - javascript

I'm new to MEAN stack. I'm trying to implement this and this. I'm using $pull. But they ain't working maybe because my structure in mongodb is different from theirs. So let me first show you that:
downvoters is an string array that contains userids who downvoted that particular article. Lets say the person on downvoters[2] i.e 53et853rf later upvoted this article.Then his userid should be removed from downvoters list. Here is my code:
api.js
router.put('/update-upvotes', (req, res) => {
let articleData = req.body;
...
Article.update(
{articleid: '5p4aqbryi'},
{ $pull: { downvoters: '53et853rf' } }
);
Article.findOneAndUpdate(
{articleid: '5p4aqbryi'},
{upvotes: articleData.upvotes, upvoters: articleData.upvoters}, useFindAndModify=false,
(error, user) => {
if(error) {
...
}
else {
...
}
})
})
But that id is not deleted. There's no error or warning on console. Please correct me.
And here is the schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const articleSchema = new Schema({
articleid: String,
title: String,
content: String,
date: String,
contributor: String,
upvotes: Number,
upvoters: [],
downvotes: Number,
downvoters: []
})
module.exports = mongoose.model('article', articleSchema, 'articles');
PS: Let articleId and downvoter id be hardcoded now. I'll make them dynamic later.

Both upvoters and downvoters are String arrays so your Mongoose schema should look like below:
const articleSchema = new Schema({
articleid: String,
title: String,
content: String,
date: String,
contributor: String,
upvotes: Number,
upvoters: [String],
downvotes: Number,
downvoters: [String]
});
You should also keep in mind that update() is an asynchronous operation which needs to be awaited or handled as Promise so:
let opResult = await Article.update(
{articleid: '5p4aqbryi'},
{ $pull: { downvoters: '53et853rf' } }
);
or
Article.update(
{ articleid: '5p4aqbryi' },
{ $pull: { downvoters: '53et853rf' } }
).then(opResult => console.log(opResult));

Related

unable to push object returned from mongoose query

I am making a social media backend.
I save post added by the used in a Post model and user data in a User model.
GITHUB_REPO_LINK_AT_END
NOTE: UserSchema have a Schema.TypesOf.ObjectId Reference To POST Model. User_Model_&_Post_Model_are_provided_in_the_end
To get all posts of a particular user, I make a GET request to the route "/post" with body:
{ "id" : "6399d54c00308a2fe0bdf9fc"} //sending user id to fetct all the ID of the post from USER model, so i can then query the POST model for the posts
This the function I am getting problem with:
const getPost = async(req, res)=>{
if(req.body.id){
try {
const user = await User.findById(req.body.id).select('-_id post');
//THIS IS THE PART I NEED HELP WITH-------------------------------------------
const posts = await user.post.map(async(postID) => {
const result = await Post.findById(postID).select('-_id title body');
//console.log(result) THIS PRINTS THE CORRECT OBJ FROM DB
return result; //THIS RETURNS AN EMPTY OBJECT HERE
});
//----------------------------------------------------------------------------
res.status(200).json(posts);
} catch (error) {
console.log(error);
res.status(500).json({message: error.message});
}
}
};
when sending a GET request it returns an empty array with empty objects.//PS: no. of empty obj = actual no. of obj in DB
//This is the response
[{},{},{},{},{},{},{},{},{},{},{}]
{
//This is the user object
"_id": "6399d54c00308a2fe0bdf9fc",
"createdAt": "2022-12-14T13:52:40.483Z",
"name": "ShivamUttam",
"username": "Fadedrifleman",
"post": [
"6399d57200308a2fe0bdfa00",
"6399d5c400308a2fe0bdfa06",
"6399d5ca00308a2fe0bdfa0a",
"6399d5d600308a2fe0bdfa0e",
"6399de29e8aa8697299941c5",
"6399dec6e9b79ac66c59cd7a",
"6399df0dbea937f8b3365979",
"6399df31bea937f8b336597d",
"6399df31bea937f8b3365981",
"6399df32bea937f8b3365985",
"6399df33bea937f8b3365989"
],
"__v": 5
}
Model for USER and POST:
User:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
createdAt: {
type: Date,
default: Date.now()
},
name: {
type: String,
required: [true, 'name must be provided'],
},
username : {
type: String,
required: [true, 'Username must be provided'],
},
post:[{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post',
}],
});
module.exports = mongoose.model('User', userSchema)
Post:
const mongoose = require('mongoose')
const postSchema = new mongoose.Schema({
createdAt: {
type: Date,
default: Date.now()
},
title:{
type: String,
required: [true, "title cannot be empty"],
max: [20, "title cannot exceed 20 character"]
},
body: {
type: String,
max: [145, "body cannot exceed 145 character"],
},
tags:{
type: String,
},
});
module.exports = mongoose.model('Post', postSchema);
https://github.com/Fadedrifleman/socialMediaAppBackend/tree/master
Since you have used async callback function in the map method, a async function always return a promise, whatever the entity is returned by the function is wrapped inside a promise and that promise is returned.
If you want to use map function with async js code, you can try the following
const posts = await Promise.all(user.post.map(async(id)=>{
const result = await Post.findById(postID).select('-_id title body');
return result;
}));
and if you want to straightaway send the posts, you can also use .lean() method on posts, as in
await Post.findById(postID).select('-_id title body').lean()
You had some bugs that probably would interfere, I did a pull request to fix them: https://github.com/Fadedrifleman/socialMediaAppBackend/pull/1
But the main part would be this:
const getPost = async (req, res) => {
try {
if (req.body.id) {
const user = await User.findById(req.body.id);
await user.populate("post");
res.status(200).json(user.post);
return;
}
const posts = await Post.find({ access: 'public' }).select('-access');
res.status(200).json(posts);
} catch (error) {
res.status(500).json({ message: error.message });
}
};

How can I update 2 collections at the same time using Node.js/Mongoose/MongoDB

Thank you for taking your time reading this.
I am making blog app with Node.js/Mongoose/MongoDB. Currently, I'm struggling to figure out how to update 2 collections at the same time.
My userSchema has arrays of postSchema, and I would like to update both users and posts collections whenever I update documents.
My code is here:
const postSchema = new mongoose.Schema({
title: String,
content: String,
author: String
});
const Post = mongoose.model('Post', postSchema);
const userSchema = new mongoose.Schema({
username: String,
password: String,
displayName: String,
provider: String,
posts: [postSchema],
drafts: [postSchema]
});
const User = mongoose.model('User', userSchema);
app.post('/edit/:title', function (req, res) {
Post.findOneAndUpdate({ title: req.params.title }, {
title: req.body.title,
content: req.body.content
}, function (error, post) {
if (error) {
console.log(error);
} else {
res.redirect('/dashboard');
}
});
});
At the moment, my code only updates posts collection, and arrays of postSchema inside users collection stay the same. Could anyone help me how to solve this problem?
You can do it 2 ways
Option 1
.then() & .catch() block
Post.findOneAndUpdate({
Do your stuff here
}).then((result)=>{
Do your stuff here with result from step above
}).catch((err)=>{
Handle Error
});
Option 2
Use async/ await
async function (req, res) {
const postResult = await Post.findOneAndUpdate({ title: req.params.title }, {
title: req.body.title,
content: req.body.content
});
const userResult = await User.findOneAndUpdate({Do some stuff here});
if(!postResult || !userResult){
return new Error(...)
}
return
Since there is not much of code shared this cannot be used as is.. but the logic behind these options will remain the same even in your code ..

TypeError: Cannot use 'in' operator to search for 'userId'

So i was implementing a users model in my mvc and then i get a weird error saying
MongoClient constructor.
D:\node\node_modules\mongoose\lib\document.js:2022
if (path in this.$__.selected) {
^
TypeError: Cannot use 'in' operator to search for 'email' in saifkhan501721#gmail.com
at model.isSelected (D:\node\node_modules\←[4mmongoose←[24m\lib\document.js:2022:14)
at D:\node\node_modules\←[4mmongoose←[24m\lib\document.js:2195:14
at Array.filter (<anonymous>)
at _getPathsToValidate (D:\node\node_modules\←[4mmongoose←[24m\lib\document.js:2194:71)
at model.Document.$__validate (D:\node\node_modules\←[4mmongoose←[24m\lib\document.js:2365:23)
at D:\node\node_modules\←[4mkareem←[24m\index.js:369:33
←[90m at processTicksAndRejections (internal/process/task_queues.js:79:11)←[39m
i have no idea as to what is the reason behind the error is, is it a syntax error , logical error connection error or mispelling of a variable,well anyway here's my app.js
mongoose
.connect('mongodb+srv://turd_waffle:SaifKhan#cluster0.lltqs.mongodb.net/shop?retryWrites=true&w=majority')
.then((result) => {
User.findOne().then(user=>{
if(!user){
const user=new User({
name:'Saif',
email:'saifkhan501721#gmail.com',
cart:{
items:[]
}
})
user.save()
}
})//save() saves the documents(mostly used as a reference to generate a sample id in order to start a cluster working)
app.listen(3000)
})
.catch(err => {
console.log(err)
})
here's my user.js model to store users data in mongodb database
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
cart: {
items: [{
productId: {
type: Schema.Types.ObjectId,
ref: 'Product',
required: true
},
quantity: {
type: Number,
required: true
}
}]
},
})
userSchema.methods.addToCart = function(product) {
const cartProductIndex = this.cart.items.findIndex(cp => {
return cp.productId.toString() === product._id.toString();
})
let newQuantity = 1;
const updatedCartItems = [...this.cart.items];
if (cartProductIndex >= 0) {
newQuantity = this.cart.items[cartProductIndex].quantity + 1;
updatedCartItems[cartProductIndex].quantity = newQuantity
} else {
updatedCartItems.push({
productId: product._id,
quantity: newQuantity
})
}
const updatedCart = {
items: updatedCartItems
}
this.cart=updatedCart
return this.save()
}
module.exports = mongoose.model('User', userSchema)
can anyone please be kind enough to atleast tell me what the error above is trying to say i used app.use to create a user so i can store his id, email and name
app.use((req, res, next) => {
User.findById('5f788c080ba19e0f8c642202')
.then(user => {
req.user = new User(user.name, user.email, user.cart, user._id);
next();
})
.catch(err => console.log(err));
});
Strange issue. From the code you provided, the issue should not arise.
When I look at the code in mongoose, the only way that this could happen is if you would do something like:
new User("saifkhan501721#gmail.com")
Then this.$__.selected would be a string instead of an object (e.g. {email: "saifkhan501721#gmail.com"}) and path in this.$__.selected would cause your received type error.
Not knowing enough about your app, I would assume that there maybe is a bad User object created somewhere else / cached / or already in database. Maybe it would help to verify this using a clean database?
See the source code for reference. When I take a look at the code it seems like an oversight that it is not checked if this.$__.selected is a string, because in this case it does not fail early (e.g. Object.keys(this.$__.selected) will not cause an error).

Node JS preventin users from deleting other user's products

I have a REST API built with Node JS and I'm currently using MongoDB as my database. I want to prevent the users from deleting another user's products and for this I checked if the userId from the decoded token is the same as the product userId.
Product schema
const mongoose = require("mongoose");
const productSchema = mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
userId: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
price: { type: Number, required: true },
productImage: { type: String, required: false },
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
required: true
},
gender: { type: String, required: true }
},
{ timestamps: { createdAt: "created_at" } }
);
module.exports = mongoose.model("Product", productSchema);
The delete product method:
const id = req.params.productId;
Product.findById({ _id: id }).then((product) => {
if (product.userId != req.user._id) {
return res.status(401).json("Not authorized");
} else {
Product.deleteOne({ _id: id })
.exec()
.then(() => {
return res.status(200).json({
message: "Product deleted succesfully",
});
})
.catch((err) => {
console.log(err);
return res.status(500).json({
error: err,
});
});
}
});
};
As you guys see first I'm searching executing the findByID method to access the userId property of the product, then I'm comparing the userId from the response with the userId from the decoded token.
I don't think my method is very efficient since it's running both findById and deleteOne methods.
Can you help me with finding a better solution for this?
as Guy Incognito mentioned, what you are trying to do is an OK thing and you may want to keep it this way in case you want to send a 404 status stating the product they are trying to remove does not exist.
however, if you are trying to do it with only one request
Product.deleteOne({ _id: id, userId: req.user._id })
hope it helps!

Express mongodb find query results empty array

I'm trying to do an API call to my express server to fetch employees that work in the same place based on the location ID. However, the API call returns just an empty array while it does work in the command-line interface.
Employee model
module.exports = mongoose => {
var schema = mongoose.Schema(
{
first_name: String,
last_name: String,
address: {
housenumber: Number,
street: String,
city: String,
zip: Number,
country: String
},
phone: Number,
mobile: Number,
email: String,
enrollment_date: Date,
staff_id: Number,
location: { type : mongoose.Schema.ObjectId, ref : 'location' },
department: String,
function: String,
active: Boolean
},
{ timestamps: true }
);
schema.method("toJSON", function() {
const { __v, _id, ...object } = this.toObject();
object.id = _id;
return object;
});
const Employee = mongoose.model("employee", schema);
return Employee;
};
Employee routing for API
router.get("/location/:location_id", employees.findAllByLocation);
Employee controller handling above call
exports.findAllByLocation = (req, res) => {
Employee.find({ location: req.params.location_id })
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving Employees."
});
});
};
Dummy database data to test on
Postman API call result
However, trying to find the user with that location ID in the command line interface does work and gives the desired output.
[
So somehow it messes up and I can't seem to figure out why it's doing this. I did some research and found that it might have to do with the location being a reference as an ObjectId. So I tried wrapping the req.params.location_id to an ObjectId might fix it but that didn't work.
What's the best way to get this working?
In order to use promise chain, you have to return something and then returned value will be passed chained “then()” as data. In your example you should
return Employee.find({location:req.params.location_id})

Categories