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

MongoDB如何基于过滤聚合结果筛选近一周通话数少于2次的用户

问题说明

现有存储用户手机通话信息的集合,文档结构示例如下:

{
     "_id": "1",
     "name": "John",
     "calls": [
        "2022-07-01T01:05:00.000+00:00",
        "2022-07-08T02:14:00.000+00:00",
        "2022-07-08T05:27:00.000+00:00",
        "2022-07-09T03:46:00.000+00:00"
     ]
},
{
     "_id": "2",
     "name": "George",
     "calls": [
        "2022-06-28T01:05:00.000+00:00",
        "2022-06-29T02:14:00.000+00:00",
        "2022-06-29T05:27:00.000+00:00",
        "2022-07-02T03:46:00.000+00:00"
     ]
}

注:原示例中数组内写索引键、重复_id属于笔误,上述结构已修正为MongoDB合法的文档格式。

需求为 查询过去一周内通话次数少于2次的所有人员,逻辑拆分如下:

  • 筛选出近一周内产生的所有通话记录
  • 对筛选出的通话记录聚合计数
  • 返回计数结果小于2的对应人员文档

目前已在投影阶段实现通话记录过滤与计数逻辑,代码如下:

projection: {
    callsNumber: {
        $size: { 
            $filter: { 
                input: "$calls", 
                cond: { 
                    $gte: ["$$this", ISODate("2022-07-03T00:00:00.000+00:00")]
                }
            }
        }
    }
}

存在的问题:无法在过滤条件中引用投影生成的字段,也不知道如何将投影逻辑转换为可直接筛选文档的过滤条件。

实现方案

核心问题原因:普通find查询的执行顺序是先匹配筛选文档,再执行投影返回指定字段,因此投影阶段生成的计算字段无法被前置的筛选逻辑引用。有两种成熟实现方式:

方案1:find + $expr 单查询实现

MongoDB 3.6及以上版本支持$expr操作符,允许在查询条件中直接使用聚合表达式,不需要单独定义投影,直接把通话次数计算逻辑放到筛选条件中即可:

// 动态计算一周前的时间,不需要硬编码日期
const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)

db.collection.find({
  $expr: {
    $lt: [
      {
        $size: {
          $filter: {
            input: "$calls",
            cond: { $gte: ["$$this", oneWeekAgo] }
          }
        }
      },
      2
    ]
  }
})

方案2:聚合管道分阶段实现

如果逻辑更复杂需要拆分步骤,可使用聚合管道,管道阶段按书写顺序执行,可以先计算通话次数字段,再基于该字段做筛选:

const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)

db.collection.aggregate([
  // 第一步:新增字段计算近一周通话次数
  {
    $addFields: {
      callsNumber: {
        $size: {
          $filter: {
            input: "$calls",
            cond: { $gte: ["$$this", oneWeekAgo] }
          }
        }
      }
    }
  },
  // 第二步:筛选通话次数小于2的文档
  {
    $match: {
      callsNumber: { $lt: 2 }
    }
  },
  // 可选:如果不需要返回callsNumber字段,可加投影阶段去掉
  {
    $project: {
      callsNumber: 0
    }
  }
])

结果验证

以给出的测试数据、硬编码截止时间为2022-07-03T00:00:00Z为例:

  • John近一周通话共3次(7月8日2次、7月9日1次),不符合条件不会被返回
  • George近一周通话共1次(7月2日1次),符合条件会被返回

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 03:27:32