NodeJS - 控制器 - 来自集合的多个查询 + forEach

Posted

技术标签:

【中文标题】NodeJS - 控制器 - 来自集合的多个查询 + forEach【英文标题】:NodeJS - controller - multiple queries from collections + forEach 【发布时间】:2018-10-15 19:21:06 【问题描述】:

对 promises 和 async & await 感到困惑。想听听您的建议,以下控制器案例的最佳实践是什么?现在它在将数据传递给客户端之前不会获取用户详细信息。

我正在使用 NodeJS 为服务器端做一个简单的 API 端点。控制器正在进行两个查询以完成简单的结果作为客户端的 json 对象。是的,有这个无用的 run[] 数组,但它在构建整个事件结果时使用:)

exports.runresults3 = function(req, res) 

  // Route is passing ID
  console.log('id', req.params.id);

  // Test results object to fill
  const resultsParams = 
    run: [
      runid: ,
      endtimeid: [],
      userid: [],
      endtime: [],
      username: [],
    ]
  

  // Run objectid
  resultsParams.run[0].runid = req.params.id;

  // Get the endtimes by location/stage
  Endtime.find(stage: req.params.id)
    .then(data => 
      // First loop
      data.forEach(value => 
        resultsParams.run[0].endtimeid.push(value._id);
        resultsParams.run[0].userid.push(value.user);
        resultsParams.run[0].endtime.push(value.endtime);
      )
    )
    .then(() => 
      // Second loop to get user details for results object
      resultsParams.run[0].userid.forEach((userId, i) => 
        TempUser.findById(userId)
          .then(userdetails => 
            console.log('userdetails.name', userdetails.name);
            resultsParams.run[0].username.push(userdetails.name);
          );
      )
    )
    .then(() => 
      res.json(resultsParams);
    );

///////////客户端会得到json如下


"run": [
    
        "runid": "5ae850d51717862590dc30d4",
        "endtimeid": [
            "5aec482d98555332145eccd3",
            "5aec48c098555332145eccd6",
            "5aec4a2c98555332145eccda",
            "5aec4ab398555332145eccdd",
            "5aec4bb998555332145ecce1",
            "5aec4e42c3bcbb196c8474fc",
            "5aec4e44c3bcbb196c8474fe",
            "5aec4e45c3bcbb196c847500"
        ],
        "userid": [
            "5aec13b098555332145eccbe",
            "5ae869c797e54a37f498c98f",
            "5aec4a1298555332145eccd7",
            "5aec4a1298555332145eccd7",
            "5aec4ba698555332145eccde",
            "5aec13a598555332145eccbc",
            "5ae869c797e54a37f498c98f",
            "5aec13b098555332145eccbe"
        ],
        "endtime": [
            24424,
            3280,
            11858,
            38874,
            5738,
            40384,
            50906,
            36717
        ],
        "username": []
    
]

【问题讨论】:

您在resultsParams.run[0].userid.forEach 之前的第二个then 中缺少return,并且.forEach 需要是.mapPromise.all,所以它知道等待所有结果。 作为一个快速的澄清点,是否期望数组中的行在所有 3 个数组中的顺序相同?每个结果都有一个对象数组而不是数组数组更有意义吗? @BenjaminGruenbaum 感谢您的回复,但还没有完全奏效。用户详细信息终于来了,所以数据已经传递给客户端.. CodyKnapp,是的,你是对的,那更干净 我只是改变了整个结果概念。现在它在保存它们时正在准备好结果,因此在通过单个运行 id 提取结果时不需要进行复杂的查询。这让我觉得即使它是一个 mvp 应用程序。 :) 目前不需要帮助!谢谢你们的cmets!!! 【参考方案1】:

exports.runresults3 = function(req, res) 

  // Route is passing ID
  console.log('id', req.params.id);

  // Test results object to fill
  const resultsParams = 
    run: [
      runid: ,
      endtimeid: [],
      userid: [],
      endtime: [],
      username: [],
    ]
  

  // Run objectid
  resultsParams.run[0].runid = req.params.id;

  Endtime.find(stage: req.params.id)
    .then(data => 
      return Promise.all(data.map(record => 
        TempUser.findById(record.user)
          .then(userdetails => 
            return 
              endtimeid: record._id,
              userid: record.user,
              endtime: record.endtime,
              username: userdetails.name
            ;
          )
      ))
      .then(detailRecords => 
        return detailRecords.reduce((acc, curr) => 
          acc.endtimeid.push(curr.endtimeid);
          acc.userid.push(curr.userid);
          acc.endtime.push(curr.endtime);
          acc.username.push(curr.username);
          return acc;
        ,resultsParams.run[0]);
      )
      .then(() => 
        res.json(resultsParams);
      );
    );

如果您需要在此结果对象中将事物累积到事物的“关联数组”样式中,我可能会这样做。

