如何查询MongoDB内部循环而不跳过结果?

时间:2019-06-15 16:02:45

标签: javascript node.js mongodb rest

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

我尝试了无数种不同的方法来完成for循环,而不会跳过任何查询或过早返回完成的数组,但是这样做没有成功。当前的实现在第一个API调用上有效,但在下一个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}。

1 个答案:

答案 0 :(得分:0)

Promise.all可以帮助您编排多个承诺,而无需使用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(...
}