Populating nested properties with Mongoose - node.js

In the API I'm trying to write with Node and Mongoose, the following query:
User.findOne({username: req.params.username}, "-_id -__v")
.populate({path: "songs", select: "-_id -__v"})
.populate({path: "following", select: "-_id username email"})
.exec(function(err, user) {
res.send(user);
});
returns the following JSON:
{
"email": "alice#alice.org",
"username": "alice",
"following": [
{
"username": "john",
"email": "john#john.org"
}
],
"songs": [
{
"slug": "dvorak-cello-concerto",
"artist": "5403e825cc9c45e9c55c4e7d",
"title": "Cello Concerto"
}
]
}
In the songs schema, I've setup artist as the following:
artist: {type: Schema.Types.ObjectId, ref: 'User'}
What is the best way to also populate the 'artist' property in every song that's populated in the initial query, rather than just its _id which references the user that song belongs to?

I figured out a way to do this, but please correct me if there's a better/cleaner way of doing it.
User.findOne({username: req.params.username}, "-_id -__v")
.populate({path: "songs", select: "-_id -__v"})
.exec(function(err, user) {
Songs.populate(user, {
path: 'songs.artist',
select: '-_id username',
model: 'User'
}, function (err, user) {
res.send(user);
});
});
Also, I'm only returning the 'username' value for the user which is all I need, but 'artist' still ends up being an object with that one property. If anybody knows how to return that value just as a string.
I'd love to know how.

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' });

Id not getting removed from nested array

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

Find documents that contain certain value for sub-object field Mongoose

Note: I asked this question here, however at that time I was working purely with MongoDB, now I am trying to implement this with Mongoose. I decided it was appropriate to ask a separate question as I believe the answer will be fundamentally different, however please let me know if I was incorrect in that decision.
I have a collection with the following format:
[
{
firstname: 'Joe',
lastname: 'Blow',
emails: [
{
email: 'test#example.com',
valid: false
},
{
email: 'test2#example.com',
valid: false
}
],
password: 'abc123',
_id: 57017e173915101e0ad5d94a
},
{
firstname: 'Johnny',
lastname: 'Doe',
emails: [
{
email: 'test3#example.com',
valid: false
}
],
password: 'abc123',
_id: 57017e173915101e0ad5d87b
},
]
I am trying to find a user based on the emails.email field. Here is what I have so far:
UserModel.find()
.where('emails')
.elemMatch(function (elem) {
elem.where('email').equals(userEmail);
})
.limit(1)
.exec(
(err, usersReturned) => {
console.log('test2#example.com');
});
What am I doing wrong? I am new to Mongoose and just can't seem to figure this out.
You could do something like this :
UserModel.find({"emails.email": userEmail}).limit(1).exec(function(err, user){
if(err) console.log("Error: " + JSON.stringify(err));
else if(user) console.log("User Returned is : " + JSON.stringify(user));
});
You can use Mongodb aggregate function .Use $unwind on "emails.email" field and it will separate the array make as independent documents.
UserModel.aggregate( [
{ $unwind : "$emails" },
{ $match: {$emails.email:"email you want to put"}}
],function(err,result){
//write code but you want to do
});

Sequelize attributes in findOne returning all fields

I have
db.User.findOne({
attributes: ['id', 'firstName', 'lastName', 'email', 'phoneNumber', 'createdAt', 'type', 'status'],
where: {
id: id
}
}).then(function(dbUser) {
console.log(dbUser);
});
And it's returning all of the fields, not just the ones I specify in attributes. What am I doing wrong?
According to the docs, you're doing absolutely nothing wrong. I'm seeing similar behavior. Sequelize seems to be going through some growing pains. :\
The code provided works with version 7 at least.
const user = await User.findOne({
attributes : ['id','name','email','contact'],
where: {email:req.body.email}
});
Response from postman
{
"status": "success",
"user": {
"id": 1,
"name": "admin",
"email": "admin#eatanddrink.io",
"contact": "0724466628"
},
}
db.animals.findOne({ },{_id:0, numlegs:1,class:1, name:1}) returns only the specific fields.

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