Mongoose - Lean with Deep population - node.js

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.

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.

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

Mongoose multiple deep populates

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

Looping through objects and adding them to Sequelize db

I have an array of objects that I like to put into SQL via Sequelize and I'm running into issues.
[
{ owner: false,
id: '2342365',
name: 'awrAPI' },
{ owner: false,
id: '5675689699',
name: 'TgatAPI' },
{ owner: true,
id: '57456767',
name: 'ytasyAPI' }
[
Currently the way i have it set up is through a simple for in.
for( var key in guilds ) {
Guild
.findOrCreate({where: {primid: guilds[key].id}, defaults: {
primid: guilds[key].id,
name: guilds[key].name,
owner: guilds[key].icon
}})
.spread(function(guild, created) {
console.log(guild.get({
plain: true
}))
console.log(created)
})
}
I assume this is wrong and was wondering if there is a better way to loop through my object and chain the findorcreates. Currently it goes through the first object, but then does not add any more. I've been looking into using Bluebird, but I'm not too familiar with using promises. Thoughts?
var myData = [
{
owner: false,
id: '2342365',
name: 'awrAPI'
},
{
owner: false,
id: '5675689699',
name: 'TgatAPI'
},
{
owner: true,
id: '57456767',
name: 'ytasyAPI'
}
];
Promise.all(myData.map(function(value) {
// Do your thing
return Guild.findOrCreate...
})).then(function() {
// All is resolved do your next thing
})
An alternative would be to utilize squelize's bulkCreate model method for multiple records insertion.
link to sequelize's bulkCreate docs
quick snippet here:
const dataForInsertion = [
{
username:"daniel",
currentORM:"Sequelize"
},
{
username:"lekan",
currentORM:"Mongoose"
},
]
Guild.bulkCreate(dataForInsertion)
.then(()=>{})
.catch(()=>{})

Resources