NodeJS - Mongoose Populate Nested Items - node.js

I am trying to populate a GET request.
This my model/schema:
const turnosSchema = mongoose.Schema({
turno: { type: Number, required: true},
nombre: { type: String, required: true},
hora_inicio: { type: String, required: true},
hora_fin: { type: String, required: true},
menus: { type: Array, required: true},
cod_vestir: { type: mongoose.Schema.Types.ObjectId, ref: 'cod_vestir', required: true},**//This is the item I want to populate with another collection called "codsvestirs"**
})
const LandingAyBSchema = mongoose.Schema({
titulo: { type: String, required: true},
especialidad: { type: String, required: true},
descripcion: { type: String, required: true},
observaciones: { type: String, required: true},
turnos: [turnosSchema],**//This is an array with a "turnosSchema"**
orden: { type: Number, required: true},
activo: { type: Number, required: true},
imagen: {type: String, required: true},
lang: { type: String, required: true, maxLength: 2},
})
Heres is a pic of the data hierarchy.
Here is the code I have so far. It doesn't have the actual populate code cause I have not been able to make it work. I've tried the solution from this thread which I think better suits my scenario but it didn't work Populate nested array in mongoose
exports.getAllLandingAyB = async (req, res, next) => {
const query_result = await LandingAyB.find({activo: 1}).sort({orden: 1});
if (!query_result) {
res.status(500).json({success: false})
}
res.status(200).json({
success: true,
cuantosLandings: query_result.length,
data: query_result
});
}

You need to set the type as a mongoId and then refer to the selected collection. For example on the turnos property
turnos: [{ type: Schema.Types.ObjectId, ref: 'Turnos' }]
I'm asumming the Turnos is the name of the collection, and the query shoud look like this
const query_result = await LandingAyB.find({activo: 1})
.populate('turnos')
.sort({orden: 1});.
you can find a good example on the populate documentation

Related

How to filter documents on mongodb based on reference populated subdocuments?

I am trying to grab documents based on populated subdocuments.
Here are my models
// User model
var UserSchema = new mongoose.Schema({
username: {type: String, required: true, trim: true},
firstName: {type: String, required: true, lowercase: true},
lastName: {type: String, required: true, lowercase: true},
phone: {type: String, required: false},
email: {type: String, required: true},
password: {type: String, required: true},
blogs: {type: mongoose.Schema.Types.ObjectId, ref: 'Blogs'}
}, {timestamps: true});
// Blog Model
var BlogSchema = new mongoose.Schema({
description: String,
tags: [String],
other: [Object],
}, {timestamps: true});
This is how I am grabbing documents
fetchAllByFilter: async function(req, res) {
try {
let result = await Users.find({}).populate('blog');
return res.status(200).send(result);
} catch (err) {
return res.status(200).send({error: err});
}
},
Now my main question is, how would I grab Users based on their Blogs referenced documents?
For example, Find Users with Blogs that has Blog.tags of "food", "cars", "movies" and/or Blog.other of [{...SomeObject}, {...SomeOtherObject}]
looking at mongo docs match an array, you could make a utility function somewhat like this...
async function findByTag(tag) {
const blogIds = await Blog.find({ tags: tag }).select("_id");
const users = await User.find({
blogs: { $in: blogIds.map((blog) => blog._id) }
}).populate("blog");
}

How to make array ref in Mongoose

Here actually I want to make the service collection that contain the array of references of the ratings. when a user rate a service than an element is pushed in the array containing reference of user , service ID no and the rating.
Service Model like this:
var ServiceSchema = new Schema({
user_id:{
type: String,
required: [true, 'please provide user id']
},
name: {
type: String,
required: [true, 'please enter your name']
},
rating : [{ type: Schema.Types.ObjectId, ref: 'rating' }],
});
Rating schema:
var RatingSchema = Schema({
S_id : { type: Schema.Types.ObjectId},
Rating : Number,
By : { type: Schema.Types.ObjectId}
});
user schema:
var UserSchema = new Schema({
id: {
type: String,
unique: true,
required: [true, 'please enter your id']
},
password: {
type: String,
required: [true, 'please enter your password']
},
name: {
type: String,
required: [true, 'please enter your name']
},
type: {
type: [{
type: String,
enum: ['visitor', 'seller']
}],
default: ['visitor']
},
});
and I have defined the export as:
module.exports = mongoose.model('user', UserSchema, 'users');
module.exports = mongoose.model('service', ServiceSchema, 'service');
module.exports = mongoose.model('rating', RatingSchema, 'rating');
I want to make a function called rate but I am not getting how to make it.
exports.rate = function(req, res) {
var curr_service = new Service(req.body, result);
new_service.save(function(err, service) {
if (err)
res.send(err);
res.json(service);
});
};
So far I have done this.
Can someone help me to understand what should I do now? because I haven't find that much about mongoose to add ref in array...
In my case. This error was happening because instead of putting {es_indexed: true} inside the object declaration, I was putting it in the object that was using. For example:
const Client: Schema({
name: {type: String, es_indexed: true},
address: {type: Adress, es_indexed: true} //Wrong, causing error
})
Adress: Schema({
address: {type: String},
zipCode: {type: Number}
})
The correct way to use, is putting es_indexed: true into primitive types inside "Adress" schema declaration.
const Client: Schema({
name: {type: String, es_indexed: true},
address: {type: Adress} //Right way
})
Adress: Schema({
address: {type: String, es_indexed: true},
zipCode: {type: Number, es_indexed: true}
})
I hope it was helpful

