从mongodb的周数获得一周的第一天

时间:2014-07-29 10:23:09

标签: mongodb mongodb-query aggregation-framework

我有包含日期​​字段的集合。我按周和其他相关字段对记录进行分组。

这是我的聚合查询:

db.raw.aggregate([
    { "$match" : {
        "Timestamp":{
            "$gte": new Date("2012-05-30"), 
            "$lt": new Date("2014-07-31")
        }
    }},
    { "$group" : {
        "_id":{ 
            "ApplicationId": "$ApplicationId",
            "Country": "$Country",
            "week":{ "$week": "$Timestamp" }
        },
        "Date":{ "$first": "$Timestamp" },
        "Visits": { "$sum": 1 }
    }}
])

我想项目:从周数开始的访问和开始日期。

4 个答案:

答案 0 :(得分:3)

您似乎想要一个"日期值"表示一周开始的日期。你最好的方法是"日期数学"在汇总运营商$dayOfWeek的帮助下:

db.raw.aggregate([
  { "$match" : {
    "Timestamp":{
      "$gte": new Date("2012-05-30"), 
      "$lt": new Date("2014-07-31")
    }
  }},
  { "$group" : {
    "_id":{ 
      "ApplicationId": "$ApplicationId",
      "Country": "$Country",
      "weekStart":{ 
        "$subtract": [
          { "$subtract": [
            { "$subtract": [ "$Timestamp", new Date("1970-01-01") ] },
            { "$cond": [
              { "$eq": [{ "$dayOfWeek": "$Timestamp" }, 1 ] },
              0,
              { "$multiply": [
                1000 * 60 * 60 * 24,
                { "$subtract": [{ "$dayOfWeek": "$Timestamp" }, 1 ] }
              ]}
            ]}
          ]},
          { "$mod": [
            { "$subtract": [
              { "$subtract": [ "$Timestamp", new Date("1970-01-01") ] },
              { "$cond": [
                { "$eq": [{ "$dayOfWeek": "$Timestamp" }, 1 ] },
                0,
                { "$multiply": [
                  1000 * 60 * 60 * 24,
                  { "$subtract": [{ "$dayOfWeek": "$Timestamp" }, 1 ] }
                ]}
              ]}
            ]},
            1000 * 60 * 60 * 24
          ]}
        ]
      }
    },
    "Date":{ "$first": "$Timestamp" },
    "Visits": { "$sum": 1 }
  }}
])

或者来自MongoDB 2.6及以上版本的$let更清洁一点:

db.raw.aggregate([
  { "$match" : {
    "Timestamp":{
      "$gte": new Date("2012-05-30"), 
      "$lt": new Date("2014-07-31")
    }
  }},
  { "$group" : {
    "_id":{ 
      "ApplicationId": "$ApplicationId",
      "Country": "$Country",
      "weekStart":{ 
        "$let": {
          "vars": {
            "dayMillis": 1000 * 60 * 60 * 24,
            "beginWeek": {
              "$subtract": [
                { "$subtract": [ "$Timestamp", new Date("1970-01-01") ] },
                { "$cond": [
                  { "$eq": [{ "$dayOfWeek": "$Timestamp" }, 1 ] },
                  0,
                  { "$multiply": [
                    1000 * 60 * 60 * 24,
                    { "$subtract": [{ "$dayOfWeek": "$Timestamp" }, 1 ] }
                  ]}
                ]} 
              ]
            }
          },
          "in": {
            "$subtract": [
              "$$beginWeek",
              { "$mod": [ "$$beginWeek", "$$dayMillis" ]}
            ]
          }
        }
      }
    },
    "Date":{ "$first": "$Timestamp" },
    "Visits": { "$sum": 1 }
  }}
])

"分组"中的结果值是一个纪元毫秒,表示一周开始的一天的开始。 "本周的开始"通常被认为是"星期日",所以如果你想要另一天,那么你需要调整适当的金额。具有$dayMillis变量值的$add运算符可用于此处应用"星期一"例如。

它不是日期对象,但您可以轻松地将其提供给另一种方法,以便在后期处理中构建日期对象。

另请注意,您正在使用的其他内容(例如$first)通常要求文档按特定顺序排序,或者通常由您的"时间戳"值。如果这些文档尚未订购,那么您首先$sort或使用$min之类的运算符来获取该范围内的第一个实际时间戳。

答案 1 :(得分:1)

对于mongo> = v3.4,请查看weekStart。 我们的想法是从给定的时间戳

中减去毫秒数
db.raw.aggregate([
         // stage 1
        { "$match" : {
            "Timestamp":{
                "$gte": ISODate("2012-05-30"), 
                "$lt": ISODate("2014-07-31")
            }
        }},

         // stage 2
        { "$project" : {

           ApplicationId: 1,
           Country: 1,
           week: {$isoWeek: "$Timestamp"},

           // [TRICK IS HERE] Timestamp - dayOfWeek * msInOneDay
           weekStart: { $dateToString: { format: "%Y-%m-%d", date: { // convert date
             $subtract: ["$Timestamp", {$multiply: [ {$subtract:[{$isoDayOfWeek: "$Timestamp"},1]}, 86400000]}] 
           }}},

         // stage 3
        { "$group" : {
        "_id":{ 
            "ApplicationId": "$ApplicationId",
            "Country": "$Country",
            "week": "$week"
        },
        "Date":{ "$first": "$weekStart" },
        "Visits": { "$sum": 1 }
        }}        
 ])

答案 2 :(得分:1)

使用MongoDB 3.6

{ 
  '$project' : {
    'firstDateOfWeek': {
        '$dateFromString': {
            'dateString': {
                '$concat': [
                    {
                        '$toString': '$_id.year'
                    }, 
                    '-', 
                    {
                        '$toString': '$_id.week'
                    }
                ]
             },
             'format': "%G-%V"
         }
     }
  }
}

答案 3 :(得分:0)

来自 mongo 3.6 https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromParts/

db.raw.aggregate([
    {
        "$match": {
            "Timestamp": {
                "$gte": new Date("2012-05-30"),
                "$lt": new Date("2014-07-31")
            }
        }
    },
    {
        "$group": {
            "_id": {
                "ApplicationId": "$ApplicationId",
                "Country": "$Country",
                "week": {
                    "$isoWeek": "$Timestamp"
                },
                                "year": {
                                    "$year": "$Timestamp"
                                }
            },
            "Visits": {
                "$sum": 1
            }
        }
    },
    {
        "$addFields": {
            "Date": {
                $dateFromParts: {
                    isoWeekYear: '$_id.year',
                    isoWeek: '$_id.week',
                    isoDayOfWeek: 1
                }
            }
        }
    }
])