Question

I am trying to figure out how to perform a count of events within a MongoDB collection, by all dates occurring within a specified date range.

Sample Document Schema within MongoDB:

{
    eventNum: 1234,
    startDate:ISODate("2014-01-01"),
    endDate: ISODate("2014-01-04")
    eventType: System Crash
}
{
    eventNum: 4567,
    startDate: ISODate("2014-01-04"),
    endDate: ISODate("2014-01-05")
    eventType: Maintenance
}

What I am trying to get at is for the Date Range:

1/1/2014 - 1/6/2014

What is the total count events for each day within this range?

Results would be:

1/1/2014: 1
1/2/2014: 1
1/3/2014: 1
1/4/2014: 2
1/5/2014: 1
1/6/2014: 0

My problem really lies in the fact that the data being tracked is not on an individual day basis, which would have allowed me to perform a basic aggregation by Date. The events have a start and end date.

Was it helpful?

Solution

More of a mapReduce problem unfortunately as you would otherwise not be able to emit the dates required to make this work.

db.events.mapReduce(
    function() {
        var oneDay = ( 1000 * 60 * 60 * 24 ),
            start = this.startDate.valueOf()
            - ( this.startDate.valueOf() % oneDay ),
            end = ((this.endDate.valueOf()
            - ( this.endDate.valueOf() % oneDay )) + oneDay);

        for ( var day = start; day < end; day += oneDay ) {
           emit ( new Date( day ), 1 );
        }
    },
    function(key, values) {
        return Array.sum( values );
    },
    { 
         "query": { 
             "startDate": { "$gte": new Date("2014-01-01") },
             "endDate": { "$lt": new Date("2014-01-06") }
          },
         "out": { "inline": 1 }
    }
)

If you really want the zero values for events that do not exist within the date range you can alter a little:

db.events.mapReduce(
    function() {
        var oneDay = ( 1000 * 60 * 60 * 24 ),
            start = this.startDate.valueOf()
            - ( this.startDate.valueOf() % oneDay ),
            end = ((this.endDate.valueOf()
            - ( this.endDate.valueOf() % oneDay )) + oneDay);

        for ( var day = start; day < end; day += oneDay ) {
           emit ( new Date( day ), 1 );
        }

        for ( var day = end; day <= ending.valueOf(); day += oneDay ) {
           emit( new Date( day ), 0 );
        }

    },
    function(key, values) {
        return Array.sum( values );
    },
    { 
         "query": { 
             "startDate": { "$gte": new Date("2014-01-01") },
             "endDate": { "$lt": new Date("2014-01-06") }
          },
          "scope": { "ending": new Date("2014-01-06") },
          "out": { "inline": 1 }
    }
)

Which gives you the output you want:

   "results" : [
            {
                    "_id" : ISODate("2014-01-01T00:00:00Z"),
                    "value" : 1
            },
            {
                    "_id" : ISODate("2014-01-02T00:00:00Z"),
                    "value" : 1
            },
            {
                    "_id" : ISODate("2014-01-03T00:00:00Z"),
                    "value" : 1
            },
            {
                    "_id" : ISODate("2014-01-04T00:00:00Z"),
                    "value" : 2
            },
            {
                    "_id" : ISODate("2014-01-05T00:00:00Z"),
                    "value" : 1
            },
            {
                    "_id" : ISODate("2014-01-06T00:00:00Z"),
                    "value" : 0
            }
]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top