如何在Mongoose嵌套查询结果中为Competition添加isLiked属性
解决方案:为Competition添加isLiked属性
你的问题核心在于异步代码的执行顺序:Like.find是异步操作,你在它的回调还没完成时就执行了return res.json(competitions),这时候isLiked属性还没被赋值,所以返回的结果里没有这个字段。另外你原代码里还有个小笔误——把Competition.find写成了Coin.find,这也会导致查询结果不对。
下面提供几种靠谱的解决方法:
方法1:用async/await重构(最易读维护)
async/await可以让异步代码看起来像同步代码,避免回调地狱,同时保证执行顺序:
// 把路由处理函数改成async函数 app.get('/your-target-route', async (req, res) => { try { // 1. 查询所有Competition数据 const competitions = await Competition.find().lean(); // 2. 查询目标用户的所有点赞记录(请根据实际业务调整查询条件) const likes = await Like.find({ // 示例:如果是当前用户的点赞,可写 userId: req.user._id // 你原代码里的req.params.id请根据实际场景验证是否正确 }).lean(); // 把点赞的赛事ID存到Set里,方便快速判断 const likedCompetitionIds = new Set(likes.map(like => like.competition_id.toString())); // 3. 为每个赛事添加isLiked属性 competitions.forEach(competition => { competition.isLiked = likedCompetitionIds.has(competition._id.toString()); }); // 4. 确保赋值完成后再返回结果 res.json(competitions); } catch (err) { console.error(err); res.status(500).json({ error: '服务器内部错误' }); } });
方法2:用MongoDB聚合查询(性能最优)
直接在数据库层面完成关联和判断,减少一次数据库查询,效率更高:
Competition.aggregate([ { // 关联Like集合,匹配赛事ID和点赞记录中的赛事ID $lookup: { from: 'likes', // 注意这里是Like集合的复数名称(MongoDB默认会将模型名转为复数) localField: '_id', foreignField: 'competition_id', as: 'matchedLikes' } }, { // 添加isLiked字段:如果匹配到点赞记录,值为true,否则为false $addFields: { isLiked: { $gt: [{ $size: '$matchedLikes' }, 0] } } }, { // 可选:移除临时的matchedLikes数组,只保留需要的字段 $project: { matchedLikes: 0 } } ]).exec((err, competitions) => { if (err) { console.error(err); return res.status(500).json({ error: '服务器内部错误' }); } res.json(competitions); });
方法3:修复原回调写法(不推荐,仅作参考)
如果你坚持用回调模式,需要把返回结果的逻辑放到Like.find的回调内部,确保属性赋值完成后再返回:
Competition.find().lean().exec(function (err, competitions) { if (err) { console.log(err); return res.status(500).json({ error: err }); } // 请确认这里的查询条件符合业务需求 Like.find({ /* 正确的查询条件,比如 userId: req.user._id */ }).lean().exec(function (err, likes) { if (err) { console.log(err); return res.status(500).json({ error: err }); } const likedIds = new Set(likes.map(like => like.competition_id.toString())); competitions.forEach(competition => { competition.isLiked = likedIds.has(competition._id.toString()); }); // 必须在这个回调里返回结果,确保isLiked已赋值 res.json(competitions); }); });
额外提示
- 原代码里的
Coin.find是笔误,一定要改成Competition.find - 不要用嵌套
forEach判断点赞,时间复杂度会达到O(n*m);用Set存储ID可以把时间复杂度降到O(n+m),效率提升明显
内容的提问来源于stack exchange,提问作者Mahmood Kohansal
相关产品推荐
相关产品推荐

