Using DELETE Rest api to remove array object from Mongodb - node.js

I am using Node.js, express, mongodb, and mongoose. I have two files: favorite and favorite-route.
"favorites" in the schema has multiple objects in it array. Given the mongodb assigned _id, I would like to create a delete method to gremove the specified object in the array.
Here is my schema in favorite:
userID:{
//type: String,
type: mongoose.Schema.Types.ObjectId,
ref: "user",
required: true,
unique: true
},
favorites:[{
publication: {
type:mongoose.Schema.Types.ObjectId,
ref: "Pet"
},
id: {
type: String,
required: true
},
comment: {
type: String,
required: true
}
}]
})
favoritesSchema.statics.deleteFavorite = async (favID)=>{
return await Favorite.findOneAndUpdate({favID})
}
This is my delete method in favorite-route file:
router.delete('/:favID', async (req,res)=>{
let doc = await Favorite.deleteFavorite(req.params.favID)
if(doc){
doc.splice(req.params.favID);
res.send(doc)
return;
}
res.status(404).send({error: "no se encontró esa publicación"})
})
And lastly, here is my http:
DELETE {{host}}/api/favorites/626eddd14762eb4ae4c77f9e
When i test this, it gives me the error:
TypeError: doc.splice is not a function
I'd appreciate any tips and insight. I have spent a while searching for answers but most suggested using $pull, which I am unsure how to implement in the delete method.
Thank you :)

you should do.
Favorite.updateOne(
{ userID: user._id }, // this is the query to find the desired favourite list
{ $pull: { favorites: { id : req.params.favID }} }, // this removes
);

Related

checking if object id is in an array of object id

I am trying to do a check to see if a logged-in user's id req.user.id. is in an array of followers of the user being checked in req.params.id, bit for some reason it doesn't work.
router.get('/api/:id/isfollowing', auth, async (req, res) => {
if (req.params.id==req.user._id) {
return res.status(200).send({ "isfollowing": "Myself" })
}
try {
const followers = await Follow.find({
user: req.params.id
})
let followersArr = followers.map(follower=>{
return follower.followedBy
})
const yes = followersArr.includes(req.user._id)
// const yes = followersArr.filter((objId) => objId==req.user._id)
console.log(yes, followersArr, req.user._id)
if (yes===false) {
return res.status(200).send({ "isfollowing": false })
}
return res.status(200).send({ "isfollowing": true })
} catch (e) {
res.status(500).send()
}
})
for some reason the check doesn't work and even when using the filter, it still returns nothing. But when I console.log the values, it is right there.
[] [ 5fa4f0af4a7bf5471c41e225, 5f9dc1777a695570e878424d ] 5f9dc1777a695570e878424d
EDIT
schemas below
User schema
const userSchema = new mongoose.Schema({
fullname: {
type: String,
required: true,
trim: true,
lowercase: true
},
username: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true
},
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
password: {
type: String,
required: true,
minlength: 7,
trim: true,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error('Passwoed cannot contain "password"')
}
}
}
})
follow schema
const followSchema = new mongoose.Schema({
// the logged in user who will be trying to follow someone will be added to "followedBy"
// the user who is getting followed will be added to "user"
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
followedBy: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Showcase'
}
}, {
timestamps: true
})
I gave follow its own schema so I can record other info like time and other info whenever a user follows another.
If I've understand well you only need a simple query.
Since you only want to know if the id is into an array, you can check that directly with mongo. You don't need load every document into memory and use JS functions like filter or something similar.
You only need a query similar to this:
db.collection.find({
"user": ObjectId("user_id"),
"followedBy": ObjectId("follower_id")
})
This will return a document that match both values.
Check here it works and tell me if is the behaviour and output you expect.
Also I will code a mongoose query and I'll update the answer.
You can use also this query in mongoose to get how many documents find the query:
var find = await model.find({"user":mongoose.Types.ObjectId(user_id),"followedBy":mongoose.Types.ObjectId(follower_id)}).countDocuments()
Includes cannot be used in this case since you are trying to find ObjectId in an array.
To find if req.user._id is present in followersArr, use Array.some() function as below
const yes = followersArr.some(followerId=>{followerId.equals(req.user._id)})
The some call will iterate over the followersArr array, calling equals on each one to see if it matches req.user._id and stop as soon as it finds a match. If it finds a match it returns true, otherwise false.
You can't use something simpler like indexOf because you want to compare the ObjectIDs by value, not by reference.

