Mongoose multiple deep populates - node.js

Say, I have Manager schema:
{
name: { type: String },
clients: [{ type: Mongoose.Schema.ObjectId, ref: 'Client'}]
}
And, I have Client schema:
{
name : { type String },
cars : [{ type: Mongoose.Schema.ObjectId, ref: 'Car' }],
houses: [{ type: Mongoose.Schema.ObjectId, ref: 'House' }]
}
And Car and House as well (their structure is not important for the matter. How do I deep populate multiple Client's fields in the single .populate() call?
What have I tried:
Manager.find()
.populate({
path: 'users',
populate: { path: 'cars' },
populate: { path: 'houses' }
});
and it would truly surprise me if it worked (as I overwrite previously declared populate key passed to the .populate() method). My example obviously returns populated houses field per user - the one which comes last. Honestly, no idea, nothing in the docs either. Is it even supported? I know it is for 'shallow' populate, based on my example:
User.populate('cars')
.populate('houses')
What about the deep one?

1) Pass an array of objects with properties to be populated:
Manager.find()
.populate({
path : 'users',
populate: [
{ path: 'cars' },
{ path: 'houses' }
]
});
This solution enables to use deep populate to its full extent. Consider:
Manager.find()
.populate({
path : 'users',
populate: [
{ path: 'cars' },
{
path : 'houses',
populate: {
path : 'rooms',
populate: 'windows'
}
}
]
});
2) Pass string of space-delimited collections to be populated:
Manager.find()
.populate({
path : 'users',
populate: 'cars houses'
});

This is the way to do nested population. In this case First it will populate users and then populate hobbies of user and then populate football from hobbies of user and then it will populate favourite playerNames from user -> hobbies -> football -> favouritePlayers.
Model.find()
.populate({
path : 'users',
populate: [
{ path: 'Hobbies' },
{
path : 'Football',
populate: {
path : 'favouritePlayers',
populate: 'playerNames'
}
}
]
});

Related

Mongoose Multiple Level Populate with Refpath

I'm trying to use Mongoose populate() feature with a nested path but WITH refpath:
Child schema:
let child = new Schema({
item: {
type: Schema.Types.ObjectId,
required: true,
refPath: "itemType",
},
itemType: {
type: String,
required: true,
enum: ["typeA", "typeB"],
},
});
Parent schema:
let parent = new Schema({
children: [child],
});
After building my models, I run a query:
let mQuery1 = Parent.find().populate({
/* Type 1, does not populate at all. */ path: "children",
populate: { path: "item" },
});
This just does nothing.
let mQuery2 = Parent.find().populate({
path: "children",
populate: [
{ path: "item", model: "typeA" },
{ path: "item", model: "typeB" },
],
});
And this query ends up populating one type of the fields and the others not(e.g, populates typeA and returns null for typeB(s)).
So my question is, how do I come about populating nested fields that utilise refPath?
Cheers.
I've found the answer, thought I would share
you won't need to populate model like this:
<br>
let mQuery2 = Parent.find().populate({<br>
path: "children",<br>
populate: [<br>
{ path: "item", model: "typeA" },<br>
{ path: "item", model: "typeB" },<br>
],<br>
});<br>
but just:
<br>
let mQuery2 = Parent.find().populate({ <br>
path: "children", populate: <br>
{path: "item"}<br>
})<br>
However, in your posting route, you must have itemType with the value of either typeA or typeB. The respective model will then be populated automatically. I also managed to pass the chosen model to the itemType automatically on the backend only so no one needs to manually key in the model name.
here's the link that has helped me with this issue:
https://github.com/Automattic/mongoose/issues/7273

Make many populate in deep populate, 2nd level

I am doing a query of nested objects using populate and when wanting to access a population of the second level, it does not recognize me if not the last population
I'm working under nodejs and mongodb, using the mongoose library
For the population of 'tours_favoritos' only recognizes me the last: 'guia' with its population of 'idiomas'. I do not populate 'sitios', nor 'turistas', nor 'categoria', nor 'empresa_turismo'. If I change the order of the population, only the last one populates me.
servicev1.addGustoFavorito = function (id_turista, id_gusto) {
return new Promise(function (resolve, reject) {
GustoTurista.findById(id_gusto, function (error, gusto) {
if (!error) {
var query = Turista.findByIdAndUpdate(id_turista, { $push: { gustos: gusto._id } }, { new: true });
query.
populate({
path: 'empresas_favoritas', model: EmpresaTurismo
}).
populate({
path: 'tours_favoritos',
model: Tour,
populate: { path: 'sitios', model: Sitio },
populate: { path: 'turistas', model: Turista },
populate: { path: 'categoria', model: CategoriaTour },
populate: { path: 'empresa_turismo', model: EmpresaTurismo },
populate: {
path: 'guia', model: Guia,
populate: { path: 'idiomas', model: Idioma }
}
}).
populate({
path: 'gustos', model: GustoTurista
})
.exec(function (err, success) {
if (err) {
return reject(err);
}
else {
return resolve(success);
}
})
} else {
return reject(error);
}
})
})
}
See code
The mistake here is in this second populate query:
.populate({
path: 'tours_favoritos',
model: Tour,
populate: { path: 'sitios', model: Sitio },
populate: { path: 'turistas', model: Turista },
populate: { path: 'categoria', model: CategoriaTour },
populate: { path: 'empresa_turismo', model: EmpresaTurismo },
populate: {
path: 'guia', model: Guia,
populate: { path: 'idiomas', model: Idioma }
}
})
You are passing an object to populate function, and everytime you are using another populate inside that object, it overrides the old one, it is similar to any object definition.
Eg:
var a={
foo : "bar",
foo : "bar2",
foo : "bar3"
}
//the last foo overrides the first two
//a.foo = bar3
just like above example, last populate is overriding the previous populates.
Now back to solve your question,
You need to pass array to populate field, so that it populates all the given fields you want
Try this:
.populate({
path: 'tours_favoritos',
model: Tour,
populate: [
{ path: 'sitios', model: Sitio },
{ path: 'turistas', model: Turista },
{ path: 'categoria', model: CategoriaTour },
{ path: 'empresa_turismo', model: EmpresaTurismo },
{
path: 'guia', model: Guia,
populate: { path: 'idiomas', model: Idioma }
}
]
})
replace the above code in place of your second populate query and it will work.

