如何在非标准周上汇总?

时间:2018-04-14 00:09:24

标签: database mongodb aggregation-framework

我有一个包含如下数据的集合:

{
  "_id": ObjectId("95159a08a27971c35a2683f"),
  "Date": ISODate("2018-04-03T07:00:00Z"),
  "Employee": "Bill",
  "Hours": 7.5
}
{
  "_id": ObjectId("372c6be4912fdd32398382f"),
  "Date": ISODate("2018-04-05T07:00:00Z"),
  "Employee": "Bill",
  "Hours": 2
}

我想得到每周的总小时数,但本周需要从周六开始,到周五结束。如果我在标准周工作,我会这样做:

db.myCollection.aggregate(
        {$match: {
          Employee: "Bill", 
          Date: {
             $gte: ISODate("2018-03-15T07:00:00Z"),
             $lte: ISODate("2018-04-06T07:00:00Z")
          }
        }},
        {$group: {
            _id: {$week: "$Date"},       
            hours:  {$sum: "$Hours"}
          }   
        }
)

在星期日和星期六结束的标准周内工作正常。

我如何修改它以使用我描述的非标准周?在查询数据并在代码级别手动聚合时,我会更好吗?我可以按周累计Date - 1 day还是太奇怪了?

更新

如果有人需要多年来完成这项工作,他们可以使用下面的Neil答案的更新版本:

{ "$group": {
  "_id": {
    "week":{
      "$let": {
        "vars": {
          "satWeek": {
            "$cond": {
              "if": { "$eq": [ { "$dayOfWeek": "$Date" }, 7 ] },
              "then": { "$add": [ { "$week": "$Date" }, 1 ] },
              "else": { "$week": "$Date" }
            }
          }          
        },
        "in": {
          "$cond": {
            "if": { "$gt": ["$$satWeek", 52] },
            "then": 0,
            "else": "$$satWeek"
          }
        }
      }
    },
    "year": { "$year": "$Date" }
  },
  "hours": { "$sum": "$hours" }
}}

1 个答案:

答案 0 :(得分:1)

$week根据“输入”日期返回,因此即使您将其调整为一天,Date - 1 day仍将在“前一周”基于从该输入返回的内容

当然,最好让“服务器”执行此类操作,否则您只是“通过线路”提取大量数据,这首先避免了使用数据库的问题。 / p>

简而言之,“从星期六开始”只是意味着当{strong>星期六日时Week 1成为Week 2等等。所以week + 1。唯一真正的警告是,Week 52以上的任何内容都会成为Week 0

因此:

{ "$group": {
  "_id": {
    "$let": {
      "vars": {
        "satWeek": {
          "$cond": {
            "if": { "$eq": [ { "$dayOfWeek": "$Date" }, 7 ] },
            "then": { "$add": [ { "$week": "$Date" }, 1 ] },
            "else": { "$week": "$Date" }
          }
        }          
      },
      "in": {
        "$cond": {
          "if": { "$gt": ["$$satWeek", 52] },
          "then": 0,
          "else": "$$satWeek"
        }
      }
    }
  },
  "hours": { "$sum": "$hours" }
}}

主要调整点基于$dayOfWeek的测试,其中星期六返回7

当然,它是52还是53作为边界取决于它是否是闰年,但是因为你想按“周”聚合,那么我认为你只需要一年最多“,然后根据您的日期选择是否在闰年内,可以将其调整为输入参数。

当然,调整编码更加体贴。但基本原则是调整“输出周”而不是“输入日期”

作为一个替代案例,我猜Date plus 1 day实际上可以让你得到同样的结果,可以向前倾斜所有日期:

{ "$group": {
  "_id": { "$week": { "$add": [ "$Date", 1000 * 60 * 60 * 24 ] } },
  "hours": { "$sum": : "$hours" }
}}

如果你需要本地时区调整,那么从MongoDB 3.6你可以简单地包括时区信息来调整:

{ "$group": {
  "_id": { 
    "$week": { 
      "date": { "$add": [ "$Date", 1000 * 60 * 60 * 24 ] },
      "timezone": "America/Chicago"
    }
  },
  "hours": { "$sum": : "$hours" }
}}

同样请注意,MongoDB 3.4中有$isoWeek和类似的函数,您可以在其中进行一些不同的处理:

  

以ISO 8601格式返回周数,范围从1到53.周数从1开始,周(星期一到星期日)包含年份的第一个星期四。

因此,所有数学都将基于星期一而不是星期日,并考虑到一年中的哪一天被计为“第一周”,以及开始来自1而不是0