Nested document referencing in mongodb with mongoose

I am trying to build a kanban as a project for my portfolio with the mern stack with a rest api. I am having issues querying the models with deeply nested objects in it. My idea is to have the lists be referenced in the project, the task be referenced in the list and the user be referenced in the task and the project as a contributor. My issue is getting the information of the task (title, etc.) through populating the project model with mongoose, or MongoDB for that matter. How should I approach this? Is it even possible to do with MongoDB? I see a lot of people doing this with a sql database. The four model schemas are as follows:
const projectSchema = new Schema({
title: { type: String, required: true, max: 32, trim: true },
lists: [{ type: Schema.Types.ObjectId, ref: 'List' }],
contributors: [{ type: Schema.Types.ObjectId, ref: 'User' }],
});
const listSchema = new Schema({
title: { type: String, required: true, max: 32, trim: true },
tasks: [{ type: Schema.Types.ObjectId, ref: 'Task' }],
});
const taskSchema = new Schema({
title: { type: String, required: true, max: 38, trim: true },
text: { type: String, max: 255, trim: true },
assignee: { type: Schema.Types.ObjectId, ref: 'User' },
});
const userSchema = new Schema(
Name: { type: String, required: true, trim: true },
projects: [{ type: Schema.Types.ObjectId, ref: 'Project' }],
tasks: [{ type: Schema.Types.ObjectId, ref: 'Task' }],
);
The issue I have is displaying the tasks in the list. When I populate the list with the project call, I get the ObjectId of the task, but would like to get the task title, which as it seems is not possible by applying the .populate call again. I have the following api controller/route:
router.get('/:projectId', async (req, res) => {
try {
const project = await Project.findOne({
_id: req.params.projectId,
}).populate('lists', 'title tasks');
res.json(project);
} catch (error) {
if (error) {
console.error(error.message);
res.status(500).send('Server Error');
}
}
});
How would I approach getting the task title and later on the user name for all the tasks in the list referencing a project?
So I found a solution to this. Not sure if this is the most efficient method to solve this. If anyone can provide a better solution, please post it on here.
I found the mongoose documents explains quite well how to populate over multiple levels.
https://mongoosejs.com/docs/populate.html#deep-populate
Now the solution for the answer looks as follows
router.get('/:projectId', async (req, res) => {
try {
const project = await Project.findOne({
_id: req.params.projectId,
}).populate({ path: 'lists', populate: { path: 'tasks' } });
res.json(project);
} catch (error) {
if (error) {
console.error(error.message);
res.status(500).send('Server Error');
}
}
});

It is possible to pull elements from a referred objects' array using mongoose?

