首页 > 解决方案 > Mongodb 统计过去 X 周的文档 - 不是过去 XX 天

问题描述

虽然这里有类似的问题,如何查找/计算特定周范围内的文件,但我无法找到,如何匹配/计算最近 X 周的文件。考虑这个查询:

db.getCollection('post').aggregate([

   {
        $match: {
            createdDate: {'$gte': new Date(new Date() - 7 * 60 * 60 * 24 * 1000)},
        }
    },
    {
        $group: {
            _id: {$week: '$createdDate'},
            count: {$sum: 1}
        }
    }
])

此查询计算过去 7 天的文档数,并按周数对计数进行分组 - 但结果实际上包含两个文件:本周的文档数和上周从当前日期开始的文档数天 - 7 天。

示例 - 从星期三开始的查询:

无论是在一周的第一天还是一周的最后一天开始,查询都应该返回正确的结果。还必须考虑年份变化 - 12 月的最后一周有第 52 周,1 月的第一周有第 1 周。

在没有任何相关文件的情况下数周可能会导致其他潜在问题。您只是不能按周数对过去 21 天的文档进行分组并使用$sortand $limit,因为没有任何相关文档的周不会出现在分组中。

标签: mongodbdateweek-number

解决方案


花一些时间试图找到一个基于$week运营商的自己的解决方案并决定将其发布在这里,但最后我意识到, harrgit kohli建议的方法更好。我稍后会解释。经过进一步调查,我注意到第一个答案存在一些重大缺陷,原因是在没有考虑任何相关文件的情况下没有花费数周时间。

更新:

我终于找到了一个基于周数的解决方案,即使查询在一年中发生变化,它也应该给出正确的结果。关键是减去52(每年的周数)

db.getCollection('post').aggregate([
{
    {
       $match: { // match posts at least 2 Weeks + 6 days old
          createdDate: {'$gte':new Date(new Date().setDate(new Date().getDate() - 3 * 7 - 1))},
       }
    },
    {
       $addFields: {
          createdInWeek: { 
             $cond: {
                 if: {
                     $eq: [
                        { $year: "$createdDate"},
                        { $year: new Date()}
                     ]
                 }, then: {
                     $week: "$createdDate"
                 }, else: {
                     $subtract: [ { $week: "$createdDate"}, 52]
                 }
              }
          },
          currentWeek: { $week: new Date() },
      }
  },
  {
      $match: {
          $expr: {
              $and: [
                  {$lte: ["$createdInWeek", { $subtract: ["$currentWeek", 1] } ]},
                  {$gte: ["$createdInWeek", { $subtract: ["$currentWeek", 2] } ]},
              ]
          }
      }    
  },
  {
      $group: {
          _id: {$week: '$createdDate'},
          count: {
              $sum: 1
          }
      }
  },
])

因此,包含 53 周的年份可能仍然存在问题,请参考此处

以前的:

db.getCollection('post').aggregate([
{   // optional, can pre-sort your collection, so you don't add fields to every document in a huge collection
    $match: {
        createdDate: {'$gte':new Date(new Date().setDate(new Date().getDate() - 2 * 7 + 1))},
    }
},
{
    $addFields: {
        createdInWeek: { $week: '$createdDate' },
        currentWeek: { $week: new Date() },
    }
},
{
    $match: { // use of $expr. necessary since MongoDb 3.6 see https://stackoverflow.com/a/58567621/1991697
        $expr: {
           $and: [
              {$lte: ["$createdInWeek", { $subtract: ["$currentWeek", 1] } ]},
              {$gte: ["$createdInWeek", { $subtract: ["$currentWeek", 2] } ]},
           ]
        }
    }               
},
{
   $group: {
       _id: {$week: '$createdDate'},
       count: {
          $sum: 1
       }
    }
},    
])

这种方法让 Mongo 计算当前周的周数以及createdDate集合中每个文档的周数,然后您可以使用进一步的$match运算符进行简单的文件筛选。它还可以让您更轻松地设置周范围。

如果您的周范围将在一年的休息时间分开,这可能会失败。(一月的第一周是第 0 周,十二月的最后一周是第 52 周)


推荐阅读