Question

I have a mongo document in which some registers are stored by with a date, for instance in a very simplified manner:

{
    "vehicleId" : "vehicle4",
    "telemetryDate" : ISODate("2013-06-22T05:00:00Z"),
    "alarmsTotal" : 9
}
{
    "vehicleId" : "vehicle5",
    "telemetryDate" : ISODate("2013-06-20T05:00:00Z"),
    "alarmsTotal" : 2
}

I need to perform a group by the vhicleId, summing the alarm total. Nevertheless there is an option to exclude or not the Weekends (Saturday, Sunday).

I have been searching to no avail the answer to this question with Mongo. My query, without taking the exclude weekends into account that works correctly is:

{
   "aggregate":"telemetrySummary",
   "pipeline":[
      {
         "$match":{
            "telemetryDate":{
               "$gte":{
                  "$date":"2013-06-20T05:00:00.000Z"
               },
               "$lte":{
                  "$date":"2013-06-24T05:00:00.000Z"
               }
            }
         }
      },
      {
         "$group":{
            "_id":{
               "vehicleId":"$vehicleId"
            },
            "alarms":{
               "$sum":"$alarmsTotal"
            }
         }
      }
   ]
}

I need to exclude the values where the mongo operator $dayOfWeek is 1 (sunday) and 7 (sundays). I tried through many queries, one of the logical ones being:

{
   "aggregate":"telemetrySummary",
   "pipeline":[
      {
         "$match":{
            "telemetryDate":{
               "$gte":{
                  "$date":"2013-06-20T05:00:00.000Z"
               },
               "$lte":{
                  "$date":"2013-06-24T05:00:00.000Z"
               }
            },
            "{ \"$dayOfWeek\" : \"$telemetryDate\"}":{
               "$in":[2,3,4,5,6]
            }
         }
      },
      {
         "$group":{
            "_id":{
               "vehicleId":"$vehicleId"
            },
            "alarms":{
               "$sum":"$alarmsTotal"
            }
         }
      }
   ]
}

I think my real problem is basically that I do not know how to store a $dayOfWeek operation over a non-grupable variable as a telemetryDate into a day variable that I can compare with a $in operator.

Thanks for reading and hoping to find some guidance :)

Was it helpful?

Solution 2

It's actually pretty simple to do this - you can use $project to create new fields that didn't exist in the original document - you can compute new values or you can conditionally project different values, depending on what original values are. You can even calculate in a single group phase both total alarms as well as only alarms on weekdays.

Here is what you need to do in the project phase:

{"$project" : {
        "vehicleId" : 1,
        "telemetryDate" : 1,
        "alarmsTotal" : 1,
        "alarmsWeekdays" : {
            "$cond" : [
                {
                    "$or" : [
                        {
                            "$eq" : [
                                {
                                    "$dayOfWeek" : "$telemetryDate"
                                },
                                1
                            ]
                        },
                        {
                            "$eq" : [
                                {
                                    "$dayOfWeek" : "$telemetryDate"
                                },
                                7
                            ]
                        }
                    ]
                },
                0,
                "$alarmsTotal"
            ]
        }
    }
}

The above says: "Pass through these three fields, but also compute a new field alarmWeekdays like so: ..."

The actual expression in more "English" terms happens to be:

IF `$dayOfWeek` of telemetryDate is equal to 1 
OR `$dayOfWeek` of telemetryDate is equal to 7
THEN `$project` the value 0 as `alarmsWeekdays
ELSE `$project` the value from `$alarmsTotal` as `alarmsWeekdays`

OTHER TIPS

You should use $dayOfWeek inside $project like so:

[{<your match by date>}, {$project:{vehicleId:1, telemetryDate:1, alarmsTotal:1, dow:{$dayOfWeek:'$telemetryDate'}}}, {$match:{dow:{$in:[2,3,4,5,6]}}}, <rest of pipeline> ]

btw, your first $match can benefit from using index, like normal find command. It will improve performance on large datasets.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top