【问题标题】:Nested Mongo Calls In Node/ExpressNode/Express 中的嵌套 Mongo 调用
【发布时间】:2020-12-25 15:27:58
【问题描述】:

我正在尝试构建一个由单独的 React 前端使用的 REST api。在一个页面上,我需要从单个 mongo 数据库中的不同集合中提取数据,并将其捆绑在一起,然后再将响应返回给前端,以最大限度地减少 api 调用。我的数据库中有以下集合:

  • 用户
  • 项目

其中每一个的数据结构如下:

  • 用户集合 = [{id: ObjectId, completedItems=[ObjectId, ObjectId, ...], ...}]
  • ItemsCollection = [{id: ObjectId, name: string, ...}]

在我的 node/express 应用程序中,我想做一个调用来获取所有用户,对于每个用户,在返回响应之前查询项目集合以提取每个项目的名称。

当我最终返回响应时,我希望能够返回以下结构的对象,但不改变我存储数据的结构。

回复 = [{id: ObjectId, completedItems=[{id: ObjectId, name: string}, {id: ObjectId, name: string}]}]

我遇到的问题是,当我进行此调用时,当用户的 completedItems 数组中没有项目时,整个后端在尝试进行第二次 api 调用时崩溃,我最终得到诸如“Cannot read property 'name' of null”之类的错误消息。

我假设我在这里遗漏了某种错误处理,或者我可能没有以正确的方式考虑整体结构。

由于这是我的第一个主要 REST api,我不知道我是否应该在返回响应之前尝试在后端合并这些信息,或者我是否应该在前端处理这个,返回一个响应用户调用前端,然后对后端进行第二次 api 调用以获取项目数据。

任何指导将不胜感激!

'''lang-js

const getUsers = async (req, res, next) => { 
    let users 
    try { 
        users = await User.find({}, {name:1, items: 1})
    } catch (err) { 
        return next(new Error('Could not fetch inductions').code(500)) 
    } 
    for (let i in users){
        let {id, name, items} = users[i] 
        item = await Item.findById(item, {name:1}) 
    } 
    users[i] = { 
        id: id, name: name, item: {
            name: item.name, id: item.id
        } 
    } 
}

'''

【问题讨论】:

  • 请您分享您用于此功能的代码
  • ''' const getUsers = async (req, res, next) => { 让用户尝试 { users = await User.find({}, {name:1, items: 1}) } catch (err) { return next(new Error('Could not fetchductions').code(500)) } for (let i in users){ let {id, name, items} = users[i] item = await Item .findById(item, {name:1}) } users[i] = { id: id, name: name, item: {name: item.name, id: item.id} } '''
  • 道歉 - 我显然也不知道如何在 cmets 中添加代码块...
  • 只需编辑您的问题并使用 {} 格式化您的代码

标签: node.js mongodb rest express


【解决方案1】:

我会提出 3 个选项:

  1. 要在 Mongoose 中使用“填充”,您可以编辑您的 mongoose 用户模型以允许 completedItems 引用 ItemsCollection,然后在查询用户并使用“填充”方法时,mongoose 会自动为您检索所需的项目。

    你可以这样做:

    const userSchema = Schema({
                          _id: Schema.Types.ObjectId,
                          completedItems: [{ type: Schema.Types.ObjectId, ref: 'Items' }]
                       });
    

    注意这里的 Items 是指您给 ItemsSchema 的名称。

    查看documentation了解更多详情

  2. 要在尝试查询 ItemsCollection 之前首先检查 completedItems 数组长度,就像如果用户没有 completedItems 您将不必查询 ItemsCollection。

    做类似的事情:

    if(user.completedItems.length != 0)
        ItemsCollection.find({id: user.completedItems})
    
  3. 第三种选择是为此使用 GeaphQL,这样您就可以检索所有需要的数据来执行以下操作:

    UsersCollection.find()
                 .populate({
                     path: 'ItemsList',
                     model: 'Items'
                 })
    

【讨论】:

  • 感谢艾哈迈德,非常清晰全面的回答,非常感谢。特别是选项 1 看起来非常有趣。我会调查所有这些,但如果可以让猫鼬为我完成繁重的填充工作,那将是完美的。
  • 很高兴我的回答对您有用。如果您需要任何进一步的说明,请随时询问。如果您觉得我的答案足够有用,请点击我的答案旁边的“复选标记”和向上的箭头投票,将其作为您问题的答案。
  • 尝试了填充方法。使用起来非常简单,正是我想要的!感谢您让我的生活更轻松 :)
  • 不客气,我很高兴听到这个消息。如果您可以通过单击我的答案旁边的“向上”箭头对我的评论进行投票,那就太好了。
  • 当然——我已经做到了,但由于我对此还是很陌生,我认为我的投票实际上并不计入公开展示。但是大概随着我在这里的时间越来越多并且我的帐户越来越多,它最终会算数吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-25
  • 2016-05-20
  • 2016-11-27
  • 2017-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多