Mongoose Multiple Level Populate with Refpath - node.js

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

Related

Finding the property by value mongoose

Have a question about finding nested property in mongoose. Can't find the right solution for my issue. Lets say I have Parent with properties: name, child. Same will go for every single child: name, child. Is there a possible way for example to find the value by entering just a name? If the nth children is at nth level with name "Tom" for this moment I have to go like children.name.children.name......
-Parent
-children
-children
-children
My schema looks like:
const TreeSchema = new mongoose.Schema({
name: {
type: "String",
unique: true,
required: true,
},
children: [
{
name: {
type: "String",
},
},
],
date: {
type: Date,
default: Date.now,
},
});
request image
So the issue is that if I have nth child I can't just find him like this. I have to keep going deep every time like children.name : req....
router.post("/:parent/:child", async (req, res) => {
let tree = await Tree.findOne({name : req.params.child})
]);

Nodejs sequelize hasMany issue

I've breaking my head over this sequelize to get the products questions and also to include its answers as well
const ProductQuestions = sequelize.define('product_questions', {
user: {
type: Sequelize.BIGINT
},
product: {
type: Sequelize.BIGINT
},
question: {
type: Sequelize.TEXT
}
});
ProductQuestions.associate = function(models) {
ProductQuestion.hasMany(models.product_answers,{
foreignKey: 'question',
as: 'questionId'
});
}
const ProductAnswer = sequelize.define('product_answers', {
question: {
type: Sequelize.BIGINT,
field: 'questionId'
},
answer: {
type: Sequelize.TEXT
},
user: {
type: Sequelize.BIGINT,
field: 'userId'
}
});
ProductQuestiosn.findAll({include: ['product_answers']});
for some reason when I that, the columns is wrong when the query runs
SELECT
"product_questions".*,
"product_answers"."id" AS "product_answers.id",
"product_answers"."questionId" AS "product_answers.questionId",
"product_answers"."answer" AS "product_answers.answer",
"product_answers"."userId" AS "product_answers.user",
"product_answers"."productQuestionId" AS "product_answers.productQuestionId"
FROM (
SELECT
"product_questions"."id",
"product_questions"."productId" AS "product",
"product_questions"."question",
"product_questions"."userId" AS "user",
FROM
"product_questions" AS "product_questions")
AS "product_questions"
LEFT OUTER JOIN "product_answers" AS "product_answers"
ON "product_questions"."id" = "product_answers"."productQuestionId"
not sure why is
ON "product_questions"."id" = "product_answers"."productQuestionId"
when it should be
ON "product_questions"."id" = "product_answers"."questionId"
thank you for your help
so i figured it out!
so it appears that I have to name my columns properly
for the product_answers table in my postgres database, I had the column questionId but it should be named productQuestionId. I guess it's for naming convention, i can't just name the foreign key the way i want.

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!

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

Resources