MongoDB中用findOne关联查询后过滤roomOption内指定参与者
当然可以仅靠findOne配合populate实现你的需求——也就是只保留roomOption.participants中profile等于当前用户(doc.profile)的条目,移除其他元素。不过你之前的配置逻辑有问题,我来给你拆解下正确的实现方式:
问题出在哪?
你原来在roomOption的populate配置里写的match: { 'participants.profile': doc.profile },这个逻辑是判断整个roomOption文档是否存在至少一个符合条件的participant,而不是过滤数组里的元素。如果有匹配的participant,就会返回完整的roomOption(包含所有participants);如果没有,就不会返回roomOption。这和你想要的“保留数组中匹配项、移除其他项”完全不是一回事。
方法一:用Populate的transform函数实时处理
Mongoose的populate支持transform选项,我们可以在roomOption被填充完成后,用这个函数手动过滤它的participants数组:
return MyRoom.findOne(doc, proj).populate({ path: 'rooms', select: ['participants', 'updatedAt', 'roomOption'], populate: [ { path: 'participants', match: { _id: {$ne: doc.profile} }, select: ['_meta.pseudo', 'pictureprofile.url'] }, { path: 'roomOption', select: ['participants', 'room', 'createdAt', 'updatedAt'], // 明确指定需要返回的字段 transform: (roomOptionDoc) => { // 过滤participants,只保留profile匹配当前用户的条目 roomOptionDoc.participants = roomOptionDoc.participants.filter(participant => participant.profile.toString() === doc.profile.toString() ); return roomOptionDoc; } } ], });
这个transform函数会在roomOption被填充后立即执行,直接修改返回的roomOption文档,只保留你需要的participants。
方法二:查询完成后手动处理结果
如果你觉得transform函数不够直观,也可以在查询结果返回后,直接遍历并修改文档对象:
// 先执行查询和填充 const targetRoom = await MyRoom.findOne(doc, proj).populate({ path: 'rooms', select: ['participants', 'updatedAt', 'roomOption'], populate: [ { path: 'participants', match: { _id: {$ne: doc.profile} }, select: ['_meta.pseudo', 'pictureprofile.url'] }, { path: 'roomOption', select: ['participants', 'room', 'createdAt', 'updatedAt'] } ], }); // 过滤每个room的roomOption.participants if (targetRoom?.rooms) { targetRoom.rooms.forEach(room => { if (room.roomOption?.participants) { room.roomOption.participants = room.roomOption.participants.filter(participant => participant.profile.toString() === doc.profile.toString() ); } }); } return targetRoom;
这个方法逻辑更直白,适合需要对结果做更多自定义处理的场景。
补充提示
这两种方法都不需要用到aggregate,完全基于findOne和populate实现。另外要注意:因为我们只是修改返回的结果(不是修改数据库里的文档),所以不需要调用save()方法。
内容的提问来源于stack exchange,提问作者Raphaël Etang-Salé

