如何在MongoDB中使用$lookup运算符添加isOnline状态标识
为聊天参与者填充在线/离线状态的MongoDB聚合方案
背景
开发消息应用时,需要为聊天参与者标注在线/离线状态。当前使用MongoDB的$lookup聚合查询填充参与者信息,但无法正确设置isOnline标识。
数据结构
User Schema:
User { _id: ObjectId("123456789xx") first_name: "Sam", last_name: "Jones", email: "samjones@gmail.com", inbox: [ { participant: ObjectId("1XXXXXXXXX"), added_at: "12:00:00 09/21/2021" }, { participant: ObjectId("2XXXXXXXXX"), added_at: "12:00:00 11/21/2022" }, ] }
Session Schema:
Session { _id: "XXXuid", expire: "12:00:00 07/01/2023", session: { userId: "1XXXXXXXXX", name: "Sam Smith", email: "samsmith@gmail.com" } }
说明:已登录用户会存在于Session集合中,通过
session.userId关联User的_id。
当前问题
现有$lookup仅能填充参与者基础信息,但无法根据Session判断在线状态:
{ $lookup: { from:"Users", localField:"inbox.participant", foreignField: "_id", as: "participants", pipeline:[ { $addField: { isOnline: false } } ] } }
解决方案
需要嵌套两次$lookup:先关联User集合获取参与者信息,再关联Session集合判断在线状态,最后将结果映射回inbox数组。完整聚合步骤如下:
db.Users.aggregate([ // 拆分inbox数组,逐个处理参与者 { $unwind: "$inbox" }, // 关联User集合,填充参与者基础信息 { $lookup: { from: "Users", localField: "inbox.participant", foreignField: "_id", as: "inbox.participant" } }, // 将参与者数组转为单个对象 { $unwind: "$inbox.participant" }, // 关联Session集合,检查用户是否有未过期会话 { $lookup: { from: "Session", let: { userId: "$inbox.participant._id" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ["$session.userId", "$$userId"] }, // 若expire为字符串格式,需替换为{ $gt: [{ $toDate: "$expire" }, new Date()] } { $gt: ["$expire", new Date()] } ] } } } ], as: "inbox.participant.isOnline" } }, // 根据Session查询结果设置isOnline布尔值 { $addFields: { "inbox.participant.isOnline": { $cond: { if: { $gt: [{ $size: "$inbox.participant.isOnline" }, 0] }, then: true, else: false } } } }, // 重新组合inbox数组 { $group: { _id: "$_id", first_name: { $first: "$first_name" }, last_name: { $first: "$last_name" }, email: { $first: "$email" }, inbox: { $push: "$inbox" } } } ])
关键说明
- 用
$unwind拆分inbox数组,确保每个参与者能单独关联数据 - 嵌套
$lookup先获取用户基础信息,再检查Session集合中是否存在未过期会话 - 通过
$size判断Session查询结果的长度,自动设置isOnline的布尔值 - 最后用
$group将拆分的inbox元素重新组合成数组
最终结果
{ _id: ObjectId("123456789xx") first_name: "Sam", last_name: "Jones", email: "samjones@gmail.com", inbox: [ { participant: { first_name:"Sam", last_name: "Smith", email: "samsmith@gmail.com", isOnline: true }, added_at: "12:00:00 09/21/2021" }, { participant: { first_name:"Emily", last_name: "Grande", email: "emilygrande@gmail.com", isOnline: false }, added_at: "12:00:00 11/21/2022" }, ] }
内容的提问来源于stack exchange,提问作者Shaun
相关产品推荐
相关产品推荐

