Reputation: 2296
I want to collect the data with its child (another item which is bound to that product ID) I have a collection with this schema
// User Schema
const filtersSchema = mongoose.Schema({
filter_name:{
type: String,
required: true
},
filter_code:{
type: String,
required: true,
unique: true
},
bind_to: {
type: Schema.Types.ObjectId,
default: null
},
filter_status:{
type: Boolean,
default: true
},
created_on:{
type: Date,
default: Date.now
},
updated_on:{
type: Date,
default: Date.now
}
});
If I enter a data to it, default bind_to value will be null, that means its parent. If I send the bind_to ID of a parent, it'll be ObjectID.
I want to collect data like this
[{
-- parent object --
children:[
{
-- child object --
},
{
-- child object --
}
]
}]
if we have more than one item it'll go through loop (forEach) but the callback is getting sent before the forEach loop getting finished. I know forEach is asynchronous and the request is synchronous. but confused about how to do that!
you can see the module below
// Get Filters by Parent ID
module.exports.getFiltersByParentId = (pid, callback) => {
Filters.find({bind_to: pid}, callback);
}
//For getting the parent object and looping it to get its child objects
module.exports.getFilters = (callback, limit) => {
Filters.find({bind_to: null}, (err, filters) => {
if (err) {
console.log(err);
let obj = {status: false, error: err.errmsg};
callback(obj);
} else {
const resObj = [];
filters.forEach(async function (ele) {
await Filters.getFiltersByParentId(ele._id, (err, cfil) => {
if (err) {
let obj = {status: false, message: err.errmsg};
callback(obj);
} else {
console.log(ele, "Obj");
ele.children = cfil;
resObj.push(ele);
}
});
});
Promise.all(resObj).then(res => {
let obj = {status: true, data: res, message: "Filters found"};
callback(obj);
});
}
});
}
but in this case result object will be empty. how can I get the proper object with the values as mentioned above?
Even I tried with this method
const resObj = [];
filters.map(function (ele) {
Filters.getFiltersByParentId(ele._id, (err, cfil) => {
if (err) {
let obj = {status: false, message: err.errmsg};
callback(obj);
} else {
console.log(ele, "Obj");
ele.children = cfil;
resObj.push(ele);
}
});
});
Promise.all(resObj).then(res => {
let obj = {status: true, data: res, message: "Filters found"};
callback(obj);
});
And This
Promise.all(filters.map(function (ele) {
Filters.getFiltersByParentId(ele._id, (err, cfil) => {
if (err) {
let obj = {status: false, message: err.errmsg};
callback(obj);
} else {
console.log(ele, "Obj");
ele.children = cfil;
resObj.push(ele);
}
});
})).then(res => {
let obj = {status: true, data: res, message: "Filters found"};
callback(obj);
});
Ok, Now im returning a promise from getFiltersByParentId
module.exports.getFiltersByParentId = (pid, callback) => {
return new Promise(function(resolve, reject) {
Filters.find({bind_to: pid}, function (err, results) {
if (err) {
reject(err);
} else {
resolve(results);
}
})
});
}
Upvotes: 0
Views: 88
Reputation: 2296
After making some changes to my question and using @Jaromanda X's & @CertainPerformance guide I came up with a solution for my question. it's not exactly as I wanted, but it's quite the same output.
I changed gFilters.getFiltersByParentId
method to return Promise
module.exports.getFiltersByParentId = (pid, callback) => {
return new Promise(function(resolve, reject) {
Filters.find({bind_to: pid._id}, function (err, results) {
if (err) {
reject(err);
} else {
let obj = {parent: pid, children: results};
resolve(obj);
}
})
});
}
Then changed forEach
to array.map
and used promise array in Promise.all
function
module.exports.getFilters = (callback, limit) => {
Filters.find({bind_to: null}, (err, filters) => {
if (err) {
console.log(err);
let obj = {status: false, error: err.errmsg};
callback(obj);
} else {
const resObj = [];
var promises = [];
filters.map(function (ele) {
promises.push(Filters.getFiltersByParentId(ele, (err, cfil) => {
if (err) {
let obj = {status: false, message: err.errmsg};
callback(obj);
} else {
console.log(ele, "Obj");
ele.children = cfil;
resObj.push(ele);
}
}));
});
Promise.all(promises).then(res => {
let obj = {status: true, data: res, message: "Filters found"};
callback(obj);
});
}
});
}
Finally, my output looks something like this
[
{
parent: {
--- parent object ---
},
children: [
{
--- child object 1 ---
},
{
--- child object 2 ---
},
{
--- child object n ---
}
]
}
]
Upvotes: 0
Reputation: 1
Firstly, since Filters.getFiltersByParentId
doesn't return a promise, there is no sense in await
ing the response - so, I wrapped it in a new Promise - if I had time there's probably a simpler way to do this without bothering with async/await, since there's actually no promises at all in the code except for the promise no introduced to make use of async/await
Still, I believe the code is simpler to read this way, so, lets keep with the async/await and promise theme
Secondly, using for ... of
loop makes the code very simple, especially since you want any error to cease further calls to Filters.getFiltersByParentId
code as follows
module.exports.getFilters = (callback, limit) => {
Filters.find({bind_to: null}, async (err, filters) => {
if (err) {
console.log(err);
let obj = {status: false, error: err.errmsg};
callback(obj);
} else {
const resObj = [];
for (const ele of filters) {
try {
let result = await new Promise((resolve, reject) => {
Filters.getFiltersByParentId(ele._id, (err, cfil) => {
if (err) {
let obj = {status: false, message: err.errmsg};
reject(obj);
} else {
console.log(ele, "Obj");
ele.children = cfil;
resolve(ele);
}
});
});
resObj.push(result);
} catch(e) {
return callback(obj);
}
}
let obj = {status: true, data: resObj, message: "Filters found"};
callback(obj);
}
});
};
edit: I had time :p
Here is the code without async/await, since there are no promises
module.exports.getFilters = (callback, limit) => {
Filters.find({bind_to: null}, (err, filters) => {
if (err) {
console.log(err);
let obj = {status: false, error: err.errmsg};
callback(obj);
} else {
const resObj = [];
const getFilters = (index) => {
if (index < filters.length) {
const ele = filters[index];
Filters.getFiltersByParentId(ele._id, (err, cfil) => {
if (err) {
let obj = {status: false, message: err.errmsg};
callback(obj);
return;
} else {
console.log(ele, "Obj");
ele.children = cfil;
getFilters(index + 1);
}
});
} else {
let obj = {status: true, data: resObj, message: "Filters found"};
callback(obj);
}
};
getFilters(0);
}
});
};
Upvotes: 1