Mongoose - Lean with Deep population

I'm using mongoose to populate couple of objects together, when i try to access the parent object and add attribute to it, all is fine, but when editing an attribute inside an object of the parent, changes aren't applied.
let order = await Order.findOne({ _id: req.params.id }).populate({
path: 'style',
match: { removed: false },
populate: [{
path: 'processes',
match: { removed: false },
populate: { path: 'machine_type' }
},
{ path: 'sizes' }]
}).lean();
When I try order.style.processes[i].anyAttribute= AnyValue; the object remains unchanged.

Mongoose Population not working returning [Object] only not the fields

Hey guys i am trying to fetch some data using mongoose from MongoDB.
it is not even populating the first level .
i will try my best to explain the situation below.
I have my documents structure like below:
Schema[s]
- UsersSchema
_id : { type: mongoose.Schema.Types.ObjectId },
userData : [{ type: mongoose.Schema.Types.ObjectId, ref: 'DataType' }],
- DataTypeSchema
_id : { type: mongoose.Schema.Types.ObjectId },
data : [{ type: mongoose.Schema.Types.ObjectId, ref:'Data' }],
- DataSchema
_id : { type: mongoose.Schema.Types.ObjectId },
desc: String
Filling the data :
in User > userData : ["dataTypeID" , "dataTypeID" ]
in DataType > data : ["dataID" , "dataID" ]
in Data > desc: "pending"
The problem:
i am trying to get the desc field of Data in my User document.
What i tried :
1 try
await User.find().populate('userData').lean();
response : it should populate the DataType model . instead i am looking at [ [Object], [Object] ],
[{
_id: 5b37aa4638a07505e809191a,
userData: [ [Object], [Object] ],
__v: 0
}]
2 try
await User.find().populate('userData.data').lean();
i tried to do some thing which i don't know what it will do :D
response : and now it returns the id's of the DataType model
[{
_id: 5b37aa4638a07505e809191a,
userData: [ 5b35577759407235a4293020, 5b355fb5f51a0c1de0a3ab3d ],
__v: 0
}]
3 try
then after reading the mongoose documentation :
await User.find().populate({
path: 'userData',
model: 'User',
populate: {
path: 'data',
model: 'DataType',
}
}).lean();
response : got and empty array userData : []
[{
_id: 5b37aa4638a07505e809191a,
userData: [ ],
__v: 0
}]
note : if i remove the model: 'User' from the first populate i get the same response as .populate('userData') = [ [Object], [Object] ],
after allot of searching in answers i fond this : mongoose-deep-populate to deep populate which is also working as the normal mongoose populate no effect at all.
it just doesn't make sense to me that why i can't even populate the DataType in the User :(
i hope any one will help me out in this weird kinda situation .
thanks
You were almost going in the right direction with the 3rd try, but you gave the wrong model to populate from.
This is indeed a problem of nested population or deep population, as you found out, but you need to pass the correct model while populating,
Since userData is a _id of DataType collection, the model should be DataType and not User, similarly data is a _id of Data collection, the model should be Data not DataType. Make these changes and it should work as expected.
model in .populate() is to tell mongodb/mongoose which collection to populate it from.
Try this :
await User.find().populate({
path: 'userData',
model: 'DataType',
populate: {
path: 'data',
model: 'Data',
}
}).lean();
Also, Since you have defined Ref in the schema as well, you don't actually need to pass model in the populate, it will automatically know which collection to populate from.
Below query should work as well:
await User.find().populate({
path: 'userData',
populate: {
path: 'data'
}
}).lean();
Hope this helps!

what's wrong with mongoose populate with match?

I am trying to populate a field with the match in mongoose but it does not return anything.
Code:
// provided in Mongoose document.
Story.
find(...).
populate({
path: 'fans',
match: { age: { $gte: 21 }},
select: 'name -_id',
options: { limit: 5 }
}).
exec();
my mongoose version is:- "mongoose": "^4.13.12"
real code:-
let populateQuery = [{
path: 'service',
select: 'name pricessss'
match: { name: { $eq: 'someting' }}
}, {
path: 'job',
select: 'appliedUser'
}];
models.Project.find()
.populate(populateQuery)
.exec((err, projects) =>{})
o/p:- In projects.job the populated data is there but on projects.service it's returning null

Resources