You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在MongoDB中批量筛选指定列表内近7天未登录的用户?

问题

我正在NestJS结合MongoDB的环境中开发一个API,目的是给近7天未登录系统的注册用户发送提醒邮件。

每次用户登录时,我会在session集合中创建如下结构的文档(用户单日可拥有多个session):

{
  user_email: "dev@gmail.com",
  _created: "2019-05-13T13:09:39.000+00:00"
}

我有一个需要验证的目标邮箱列表,示例如下:

['dev@gmail.com', 'test@fakemail.com', 'hahew66382@kaimdr.com', 'hoheray334@oncebar.com']

有没有办法不用逐个遍历用户,就能判断这些邮箱对应的用户在近7天内是否有session记录?我考虑过用聚合查询,再对比结果中未出现的邮箱,但对聚合查询经验不足,不确定思路是否正确。以下是我的尝试:

[
  {
    '$match': {
      'user_email': {
        '$in': ['dev@gmail.com', 'test@fakemail.com', 'hahew66382@kaimdr.com', 'hoheray334@oncebar.com']
      }, 
      '_created': {
        '$gte': new Date('Thu, 10 Nov 2022 00:00:00 GMT') // 7 days ago since today is 2022-11-17
      }
    }
  }, {
    '$sort': {
      '_created': -1
    }
  }, {
    '$group': {
      '_id': '$user_email', 
      'last_session': {
        '$first': '$_created'
      }
    }
  }
]
解决方案

你的思路完全可行,通过聚合查询筛选近7天有活跃session的用户,再从目标列表中排除这些用户,就能得到需要发邮件的对象。下面给你两种优化方案:

方案一:客户端完成差集计算

先通过聚合查询获取近7天有登录记录的用户邮箱,再在代码层面过滤目标列表:

// 动态计算7天前的日期,避免硬编码
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);

// 聚合查询获取活跃用户邮箱
const activeUsers = await sessionCollection.aggregate([
  {
    $match: {
      user_email: { $in: targetEmailList },
      _created: { $gte: sevenDaysAgo }
    }
  },
  {
    $group: { _id: '$user_email' } // 按邮箱去重,只保留唯一邮箱
  },
  {
    $project: { email: '$_id', _id: 0 } // 重命名字段方便后续处理
  }
]).toArray();

// 提取活跃邮箱数组
const activeEmails = activeUsers.map(item => item.email);

// 过滤出近7天未登录的用户邮箱
const inactiveUsers = targetEmailList.filter(email => !activeEmails.includes(email));

方案二:完全用MongoDB聚合完成逻辑

如果不想在客户端处理数组,可以直接用MongoDB的$setDifference操作符在聚合管道内计算差集:

const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);

const result = await sessionCollection.aggregate([
  // 筛选近7天内目标用户的session记录
  {
    $match: {
      user_email: { $in: targetEmailList },
      _created: { $gte: sevenDaysAgo }
    }
  },
  // 按邮箱分组去重
  {
    $group: { _id: '$user_email' }
  },
  // 将活跃邮箱合并为一个数组
  {
    $group: {
      _id: null,
      activeEmails: { $push: '$_id' }
    }
  },
  // 计算目标列表与活跃列表的差集,得到非活跃用户
  {
    $project: {
      inactiveUsers: { $setDifference: [targetEmailList, '$activeEmails'] },
      _id: 0
    }
  }
]).toArray();

// 最终结果就是需要发邮件的用户邮箱数组
const inactiveUsers = result[0]?.inactiveUsers || [];

额外优化建议

  • 你的原始聚合查询已经覆盖核心逻辑,如果需要保留用户最后登录时间,可以保留$sort和last_session字段;如果只需要判断活跃状态,去掉$sort和last_session能提升查询效率。
  • 给session集合创建联合索引,大幅提升查询速度:
    // 创建user_email和_created的联合索引
    await sessionCollection.createIndex({ user_email: 1, _created: -1 });
    

内容的提问来源于stack exchange,提问作者fvilar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 00:05:21