Id not getting removed from nested array - node.js

Hello I am trying to remove nested array object id from document but it is not getting removed. Although I am getting message "Deleted"
I have a response where the structure is :-
{
"admins": {
"users": [
"5d0364048db4957100f33fea" //<===want to delete this relational id
],
"email": "1cf1eede89#himail.online",
"password": "$2a$10$vHyGxX9P.t0/ybKcmIzkc.ZCX18oHaVnvTgJIWA2gTNzJ3TCdXS4a",
"_id": "5d0339d5b4b28b6ddff06802",
"companyName": "GH",
"__v": 0
}
I want to delete users _id from the array.
I tried this but it is not getting removed.
router.delete('/userDelete/:uId', checkAuth , (req, res, next) =>{
if(req.userData.role2 === 'admin') {
Admin.findOneAndUpdate({ _id: req.params.userId },{ $pull: { 'admins.users': req.params.uId}})
.exec()
.then(result => {
res.status(200).send(["Deleted"]);
})
.catch(err =>{
if (err.code == 500)
res.status(500).send(["Didn't get deleted"]);
else
return next(err);
});
Controller is like this :-
var admin = new Admin();
admin.companyName = req.body.companyName;
admin.admins = {
email : req.body.email,
password: req.body.password,
users : []
};
I am stuck here , what changes I must do in my route ?
EDIT:- DB is like this

The problem is here Admin.findOneAndUpdate({ _id: req.params.userId }
req.params.userId is undefined, because it does not exist in your path. req.params object holds only one property, uId.
So your query does not find any data.
req.params.userId would have value if your method route had a form like this router.delete('/userDelete/:userId/:uId).
So you could add userId in the url of your delete request and access it through req.params object. The new url should be like this
/userDelete/userId/uId
(e.g.)
userDelete/5d0339d5b4b28b6ddff06802/5d0364048db4957100f33fea

try to hard code your id here
Admin.findOneAndUpdate({ _id: req.params.userId },{ $pull: { 'admins.users': "5d0364048db4957100f33fea"}})
If it is working then do
req.params.uId.toString()

Try marking the '_id' as false in the schema itself for the array. For eg, in your schema, mark '_id' as false like below:
admin: {
type: Object,
users: [{
_id: false
}]
}
.
.
//rest of the schema

Related

Using ref in express js to make a relationship in mongodb

I have tried to make the relationship between two models in node js express app. I used the world ref to retrieve the user's names who created the courses however, when I create the course from the user after login or register, it does not show anything about the user who created that course. Instead, it shows an empty [] array. I want to show the user full name who created that course.
User's ref in the course model:
},
creator: [
{
ref: "Users",
type: mongoose.Schema.Types.ObjectId,
},
],
Here I am trying to get the user who created the course:
//Get all courses
exports.getAllCourses = async (req, res) => {
await Courses.find()
.sort("-created")
.populate("creator", "firstName lastName fullName")
.exec((err, courses) => {
if (err) {
res.status(500).send({ message: error.message });
} else {
res.status(200).send({
message: "All courses fetched successfully",
result: courses,
});
}
});
};
The result I am getting in postman:
{
"message": " All courses fetched successfully",
"result": [
{
"creator": [], <----- Here I am getting the user as an empty array
"_id": "606d0f4d6051bb308089b5d2",
"courseCode": "Math202",
"courseName": "Math linear algebra",
"section": "001",
"semester": 1,
"__v": 0
}
.populate({ path: 'creator', model: 'Users', select: 'firstName lastName fullName' });

Cannot find id and update and increment sub-document - returns null Mongoose/mongoDB

I have a problem where I cannot seem to retrieve the _id of my nested objects in my array. Specifically the foods part of my object array. I want to find the _id, of lets say risotto, and then increment the orders count dynamically (from that same object).
I'm trying to get this done dynamically as I have tried the Risotto id in the req.body._id and thats fine but i can't go forward and try to increment orders as i get null.
I keep getting null for some reason and I think its a nested document but im not sure. heres my route file and schema too.
router.patch("/update", [auth], async (req, res) => {
const orderPlus = await MenuSchema.findByIdAndUpdate({ _id: '5e3b75f2a3d43821a0fb57f0' }, { $inc: { "food.0.orders": 1 }}, {new: true} );
//want to increment orders dynamically once id is found
//not sure how as its in its own seperate index in an array object
try {
res.status(200).send(orderPlus);
} catch (err) {
res.status(500).send(err);
}
});
Schema:
const FoodSchema = new Schema({
foodname: String,
orders: Number,
});
const MenuSchema = new Schema({
menuname: String,
menu_register: Number,
foods: [FoodSchema]
});
Heres the returned Database JSON
{
"_id": "5e3b75f2a3d43821a0fb57ee",
"menuname": "main course",
"menu_register": 49,
"foods": [
{
"_id": "5e3b75f2a3d43821a0fb57f0",
"foodname": "Risotto",
"orders": 37
},
{
"_id": "5e3b75f2a3d43821a0fb57ef",
"foodname": "Tiramisu",
"orders": 11
}
],
"__v": 0
}
the id for the menuname works in its place but i dont need that as i need to access the foods subdocs. thanks in advance.
You are sending food id (5e3b75f2a3d43821a0fb57f0) to the MenuSchema.findByIdAndUpdate update query. It should be the menu id which is 5e3b75f2a3d43821a0fb57ee
You can find a menu by it's id, and update it's one of the foods by using food _id or foodname using mongodb $ positional operator.
Update by giving menu id and food id:
router.patch("/update", [auth], async (req, res) => {
try {
const orderPlus = await MenuSchema.findByIdAndUpdate(
"5e3b75f2a3d43821a0fb57ee",
{ $inc: { "foods.$[inner].orders": 1 } },
{ arrayFilters: [{ "inner._id": "5e3b75f2a3d43821a0fb57f0" }], new: true }
);
res.status(200).send(orderPlus);
} catch (err) {
res.status(500).send(err);
}
});
Update by giving menu id and foodname:
router.patch("/update", [auth], async (req, res) => {
try {
const orderPlus = await MenuSchema.findByIdAndUpdate(
"5e3b75f2a3d43821a0fb57ee",
{ $inc: { "foods.$[inner].orders": 1 } },
{ arrayFilters: [{ "inner.foodname": "Risotto" }], new: true }
);
res.status(200).send(orderPlus);
} catch (err) {
res.status(500).send(err);
}
});

Mongoose find with or operator failing

I have an input with autocomplete and I want to find user by _id, name or email.
This query is working fine but it search just using name or email:
var matchRegexp = new RegExp(_.escapeRegExp(req.query.match), 'i');
User.find(
{$or: [{email: matchRegexp}, {name: matchRegexp}]},
{email: 1, name: 1, _id: 0}
).limit(limitResults).exec(
function(error, users) {
if (error) {
return res.status(400).json({
"message": "Query failed",
});
} else {
return res.json({
"results": users
});
}
}
);
If I try to add _id at the $or it fails searching by name or email but it works with _id:
{$or: [{email: matchRegexp}, {name: matchRegexp}, {_id: req.query.match}]}
How can I let it work using all 3 options?
EDIT:
This is the error:
CastError: Cast to ObjectId failed for value "ayeye" at path "_id" for
model "User"
it is not considering at all name and email...
Because your match string is not a valid ObjectId you are getting this error.
So here you can first check the validation for valid ObjectId and then make a criteria accordingly.
const ObjectId = require('mongoose').Types.ObjectId
const criteria = { $or: [{ email: matchRegexp },{ name: matchRegexp }] }
If (ObjectId.isValid(matchRegexp)) {
criteria.$or.push({ _id: matchRegexp })
}
User.find(criteria, { email: 1, name: 1, _id: 0 }).limit(limitResults).exec()

MongoDB Query Returns Empty Nested Object

I've got a 'conversations' collection in MongoDB which I'm querying from NodeJS to use the returned data to render the conversation's page.
The data has been stored in the database correctly as far as I can see, when I query it everything comes back as I'd expect, apart from a couple of nested objects - the two users that the conversation belongs to.
Here's what I get when I console.log a conversation (note the 'participants' field:
[ { _id: 57f96549cc4b1211abadf28e,
__v: 1,
messages: [ 57f96549cc4b1211abadf28d ],
participants: { user2: [Object], user1: [Object] } } ]
In Mongo shell the participants has the correct info - the id and username for both participants.
Here's the Schema:
var ConversationSchema = new mongoose.Schema({
participants: {
user1:
{
id: String,
username: String
},
user2:
{
id: String,
username: String
},
},
started: Number,
messages: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Message"
}
]
});
Here's the creation of the conversation document:
var conv = {
participants : {
"user1" : {
"id" : req.body.senderId,
"username" : req.body.senderName
},
"user2" : {
"id" : req.body.recipientId,
"username" : req.body.recipientName
}
},
created : Date.now(),
messages : [] // The message _id is pushed in later.
}
Conversation.create(conv, function(err, newConvo){
if(err){
console.log(err);
} else {
newConvo.messages.push(newMessage);
newConvo.save();
}
})
And lastly, in case it's useful, here's the query to Mongo:
// view all conversations a user belongs to
app.get('/messages', function(req, res){
Conversation.find({
$or : [
{"participants.user1.id" : req.user._id},
{"participants.user2.id" : req.user._id}
]
}, function(err, convos){
if(err){
console.log('Error getting Convos ' + err)
} else {
res.render('messages', {convos: convos, currentUser: req.user});
}
});
});
Thanks a lot for any help that!
It seems that everything is alright, the console.log just doesn't print nested objects by default. Try using:
console.log(JSON.stringify(conversation))
When logging a conversation in order to see the participants objects.
Fixed it!
Andresk's answer above was a big shove in the right direction. As he said, everything was OK, but I wasn't accessing the returned object in the correct way. It's obvious now, but I wasn't providing the index number for the 'convos' object.
I simply needed to do this, even though I was only getting one 'conversation' document back from MongoDB:
console.log(convos[0].participants.user1.username);

Cascade delete from array using Mongoose middleware remove hook

I am building a Node.js express RESTfull API using Mongodb and mongoose.
This is my schema:
var UserSchema = new mongo.Schema({
username: { type: String },
password: { type: String, min: 8 },
display_name: { type: String, min: 1 },
friends: { type: [String] }
});
UserSchema.post('remove', function(next){
console.log({ friends: this._id }); // to test if this gets reached (it does)
UserSchema.remove({ friends: this._id });
});
And this is the function that removes a User:
.delete(function(req, res) {
User.findById(req.params.user_id, function(err, user) {
if (err) {
res.status(500);
res.send(err);
} else {
if (user != null) {
user.remove();
res.json({ message: 'User successfully deleted' });
} else {
res.status(403);
res.json({ message: 'Could not find user.' });
res.send();
}
}
});
});
What I need to do is when a user is removed, his or her _id (String) should also be removed from all the other users' friends array. Hence the remove hook in the schema.
Right now the user gets deleted and the hook gets triggered, but the user _id is not removed from the friends array (tested with Postman):
[
{
"_id": "563155447e982194d02a4890",
"username": "admin",
"__v": 25,
"password": "adminpass",
"display_name": "admin",
"friends": [
"5633d1c02a8cd82f5c7c55d4"
]
},
{
"_id": "5633d1c02a8cd82f5c7c55d4",
"display_name": "Johnybruh",
"password": "donttouchjohnsstuff",
"username": "John stuff n things",
"__v": 0,
"friends": []
}
]
To this:
[
{
"_id": "563155447e982194d02a4890",
"username": "admin",
"__v": 25,
"password": "adminpass",
"display_name": "admin",
"friends": [
"5633d1c02a8cd82f5c7c55d4"
]
}
]
To try and figure it out I have looked at the Mongoosejs Documentation, but the mongoose doc example doesn't cover the remove hook. Also this stackoverflow qestion but this question seems to be about removing from other schemas.
I think i'm doing the remove in the hook wrong, but I can't seem to find the problem.
Thanks in advance!
EDIT:
I could not get the first suggestion by cmlndz to work, so I ended up fetching all the documents with arrays that contained the to-be-deleted users' id and pulling it from them one-by-one:
The delete function now contains this bit of code that does the magic:
// retrieve all documents that have this users' id in their friends lists
User.find({ friends: user._id }, function(err, friends) {
if (err) {
res.json({ warning: 'References not removed' });
} else {
// pull each reference to the deleted user one-by-one
friends.forEach(function(friend){
friend.friends.pull(user._id);
friend.save(function(err) {
if (err) {
res.json({ warning: 'Not all references removed' });
}
});
});
}
});
You could use $pull to find all documents that contain the "ID" in the "friends" array -or- find any matching document and popping the "ID" out of the array one by one.

Resources