MongoDB如何用单条update/aggregate实现用户会话嵌套数组多条件增改操作
实现方案
前置准备
提前在业务代码中预处理好以下变量,再传入MongoDB语句:
targetUserId:当前登录用户ID,对应req.user._idcurrentDay:当前日期,格式为dd/MM/YYYY,例如14/11/2021currentHour:当前时间,格式为hhmmssreqReferer:请求来源,对应req.headers.refererreqIp:客户端IP,对应req.socket.remoteAddress
单条updateOne实现语句
db['users-sessions'].updateOne( // 匹配需要更新的目标文档,示例按_id匹配 { _id: "abcd" }, [ // 第一步:处理usersession数组,响应用户存在/不存在的逻辑 { $set: { usersession: { $cond: { // 判断usersession中是否存在当前用户的记录 if: { $in: [targetUserId, "$usersession.userId"] }, then: { $map: { input: "$usersession", as: "user", in: { $cond: { if: { $eq: ["$$user.userId", targetUserId] }, then: { $mergeObjects: [ "$$user", { date: { $let: { vars: { // 判断当前用户的date数组中是否存在当天记录 hasCurrentDay: { $in: [currentDay, "$$user.date.day"] }, // 新的当天记录结构 newDayEntry: { day: currentDay, sessionday: [ { hour: currentHour, referer: reqReferer, ip: reqIp } ] }, // 原有date数组 originalDate: "$$user.date" }, in: { $cond: { // 情况1:存在当天记录,更新对应sessionday if: "$$hasCurrentDay", then: { $map: { input: "$$originalDate", as: "dayEntry", in: { $cond: { if: { $eq: ["$$dayEntry.day", currentDay] }, then: { $mergeObjects: [ "$$dayEntry", { sessionday: { $concatArrays: [ "$$dayEntry.sessionday", [{ hour: currentHour, referer: reqReferer, ip: reqIp }] ] } } ] }, else: "$$dayEntry" } } } }, // 情况2:不存在当天记录,新增日期条目,同时处理长度>=10的替换逻辑 else: { $cond: { if: { $gte: [{ $size: "$$originalDate" }, 10] }, // 日期数>=10,删除最早的(第一个元素),添加新日期 then: { $concatArrays: [ { $slice: ["$$originalDate", 1, 9] }, ["$$newDayEntry"] ] }, // 日期数<10,直接追加新日期 else: { $concatArrays: ["$$originalDate", ["$$newDayEntry"]] } } } } } } } } ] }, else: "$$user" } } } }, // 情况3:用户不存在,新增用户条目 else: { $concatArrays: [ "$usersession", [ { userId: targetUserId, date: [ { day: currentDay, sessionday: [ { hour: currentHour, referer: reqReferer, ip: reqIp } ] } ] } ] ] } } } } } ] )
逻辑说明
完全覆盖全部4个操作条件:
- 用户存在且当天记录存在:直接往对应
sessionday数组追加新的会话记录 - 用户存在但当天记录不存在:往对应用户的
date数组新增当天的会话结构 - 用户不存在:直接往
usersession数组新增完整的用户会话结构 - 对应用户
date数组长度>=10时,自动移除最早的日期记录,再添加新的日期记录(默认date数组按时间从早到晚排序,最早记录为数组第一个元素,若排序逻辑不同可调整$slice参数即可)
内容的提问来源于stack exchange,提问作者ropo ast
相关产品推荐
相关产品推荐

