Агрегация Mongodb по дням и часам
Я использую агрегацию mongodb для агрегирования набора данных. Моя ситуация немного сложная. У меня есть коллекция следующим образом:
{
startTime: ISODate("2014-12-31T10:20:30Z"),
customerId: 123,
ping: "2",
link: "3"
}
Теперь я хочу объединить данные в другую коллекцию следующим образом:
{
_id: {
day: ISODate("2014-12-31T00:00:00Z"),
customerId: 123
},
hours: [
{
hour: ISODate("2014-12-31T10:00:00Z"),
pings: 2,
links: 3
},
{
hour: ISODate("2014-12-31T11:00:00Z"),
pings: 5,
links: 6
}
]
}
как вы можете видеть, данные группируются по дням, а затем по часам. У меня есть следующий запрос агрегации, чтобы сгруппировать их по дням, но как сгруппировать их по часам? Есть Идеи?
var pipeline = [
{
$project : {
startTime : 1,
customerId: 1,
ping:1,
link:1,
date : "$startTime",
h : {
"$hour" : "$startTime"
},
m : {
"$minute" : "$startTime"
},
s : {
"$second" : "$startTime"
},
ml : {
"$millisecond" : "$startTime"
}
}
},
{
$project: {
startTime : 1,
customerId: 1,
ping:1,
link:1,
date : {
"$subtract" : [
"$date",
{
"$add" : [
"$ml",
{
"$multiply" : [
"$s",
1000
]
},
{
"$multiply" : [
"$m",
60,
1000
]
},
{
"$multiply" : [
"$h",
60,
60,
1000
]
}
]
}
]
}
}
},
{
$match: {
"startTime": {
$gte: new ISODate("2013-12-01T07:00:00Z"),
$lte: new ISODate("2014-01-01T08:00:00Z"),
}
}
},
// Aggregate the data
{
$group: {
_id: {day : "$date", customerId: "$customerId"},
pings : {$sum: "$ping"},
links : {$sum: "$links"}
}
}
];
1 ответов
то, что вы в основном хотите, - это двойная группировка, но вы не получаете весь объект date обратно с помощью дата операторы агрегирования, только соответствующие части:
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"
}
}
}}
])
двойной $group
дает вам формат, который вы хотите, помещая результаты в массив в день. Один документ в образце, но вы в основном получаете такие результаты:
{
"_id" : {
"customerId" : 123,
"day" : 365
},
"hours" : [
{
"hour" : 10,
"pings" : 2,
"links" : 3
}
]
}
если вы найдете результаты операторов даты трудно иметь дело или хотите упрощенный" сквозной " результат для объектов даты, тогда вы можете использовать вместо этого метки времени эпохи:
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"
}
}
}}
])
фокус в том, когда вы $subtract
один объект даты от другого вы получаете значение "эпохи" обратно в результате. В этом случае мы используем дату начала "эпохи", чтобы получить значение всей метки времени и просто предоставить "математику даты", чтобы исправить время до требуемых интервалов. Так результат:
{
"_id" : {
"customerId" : 123,
"day" : NumberLong("1419984000000")
},
"hours" : [
{
"hour" : NumberLong("1420020000000"),
"pings" : 2,
"links" : 3
}
]
}
что может быть более приемлемым для Вас, чем дата операторы предоставляют в результате в зависимости от ваших потребностей.
вы также можете добавить немного стенографии для этого с MongoDB 2.6 через $let
оператор, который позволяет объявлять "переменные" для операций с областью действия:
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"
}
}
}}
])
также я почти забыл упомянуть, что ваши значения для "ping" и "link" на самом деле являются строками, если это не опечатка. Но если нет, тогда убедитесь, что вы сначала конвертируете их в числа.