I recently had to do some of my own aggregations on the Oplog collection to analyze the root cause of a problem. Oplog is the logical replication collection that MongoDB replica sets use, and all the change operations get written into it. I wanted to examine a pattern of writes and how it changed over several days, but the timestamp stored in the Oplog was stored as BSON Timestamp() type. While Aggregation Framework recognizes it as a date type, you cannot do date math on it, and therefore there is no way to drop the hours/minutes/seconds to get to day granularity. You can extract the "year", "month" and "day" but how would you turn them into an ISODate() type since there is no operator to do that (yet)?
Date math to the rescue. Here's a cute way to convert a Timestamp to an ISODate() of day granularity:
Date math to the rescue. Here's a cute way to convert a Timestamp to an ISODate() of day granularity:
db.foos.find({},{_id:0,TimeStamp:1})
{ "TimeStamp" : Timestamp(1384370105,6) }
{ "TimeStamp" : Timestamp(1391248800,0) }
project1={$project:{_id:0,
y:{$subtract:[{$year:"$TimeStamp"}, 2013]},
d:{$subtract:[{$dayOfYear:"$TimeStamp"},1]},
jan1:{$literal:new ISODate("2013-01-01T00:00:00")}
} };
project2={$project:{tsDate:{$add:[
"$jan1",
{$multiply:["$y", 365*24*60*60*1000]},
{$multiply:["$d", 24*60*60*1000]}
] } } };
> db.foos.aggregate(project1, project2)
{ "tsDate" : ISODate("2013-11-13T00:00:00Z") }
{ "tsDate" : ISODate("2014-02-01T00:00:00Z") }
What we did is create a new field of type ISODate which is at midnight of January 1st of this year and add to it number of days of the year from TimeStamp (times 24, times 60, times 60, times 1000) bringing the value of it to midnight of TimeStamp but in ISODate() format. Now we can use it as '_id' field of '$group' stage!