【问题标题】:How to query MongoDB inside for loop without skipping results?如何在不跳过结果的情况下在 for 循环中查询 MongoDB?
【发布时间】:2019-06-16 17:13:16
【问题描述】:

我的数据库有三个集合,挑战,用户和条目。挑战具有标题、描述和挑战 ID 等字段。条目是已完成的挑战,包含用户 ID、挑战 ID 和图像等字段。我想将条目集合中的数据加入到相应的挑战中,这样我就可以拥有一个包含挑战标题、描述、挑战 ID 和图像的文档。 我正在尝试根据从条目集合中获取的 id 数组查询挑战集合,然后将条目查询结果作为新字段添加到文档中。 我已经实现了一个 for 循环,它允许我每次使用不同的 id 进行查询。我想将查询的结果添加到数组中,但有时它会跳过结果,结果数组中只存在一些查询。例如,当我第一次发送 API 调用时,服务器在一个数组中返回 2 个 JSON 对象,但下一次它只返回一个。我认为 for 循环和查询的同步有问题。我怎样才能让它每次都返回正确的文件?另外,有没有更好的方法可以在没有 for 循环的情况下将两个集合连接在一起?

我已经尝试了无数种不同的方法来完成 for 循环,而不会跳过任何查询或过早返回完成的数组,但都没有这样做。此当前实现适用于第一个 API 调用,但在下一次调用时失败。我正在使用 MongoDB(和 MERN 堆栈),并且我有一个 REST API,我从 React 前端发送调用。

 exports.getDoneChallenges = [check("userId").isLength({ min: 24 }),
  function(req, res) {
    var myPromise = () =>
    new Promise((resolve, reject) => {
      // Find all challenges the user has completed.
      Entry.find({ userId: req.params.id }, { _id: 0 })
      .sort({ challengeId: -1 })
      .exec()
      .then(result => {
        // Check if the user hasn't completed any challenges.
        if (!result) {
          console.log("Zero completed challenges.");
          res
            .status(401)
            .json({ message: "No completed challenges found." });
        } else {
          // Save the completed challenge's identifiers in an array.
          var ids = new Array();
          for (var i = 0; i < result.length; i++) {
            // Cast identifiers to ObjectID
            ids.push(ObjectID(result[i].challengeId));
          }
          // Array of completed challenges + images relating to each.
          var challenge_arr = new Array();

          for (let i = 0; i < result.length; i++) {
            // Match the corresponding challenge id's from entries to 
             challenges and add image as a new field.
            Challenge.aggregate([
              { $match: { challengeId: ids[i] } },
              { $addFields: { image: result[i] } }
            ])
              .exec()
              .then(challenge => {
                /* Create a new object, which has the needed fields for 
                the response.*/
                var challenge_obj = new Object();
                challenge_obj.title = challenge[0].title;
                challenge_obj.challengeId = challenge[0].challengeId;
                challenge_obj.description = challenge[0].description;
                challenge_obj.date = challenge[0].image.date;
                challenge_obj.img = challenge[0].image.img;

                // Save the challenges into the challenge array.
                challenge_arr.push(challenge_obj);
                console.log(i)
                /* If the loop is in the last round, return the filled 
                array.*/
                if (i == result.length - 1) {
                  // Return the filled array.
                  return challenge_arr;
                }
              })
              .then(challenge_arr => {
                // Check that the array isn't undefined.
                if (typeof challenge_arr !== "undefined") {
                  // Resolve the promise.
                  resolve(challenge_arr);
                }
              });
          }
        }
      });
  });
  // Call promise function and send a response after resolving it.
    myPromise().then(data => {
      res.status(200).json({ data: data });
    });
  }
  ];


var EntrySchema = new Schema({
  challengeId: ObjectId,
  userId: ObjectId,
  date: { type: Date, default: Date.now},
  img: { data: Buffer, contentType: String}
})


var ChallengeSchema = new Schema({
  challengeId: mongoose.SchemaTypes.ObjectId,
  title: String,
  description: String,
  date: {type: Date}
})

我在 Entries 集合中有两个条目,它们的挑战 ID 与挑战集合中的两个挑战相同。我用条目的 id 查询挑战集合,我应该得到 2 个添加了相应条目字段的文档。有时我会正确获取文档,但大多数时候它只返回其中的一些。例如,从 4 个预期文档中,它返回 {chall 1, null, chall 2, chall 3}。

【问题讨论】:

    标签: javascript node.js mongodb rest


    【解决方案1】:

    Promise.all 可以帮助您编排多个 Promise,而不是使用 for 循环。按照您目前的方式,您最终会在最后一个循环完成其工作时调用 resolve,而不是在 每个 循环完成其工作时调用。

    它可能看起来像:

    const promises = result.map((image, i) => 
                Challenge.aggregate([
                  { $match: { challengeId: ids[i] } },
                  { $addFields: { image } }
                ]).exec());
    Promise.all(promises)
      .then((promise_results) => ...);
    

    使用async/await 通常可以使这样的代码更易于编写和理解:

    for (const result of results) {
      const challenge = await Challenge.aggregate(...
    }
    

    【讨论】:

    • 这似乎成功了!响应时间也从 1200 毫秒下降到 20 毫秒,这样就更好了。谢谢!
    猜你喜欢
    • 2017-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多