Wat je eigenlijk wilt is een dubbele groepering, maar je krijgt niet het hele date-object terug met de operators voor datumaggregatie , alleen de relevante onderdelen:
db.collection.aggregate([
{ "$group": {
"_id": {
"customerId": "$customerId",
"day": { "$dayOfYear": "$startTime" },
"hour": { "$hour": "$startTime" }
},
"pings": { "$sum": "$ping" },
"links": { "$sum": "$link" }
}},
{ "$group": {
"_id": {
"customerId": "$_id.customerId",
"day": "$_id.day"
},
"hours": {
"$push": {
"hour": "$_id.hour",
"pings": "$pings",
"links": "$links"
}
}
}}
])
De dubbele $group
geeft u het gewenste formaat door de resultaten per dag in een array te plaatsen. Eén document in het voorbeeld, maar u krijgt in principe de volgende resultaten:
{
"_id" : {
"customerId" : 123,
"day" : 365
},
"hours" : [
{
"hour" : 10,
"pings" : 2,
"links" : 3
}
]
}
Als u de resultaten van de datumoperatoren te moeilijk vindt om mee om te gaan of als u een vereenvoudigd "pass-through"-resultaat voor datumobjecten wilt, dan kunt u in plaats daarvan als epoch-tijdstempels casten:
db.collection.aggregate([
{ "$group": {
"_id": {
"customerId": "$customerId",
"day": {
"$subtract": [
{ "$subtract": [ "$startTime", new Date("1970-01-01") ] },
{
"$mod": [
{ "$subtract": [ "$startTime", new Date("1970-01-01") ] },
1000*60*60*24
]
}
]
},
"hour": {
"$subtract": [
{ "$subtract": [ "$startTime", new Date("1970-01-01") ] },
{
"$mod": [
{ "$subtract": [ "$startTime", new Date("1970-01-01") ] },
1000*60*60
]
}
]
}
},
"pings": { "$sum": "$ping" },
"links": { "$sum": "$link" }
}},
{ "$group": {
"_id": {
"customerId": "$_id.customerId",
"day": "$_id.day"
},
"hours": {
"$push": {
"hour": "$_id.hour",
"pings": "$pings",
"links": "$links"
}
}
}}
])
De truc is wanneer je $subtract
het ene datumobject van het andere krijg je daardoor de "epoch"-waarde terug. In dit geval gebruiken we de "epoch" startdatum om de hele tijdstempelwaarde te krijgen en geven we gewoon de "date math" om de tijden te corrigeren naar de vereiste intervallen. Dus het resultaat:
{
"_id" : {
"customerId" : 123,
"day" : NumberLong("1419984000000")
},
"hours" : [
{
"hour" : NumberLong("1420020000000"),
"pings" : 2,
"links" : 3
}
]
}
Wat voor u misschien smakelijker is dan wat de datumoperators bieden, afhankelijk van uw behoeften.
Je kunt hier ook een korte afkorting voor toevoegen met MongoDB 2.6 via de $let
operator waarmee u "variabelen" kunt declareren voor bewerkingen met een bereik:
db.event.aggregate([
{ "$group": {
"_id": {
"$let": {
"vars": {
"date": { "$subtract": [ "$startTime", new Date("1970-01-01") ] },
"day": 1000*60*60*24,
"hour": 1000*60*60
},
"in": {
"customerId": "$customerId",
"day": {
"$subtract": [
"$$date",
{ "$mod": [ "$$date", "$$day" ] }
]
},
"hour": {
"$subtract": [
"$$date",
{ "$mod": [ "$$date", "$$hour" ] }
]
}
}
}
},
"pings": { "$sum": "$ping" },
"links": { "$sum": "$link" }
}},
{ "$group": {
"_id": {
"customerId": "$_id.customerId",
"day": "$_id.day"
},
"hours": {
"$push": {
"hour": "$_id.hour",
"pings": "$pings",
"links": "$links"
}
}
}}
])
Ook vergat ik bijna te vermelden dat uw waarden voor "ping" en "link" eigenlijk strings zijn, tenzij dat een typfout is. Maar als dat niet het geval is, zorg er dan voor dat je ze eerst als getallen converteert.