I am trying to retrieve data in a tree format from Mongodb recursively using Mongoose in a Node Express app. I'm trying to do it with promises and I am clearly not doing it right.
Say you have the mongodb table foos
':
{
"_id": {
"$oid": "5b5fb27232db7b13a1090577"
},
"bazs": [
{
"$oid": "5b626a2e325c181bdf4fba08"
}
],
"bars": [
{
"$oid": "5b5fb3cc32db7b13a1090579"
}
],
"name": "Parent1",
"accountId": "5b5fb27132db7b13a1090576",
"__v": 0
}
So this has two different kind of child objects. Also, the baz
objects have other baz
objects as children, as well as bar
objects. Say this is the table that the id for bars
:
{
"_id": {
"$oid": "5b626a2e325c181bdf4fba08"
},
"bazs": [
{
"$oid": "5b5fb3cc32db7b75fa01"
}
],
"bars": [
{
"$oid": "5b5fb3c7cdaf740a500a"
}
],
"accountId": "5b5fb27132db7b13a1090576",
"parent": {
"$oid": "5b5fb27232db7b13a1090577"
},
"__v": 0
}
I've been using a recursive function in a thunk waiting for an object type to retrieve using Mongoose.
const getObjectRecursive = (type, id)=>{ //'type' will be 'Foo' or 'Bar'
return new Promise((resolve, reject)=>{
type.findOne.call(type, {_id:id},{}, err=>{
if(err){
reject(err);
}
})
.then((rslt)=>{
const result = rslt;
const bars = result.bars;
const bazs = result.bazs;
result.children = {};
result.children.bars = tasks.map(async barId=>{
const barIdString = barId.toString();
await getBarRecursive(barIdString, err=>{
if(err){
reject(err);
}
});
});
result.children.bazs = bazs.map(async eventId=>{
const bazIdString = bazId.toString();
await Baz.findOne({_id:eventIdString}, {},err=>{
if(err){
reject(err);
}
});
});
resolve(result);
})
});
}
And I use it in these two functions:
const getBarRecursive = (taskId)=>{
return getObjectRecursive(Bar, taskId);
}
const getFooRecursive=(categoryId)=>{
return getObjectRecursive(Foo,categoryId);
};
Which are called by this route:
router.get('/:id', verifyToken, (req,res)=>{
Branch.getFooRecursive(req.params.id)
.then(
(foo)=>{
res.status(200).send(cat);
},
(err)=>{
res.status(500).send(err);
}
)
});
This is meant to return the tree object recursively and all the objects' children. What's happening is that the children objects result.children.bars
and result.children.bazs
become promises that never get fulfilled.
This is not the best way to do this, I know. I'm clearly not doing promises right. I also thought about retrieving the table using a $graphLookup, but I can't figure out how to do that since the children come from different collections.
How can I make the app retrieve the children to any depth?
UPDATE: This is still not working. I've changed the recursive function to this:
const getObjectRecursive = async (type, id)=>{
const rslt = await type.findOne.call(type, {_id:id},{}, {}, err=>{
if(err){
return({err: err});
}
});
const result = rslt.toObject();
const bars = result.bars;
const bazs = result.bazs;
result.children = {};
result.children.bars = tasks.map(async barId=>{
const barIdString = barId.toString();
await getBarRecursive(barIdString, err=>{
if(err){
reject(err);
}
});
});
result.children.bazs = bazs.map(async eventId=>{
const bazIdString = bazId.toString();
await Baz.findOne({_id:eventIdString}, {},err=>{
if(err){
reject(err);
}
});
});
return result
};
Everything else is the same. What's happening now is that the promises containing the bar
result isn't being fulfilled until AFTER the result is sent to the client, so I am getting an empty object in it's place, like this:
{
"bars": [
"5b626a2e325c181bdf4fba08"
],
"bazs": [],
"_id": "5b5fb27232db7b13a1090577",
"__v": 0,
"children": {
"bars": [
{}
],
"bazs": []
}
}
Some things I've tried:
- placing the
bars.map(async barId=>...
etc. inside a Promises.all statement and assigning it toresult.children
in a.then(...)
statement - placing the
bars.map(async barId=>...
etc. inside a separate async function and then sayingresult.children.bars = await getAllTasks(tasks)
How can I make sure my promise is fulfilled before the result is sent?