我的数据库有三个集合,挑战,用户和条目。挑战具有诸如标题,描述和挑战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}。
答案 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(...
}