Về cơ bản, những gì bạn muốn là một nhóm kép, nhưng bạn không lấy lại được toàn bộ đối tượng ngày bằng cách sử dụng toán tử tổng hợp ngày , chỉ những phần có liên quan:
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"
}
}
}}
])
Đôi $group
cung cấp cho bạn định dạng bạn muốn bằng cách đặt các kết quả vào một mảng mỗi ngày. Một tài liệu trong mẫu, nhưng về cơ bản bạn sẽ nhận được kết quả như sau:
{
"_id" : {
"customerId" : 123,
"day" : 365
},
"hours" : [
{
"hour" : 10,
"pings" : 2,
"links" : 3
}
]
}
Nếu bạn thấy kết quả của toán tử ngày khó xử lý hoặc muốn có kết quả "chuyển qua" đơn giản hóa cho các đối tượng ngày, thì bạn có thể truyền dưới dạng dấu thời gian kỷ nguyên thay thế:
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"
}
}
}}
])
Bí quyết trong đó là khi bạn $subtract
một đối tượng ngày từ một đối tượng khác, kết quả là bạn nhận lại giá trị "kỷ nguyên". Trong trường hợp này, chúng tôi sử dụng ngày bắt đầu "kỷ nguyên" để nhận toàn bộ giá trị dấu thời gian và chỉ cung cấp "toán ngày" để sửa thời gian theo khoảng thời gian được yêu cầu. Vì vậy, kết quả:
{
"_id" : {
"customerId" : 123,
"day" : NumberLong("1419984000000")
},
"hours" : [
{
"hour" : NumberLong("1420020000000"),
"pings" : 2,
"links" : 3
}
]
}
Điều nào có thể dễ chịu hơn đối với bạn so với những gì mà các nhà khai thác ngày cung cấp do đó tùy thuộc vào nhu cầu của bạn.
Bạn cũng có thể thêm một chút viết tắt cho điều này với MongoDB 2.6 qua $let
toán tử cho phép bạn khai báo "biến" cho các hoạt động trong phạm vi:
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"
}
}
}}
])
Ngoài ra, tôi gần như quên đề cập rằng các giá trị của bạn cho "ping" và "liên kết" thực sự là các chuỗi trừ khi đó là lỗi đánh máy. Nhưng nếu không, trước tiên hãy đảm bảo rằng bạn chuyển đổi chúng dưới dạng số.