话虽如此,我很可能会尝试将内容作为运行结果数组返回 - 类似于以下内容...


  "run": [
    
      "runid": "5ae850d51717862590dc30d4",
      "results": [
        
          "endtimeid": "5aec482d98555332145eccd3",
          "userid": "5aec13b098555332145eccbe",
          "endtime": 24424,
          "username": "User123"
        ,
        
          "endtimeid": "5aec48c098555332145eccd6",
          "userid": "5ae869c797e54a37f498c98f",
          "endtime": 3280,
          "username": "User234"
        
      ]
    
  ]

我相信下面的代码应该做到这一点。

exports.runresults3 = function(req, res) 

  // Route is passing ID
  console.log('id', req.params.id);

  // Test results object to fill
  const resultsParams = 
    run: [
      runid: ,
      results: []
    ]
  

  // Run objectid
  resultsParams.run[0].runid = req.params.id;

  Endtime.find(stage: req.params.id)
    .then(data => 
      return Promise.all(data.map(record => 
        TempUser.findById(record.user)
          .then(userdetails => 
            return 
              endtimeid: record._id,
              userid: record.user,
              endtime: record.endtime,
              username: userdetails.name
            ;
          )
      ))
    )
    .then(results => resultsParams.run[0].results = reuslts)
    .then(() => res.json(resultsParams));

当然可以进行更多的可读性重构,而且填充结果的最后一步不使用前一个流程的结果这一事实让我有点困扰 - 但我尽量避免副作用,所以这可能就是我。

【讨论】:

【参考方案2】:

这是建议的编辑版本 :) 但仍然缺少用户详细信息,看起来 promise.all 没有等待“TempUser.findById(record.user)”查询。

1 我创建了一个新事件,运行,3 个用户和 3 个结果。

2 然后我在数据库调用中设置了一些日志

3 然后我用邮递员做了一个get请求

在 Postman GET 请求后来自 localhost 的日志如下所示:

$ 节点应用程序 服务器启动于 5000 连接MongoDB 编号 5aeebd8a1b5ddf1424c25194

    从数据库中获取结束时间

[ 创建时间:2018-05-06T08:32:46.359Z, _id:5aeebdae1b5ddf1424c25199, 结束时间:23204, 用户:5aeebd751b5ddf1424c25191, 阶段:5aeebd8a1b5ddf1424c25194, __v: 0 , 创建:2018-05-06T08:32:49.414Z, _id: 5aeebdb11b5ddf1424c2519b, 结束时间:17149, 用户:5aeebd7b1b5ddf1424c25192, 阶段:5aeebd8a1b5ddf1424c25194, __v: 0 , 创建:2018-05-06T08:32:51.769Z, _id: 5aeebdb31b5ddf1424c2519d, 结束时间:10840, 用户:5aeebd7f1b5ddf1424c25193, 阶段:5aeebd8a1b5ddf1424c25194, __v: 0 ]

    将结果设置为对象

[未定义,未定义,未定义] 这应该是链上的最后一个?

    从数据库中获取用户详细信息

日期:2018-05-06T08:31:49.673Z, _id: 5aeebd751b5ddf1424c25191, name: 'Firstname Lastname1', __v: 0 2.从数据库中获取用户详细信息日期:2018-05-06T08:31:55.562Z, _id: 5aeebd7b1b5ddf1424c25192, name: 'Firstname Lastname2', __v: 0 2. 从数据库中获取用户详细信息 date: 2018-05-06T08:31:59.906Z, _id: 5aeebd7f1b5ddf1424c25193, name: 'Firstname Lastname3', __v: 0

这是由以下代码制成的:

exports.runresults3 = function(req, res) 

  // Route is passing ID
  console.log('id', req.params.id);
  
  // Test results object to fill
  const resultsParams = 
    run: [
      runid: ,
      results: []
    ]
  

  // Each run objectid
  resultsParams.run[0].runid = req.params.id;

  Endtime.find(stage: req.params.id)
    .then(data => 
      console.log('1. Get endtimes from database', data);
      return Promise.all(data.map(record => 
        TempUser.findById(record.user)
          .then(userdetails => 
            console.log('2. Get user details from database', userdetails);
            return 
              endtimeid: record._id,
              userid: record.user,
              endtime: record.endtime,
              username: userdetails.name
            ;
          )
      ))
    )
    .then(results =>  
      console.log('3. Set results to object', results);
      console.log('This should be the last one at chain?');
      
      resultsParams.run[0].results = results;
    )
    .then(() => res.json(resultsParams));

【讨论】:

以上是关于NodeJS - 控制器 - 来自集合的多个查询 + forEach的主要内容,如果未能解决你的问题,请参考以下文章

NodeJS/Mongo:通过各种集合循环查询

如何防止 DataGrip 显示来自另一个查询控制台的结果?

NodeJS:从 SQL 查询中获取 JSON

使用来自 Nodejs Postgres 查询的数据 [重复]

使用 mongodb 和 nodejs 将多个集合发送到一个 ejs 文件

大查询 - 来自部署在 GKE 中的 nodejs 应用程序的 Util.parseHttpRespBody 凭据无效