在递归节点函数中从 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.barsresult.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.childrenbars.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 barseventId 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 检索树数据的主要内容,如果未能解决你的问题,请参考以下文章

求二叉树中从根结点到叶子节点的路径

element UI tree 递归删除树节点

如何在 JAVA 中从数据库中检索无线电组数据? M 使用 Java Swing 和 SQLite [重复]

Element UI tree 递归删除选中节点生成树

在一个云功能中从 firebase 数据库中检索多个数据

[数据结构] 树森林的遍历