I have 2 mongo schemas related one with the other using ObjectId:
var User = new Schema({
username: {
type:String,
unique: true
},
password: {
type:String
},
verified: {
type: Boolean,
default: false
},
lastActivity:{
type:Date,
default:Date.now
}
});
And a watitingRoom schema with lists all the users:
var WaitingRoom = new Schema({
lastActivity:{
type:Date,
default:Date.now
},
clients: [{
type : mongoose.Schema.ObjectId,
ref: 'User'
}],
videocalls: [{
type: mongoose.Schema.ObjectId,
ref:'VideoCall'
}]
});
So, I want to 'refresh' my clients array pulling all the clients which a lastActivity less than the current time. I tried it by using the $pull tool present in mongoose. After googling and mixing different examples I tried things like:
WaitingRoom.findOneAndUpdate({}, { lastActivity: new Date(),
$pull : {clients : {"clients.lastActivity": { $lt: new Date() }}}
}, options)
.populate("clients")
.exec( function(error, waitingRoom) {
if (err) { return res.status(500).send({ msg: err.message }); }
})
Which finds the unique waiting room, updates the lastActivity field and tries to pull all the clients that has a clients.lastActivity less than the current date.
(Obviously this snipped code doesn't work)
The problem is that I didn't find any documentation or example that explains if it is possible to pull elements from a referred ObjectId schema using a nested condition clients.lastActivity
You need to first find the ids from User database and then need to $pull them from the WaitingRoom database
User.find({ lastActivity: new Date() }).then((users) => {
const ids = []
users.map((user) => {
ids.push(user._id)
})
WaitingRoom.update({}, { $pull: { clients: ids }}, { multi: true }).then(() => {
console.log('removed')
})
})

Need some clarification on mongoose/mongodb populate command

Hello so I am making a basic app with users and posts.
I followed the mongoose documentation on population (http://mongoosejs.com/docs/2.7.x/docs/populate.html) and setup my Schemas so that the users and be connected to posts
var userSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: String,
created_at: Date,
updated_at: Date,
admin: Boolean,
posts: [{ type: mongoose.Schema.ObjectId, ref: 'Post' }]
});
var postSchema = new mongoose.Schema({
_user : [{ type: mongoose.Schema.ObjectId, ref: 'User' }],
audioFile: { type: String, required: true },
imageFile: { type: String },
title: { type: String, required: true },
artist: { type: String, required: true },
start: { type: String, required: true },
stop: { type: String, required: true },
genre: { type: String, required: true },
tags: [{ type: String }]
});
app.get('/', function (req, res){
Post.find({}, function(err, allPosts){
if(!err){
res.render('main.njk', {
posts : allPosts,
title : 'Title',
isLogged : req.session.isLogged,
user : req.session.user,
messages : req.flash('alert')
});
} else { return done(err); }
});
});
Thats all fine and gravy and I can run a foreach loop on allPosts to pull each one in my HTML, but when I try to think of how I am going to display all the posts with their respective users attached to each post I am unsure of how to connect the two since all the examples in the mongoose doc is just mainly for findOne.
I was thinking something like this
app.get('/', function (req, res){
Post.find({}, function(err, allPosts){
if(!err){
allPosts.populate('_user', ['username']);
allPosts.exec(function (err, users){
if(err) console.log(err);
console.log(users);
});
res.render('main.njk', {
posts : allPosts,
title : 'Spaurk.net',
isLogged : req.session.isLogged,
user : req.session.user,
messages : req.flash('alert')
});
} else { return done(err); }
});
});
but that doesn't work of course.
So I was wondering if anyone with experience with this situation would be able to help me solve this.
Thanks a lot for any input.
EDIT, thanks to Daves help I was able to get the populate to work properly, I just cant pull the fields I want correctly with
Post.find({}).populate('_user').exec(function(err, allPosts){
In my loop {% for post in posts %}
, when I do post._user it shows the whole user schema, but when I do post._user.username it doesn't return anything. I am unsure as to why this is.
The proper way to structure a populate on a query is like this:
Post.find({})
.populate('_user')
.exec((err, allposts){...})
Then you will have an array of your Posts with the _user array populated. If you need to access a property of a user, you will need to do another loop through the _user array or specify with use you want to use _user[0].<property>

Mongoose Populate - array

can someone please help me with population of this schema? I need to populate array of Staff by their userId.
var PlaceSchema = new Schema ({
name: { type: String, required: true, trim: true },
permalink: { type: String },
country: { type: String, required: true },
...long story :D...
staff: [staffSchema],
admins: [adminSchema],
masterPlace:{ type: Boolean },
images: []
});
var staffSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account' },
role: { type: Number }
});
var adminSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account'}
})
var Places = mongoose.model('Places', PlaceSchema);
I tried to use this query, but without success.
Places.findOne({'_id' : placeId}).populate('staff.userId').exec(function(err, doc){
console.log(doc);
});
Polpulation is intended as a method for "pulling in" information from the related models in the collection. So rather than specifying a related field "directly", instead reference the related fields so the document appears to have all of those sub-documents embedded in the response:
Places.findOne({'_id' : placeId}).populate('staff','_id')
.exec(function(err, doc){
console.log(doc);
});
The second argument just returns the field that you want. So it "filters" the response.
There is more information on populate in the documentation.

Resources