在递归节点函数中从 Mongoose 检索树数据
Posted
技术标签:
【中文标题】在递归节点函数中从 Mongoose 检索树数据【英文标题】:Retrieve tree data from Mongoose in recursive node function 【发布时间】:2018-08-03 23:50:11 【问题描述】:我正在尝试在 Node Express 应用程序中使用 Mongoose 以递归方式从 Mongodb 检索树格式的数据。我试图用承诺来做到这一点,但我显然做得不对。
假设你有 mongodb 表foos
':
"_id":
"$oid": "5b5fb27232db7b13a1090577"
,
"bazs": [
"$oid": "5b626a2e325c181bdf4fba08"
],
"bars": [
"$oid": "5b5fb3cc32db7b13a1090579"
],
"name": "Parent1",
"accountId": "5b5fb27132db7b13a1090576",
"__v": 0
所以这有两种不同的子对象。此外,baz
对象还有其他baz
对象作为子对象,还有bar
对象。假设这是 bars
的 id 的表:
"_id":
"$oid": "5b626a2e325c181bdf4fba08"
,
"bazs": [
"$oid": "5b5fb3cc32db7b75fa01"
],
"bars": [
"$oid": "5b5fb3c7cdaf740a500a"
],
"accountId": "5b5fb27132db7b13a1090576",
"parent":
"$oid": "5b5fb27232db7b13a1090577"
,
"__v": 0
我一直在等待使用 Mongoose 检索对象类型的 thunk 中使用递归函数。
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);
)
);
我在这两个函数中使用它:
const getBarRecursive = (taskId)=>
return getObjectRecursive(Bar, taskId);
const getFooRecursive=(categoryId)=>
return getObjectRecursive(Foo,categoryId);
;
这条路线调用的:
router.get('/:id', verifyToken, (req,res)=>
Branch.getFooRecursive(req.params.id)
.then(
(foo)=>
res.status(200).send(cat);
,
(err)=>
res.status(500).send(err);
)
);
这意味着递归返回树对象和所有对象的子对象。正在发生的事情是子对象 result.children.bars
和 result.children.bazs
成为永远不会实现的承诺。
我知道,这不是最好的方法。我显然没有做正确的承诺。我还考虑过使用 $graphLookup 检索表格,但我不知道如何做到这一点,因为孩子们来自不同的集合。
我怎样才能让应用程序检索任何深度的孩子?
更新:这仍然不起作用。我已将递归函数更改为:
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
;
其他一切都是一样的。现在发生的情况是,包含bar
结果的承诺在结果发送给客户端之前不会被履行,所以我在它的位置得到一个空对象,如下所示:
"bars": [
"5b626a2e325c181bdf4fba08"
],
"bazs": [],
"_id": "5b5fb27232db7b13a1090577",
"__v": 0,
"children":
"bars": [
],
"bazs": []
我尝试过的一些事情:
将bars.map(async barId=>...
等放在Promises.all 语句中,并在.then(...)
语句中将其分配给result.children
将bars.map(async barId=>...
等放在一个单独的异步函数中,然后说result.children.bars = await getAllTasks(tasks)
如何确保在发送结果之前履行我的承诺?
【问题讨论】:
该示例大量使用 ***.com/questions/23803743/… 。 Mongoose 支持承诺。为什么你一直使用 findOne 回调?你正在将这些东西与 async/await 混合在一起。如果你到处都使用 async/await,这个问题可能会得到解决。 所以你说不是让 findObjectRecursive 返回一个承诺,而是让它和 getFooRecursive 和 getBarRecursive 进入异步函数? 如果需要,您仍然可以使用递归,但控制流更清晰,错误空间更少。const result = await type.findOne(...)
等
我明天试试这个
顺便说一句,默认情况下这样做是个好主意,除非您需要相反的方法。这就像const result = await type.findOne(...).lean()
一样完成。
【参考方案1】:
Mongoose 长期支持 Promise,无需使用回调。 reject
函数未定义,使用它会导致异常。此外,call
在这里是不合理的。还有几个不一致之处,tasks
vs bars
和 eventId
vs bazId
。所有这些都会阻止代码正常工作
应该是这样的:
const getObjectRecursive = async (type, id)=>
const result = await type.findOne(_id:id).lean();
const barsPromises = result.bars.map(barId=>
return getBarRecursive(barId.toString()); // shouldn't it accept 2 args?
);
const bazsPromises = result.bazs.map(bazId =>
return Baz.findOne(_id:bazId.toString()).lean();
);
const [bars, bazs] = await Promise.all([
Promise.all(barsPromises),
Promise.all(bazsPromises)
]);
result.children = bars, bazs ;
return result;
;
【讨论】:
以上是关于在递归节点函数中从 Mongoose 检索树数据的主要内容,如果未能解决你的问题,请参考以下文章