How to make some fields not updatable once saved in mongoose?

I have build a schema as follows:
const UserInfoSchema = new Schema({
email: { type: String, required: true, unique: true },
username: { type: String, required: true, unique: true },
userId: { type: Schema.Types.ObjectId, ref: 'User'},
displayName: { type: String, required: true },
profilePic: {
filename: {type: String},
url: {type: String}
},
created_at: Date,
updated_at: Date
})
What I need here is once the fields such as email, username and userId are saved, should not be modified. Is there anything pre-build in mongoose for this kind of feature?
I have done some research on schema.pre('update', (next) => {}), but got nothing really useful/don't know if one can use for the mentioned feature. Any help on this matter is greatly appreciated. Thanks in advance.
There is an easier way
when you save the Schema, you can set the field as immutable, like this
const UserInfoSchema = new Schema({
email: { type: String, required: true, unique: true, immutable:true },
username: { type: String, required: true, unique: true, immutable:true },
userId: { type: Schema.Types.ObjectId, ref: 'User', immutable:true},
displayName: { type: String, required: true },
profilePic: {
filename: {type: String},
url: {type: String}
},
created_at: Date,
updated_at: Date
})
it won't throw any error, if you want it you should check it elsewhere, but when you try to modify the immutable fields, it wont be changed at all
for(const key in userUpdates) {
switch(key) {
case 'username':
case 'email':
throw new Error('These field/s cannot be changed anymore');
}
}
User.findByIdAndUpdate(id, userUpdates, { new: true, runValidators: true });

Create query for a child to use the parameters of the parent

Could you help me please.
I searched the internet and could not find any solutions.
How create query for a child to use the parameters of the parent?
var Photos = new Schema({
photo_id: {type: String, required: true},
photo_path_low: { type: String, required: true }
});
var Users = new Schema({
user_id: { type: String, required: true },
count_coins: { type: Number, default: 20 },
photos_relation: [Photos]
});
...
... some code
...
PhotoModel.findOne().where('parent.count_coins').gt(1)..... // parent for Example
For this case there are Object-references:
var Photos = new Schema({
photo_id: {type: String, required: true},
photo_path_low: { type: String, required: true }
createdBy: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
});
Then, when you make your query, you can populate references like this:
Photo.findOne({_id: 123})
.populate('createdBy')
.exec(function(err, post) {
// do stuff with post
});
You can find more in this mongoose documentation.

Schema Association in Mongoose

I have 2 models:
Here is the User Model:
const userSchema = new mongoose.Schema({
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
passwordResetToken: String,
passwordResetExpires: Date,
facebook: String,
twitter: String,
tokens: Array,
profile: {
name: String,
gender: String,
location: String,
website: String,
picture: String
}
}, { timestamps: true });
And here is the Revive Model:
const reviveSchema = new mongoose.Schema({
reviveShowName: {type: String, required: true},
reviveTitle: {type: String, required: true},
reviveCategory: {type: String, required: true},
reviveGoal: {type: Number, required: true},
revivePhoto: {type: String, required: true},
reviveVideo: {type: String},
reviveStory: {type: String},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
name: String
}
}, { timestamps: true });
In the Revive model, I'm trying to the reference the author and get the author's id and that works... How do I also get the name from profiles -> name...? Clearly name: String is wrong...
Mongoose relations work, based on the ref and type value of the nested object. In your case you have associated the id property of author to point to the User model.
If you want to populate the author with the user information, you should just do :
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
Then in your query you just use populate
Revive.find({})
.populate( 'author' )
.exec( function( error, docs ) {
console.log( docs ); // will have `[{author:{profile:{...}}}]` data
} );

Resources