2013-04-11 13 views
33

Tôi có một số tài liệu trong Mongo mà trông giống như sau:MongoDB tổng hợp trong nhóm hàng ngày

{ 
    _id : ObjectId("..."), 
    "make" : "Nissan", 
    .. 
}, 
{ 
    _id : ObjectId("..."), 
    "make" : "Nissan", 
    "saleDate" : ISODate("2013-04-10T12:39:50.676Z"), 
    .. 
} 

Lý tưởng nhất, tôi muốn để có thể đếm, bởi thực hiện, số lượng xe bán ra mỗi ngày. Sau đó tôi muốn xem hôm nay hoặc một cửa sổ như hôm nay trong bảy ngày qua.

tôi đã có thể thực hiện được quan điểm hàng ngày với một số mã xấu xí

db.inventory.aggregate(
    { $match : { "saleDate" : { $gte: ISODate("2013-04-10T00:00:00.000Z"), $lt: ISODate("2013-04-11T00:00:00.000Z") } } } , 
    { $group : { _id : { make : "$make", saleDayOfMonth : { $dayOfMonth : "$saleDate" } }, cnt : { $sum : 1 } } } 
) 

Mà sau đó mang lại kết quả

{ 
    "result" : [ 
    { 
     "_id" : { 
     "make" : "Nissan", 
     "saleDayOfMonth" : 10 
     }, 
     "cnt" : 2 
    }, 
    { 
     "_id" : { 
     "make" : "Toyota", 
     "saleDayOfMonth" : 10 
     }, 
     "cnt" : 4 
    }, 
    ], 
    "ok" : 1 
} 

Vì vậy, đó là ok, nhưng tôi rất muốn để không phải thay đổi hai giá trị datetime trong truy vấn. Sau đó, như tôi đã đề cập ở trên, tôi muốn có thể chạy truy vấn này (một lần nữa, mà không phải sửa đổi nó mỗi lần) và xem kết quả tương tự được binned theo ngày trong tuần trước.

Oh và đây là dữ liệu mẫu Tôi đã sử dụng cho truy vấn

db.inventory.save({"make" : "Nissan","saleDate" : ISODate("2013-04-10T12:39:50.676Z")}); 
db.inventory.save({"make" : "Nissan"}); 
db.inventory.save({"make" : "Nissan","saleDate" : ISODate("2013-04-10T11:39:50.676Z")}); 
db.inventory.save({"make" : "Toyota","saleDate" : ISODate("2013-04-09T11:39:50.676Z")}); 
db.inventory.save({"make" : "Toyota","saleDate" : ISODate("2013-04-10T11:38:50.676Z")}); 
db.inventory.save({"make" : "Toyota","saleDate" : ISODate("2013-04-10T11:37:50.676Z")}); 
db.inventory.save({"make" : "Toyota","saleDate" : ISODate("2013-04-10T11:36:50.676Z")}); 
db.inventory.save({"make" : "Toyota","saleDate" : ISODate("2013-04-10T11:35:50.676Z")}); 

Cảm ơn trước, Kevin

+0

Ý nghĩa của 676Z là gì? –

+0

Chỉ cần cập nhật (từ năm 2017, câu hỏi này là cũ ..), tôi đã thay đổi câu trả lời được chấp nhận vì khung Mongo đã phát triển để giải quyết vấn đề này dễ dàng hơn nhiều. Tuy nhiên đạo cụ cho Asya cho câu trả lời ban đầu của cô. @AboozarRajabi, "676Z" là một phần tùy chọn của định dạng thời gian [ISO 8601] (https://en.wikipedia.org/wiki/ISO_8601), trong trường hợp này "676Z" đại diện cho hai phần của 2013-04-10T11: 35: 50.676Z, đầu tiên 676 là mili giây và "Z" là phím tắt để biểu thị múi giờ UTC. – Kevin

Trả lời

48

Trong Mongo 2.8 RC2 có một nhà điều hành mới tập hợp dữ liệu: $dateToString mà có thể được sử dụng để nhóm bởi một ngày và chỉ đơn giản là có một "YYYY-MM-DD" trong kết quả:

Ví dụ từ các tài liệu:

db.sales.aggregate(
    [ 
    { 
     $project: { 
       yearMonthDay: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, 
       time: { $dateToString: { format: "%H:%M:%S:%L", date: "$date" } } 
     } 
    } 
    ] 
) 

sẽ cho kết quả:

{ "_id" : 1, "yearMonthDay" : "2014-01-01", "time" : "08:15:39:736" } 
+0

Giải pháp sạch hơn! Câu trả lời tốt! Nên được cập nhật kể từ khi trên là definetly bẩn. – wegginho

+4

Theo tài liệu, $ dateToString trả về một chuỗi, vì vậy bạn có thể mất một số khả năng bạn sẽ có với đối tượng ngày –

+0

đó là giải pháp tuyệt vời để nhóm theo ngày: D –

39

Bạn có thể muốn có một cái nhìn tại blog entry của tôi về làm thế nào để xử lý các thao tác ngày khác nhau trong Khung tổng hợp here.

Việc bạn có thể làm là sử dụng giai đoạn $project để cắt bớt ngày của bạn thành độ phân giải hàng ngày và sau đó chạy tập hợp trên toàn bộ tập dữ liệu (hoặc chỉ một phần) và tổng hợp theo ngày tháng và thực hiện.

Với dữ liệu mẫu của bạn, nói rằng bạn muốn biết có bao nhiêu xe bạn bán thực hiện, theo ngày trong năm nay:

match={"$match" : { 
       "saleDate" : { "$gt" : new Date(2013,0,1) } 
     } 
}; 

proj1={"$project" : { 
     "_id" : 0, 
     "saleDate" : 1, 
     "make" : 1, 
     "h" : { 
      "$hour" : "$saleDate" 
     }, 
     "m" : { 
      "$minute" : "$saleDate" 
     }, 
     "s" : { 
      "$second" : "$saleDate" 
     }, 
     "ml" : { 
      "$millisecond" : "$saleDate" 
     } 
    } 
}; 

proj2={"$project" : { 
     "_id" : 0, 
     "make" : 1, 
     "saleDate" : { 
      "$subtract" : [ 
       "$saleDate", 
       { 
        "$add" : [ 
         "$ml", 
         { 
          "$multiply" : [ 
           "$s", 
           1000 
          ] 
         }, 
         { 
          "$multiply" : [ 
           "$m", 
           60, 
           1000 
          ] 
         }, 
         { 
          "$multiply" : [ 
           "$h", 
           60, 
           60, 
           1000 
          ] 
         } 
        ] 
       } 
      ] 
     } 
    } 
}; 

group={"$group" : { 
     "_id" : { 
      "m" : "$make", 
      "d" : "$saleDate" 
     }, 
     "count" : { 
      "$sum" : 1 
     } 
    } 
}; 

Bây giờ chạy tập mang đến cho bạn:

db.inventory.aggregate(match, proj1, proj2, group) 
{ 
    "result" : [ 
     { 
      "_id" : { 
       "m" : "Toyota", 
       "d" : ISODate("2013-04-10T00:00:00Z") 
      }, 
      "count" : 4 
     }, 
     { 
      "_id" : { 
       "m" : "Toyota", 
       "d" : ISODate("2013-04-09T00:00:00Z") 
      }, 
      "count" : 1 
     }, 
     { 
      "_id" : { 
       "m" : "Nissan", 
       "d" : ISODate("2013-04-10T00:00:00Z") 
      }, 
      "count" : 2 
     } 
    ], 
    "ok" : 1 
} 

Bạn có thể thêm một {$ dự án} giai đoạn để tăng sản lượng và bạn có thể thêm một {$ sort} bước, nhưng về cơ bản cho mỗi ngày, cho mỗi làm cho bạn nhận được một số bao nhiêu đã được bán.

+1

Asya, cảm ơn bạn đây chính xác là những gì tôi đang tìm kiếm. Tôi cũng đã vật lộn với việc phá vỡ các chức năng, vì vậy điều này thực sự hữu ích. Cám ơn bạn một lần nữa. – Kevin

+0

Đối với người đọc trong tương lai, mục đích của phép chiếu đầu tiên là trích xuất giờ/phút/giây và thứ hai là trừ chúng khỏi thời gian biểu gốc - để lại ngày làm tròn – ZECTBynmo

2

tôi thích user1083621 's câu trả lời nhưng phương pháp gây ra một số hạn chế trong hoạt động sau wit h trường này - bởi vì bạn không thể sử dụng nó làm trường ngày trong (ví dụ) các giai đoạn đường ống tổng hợp tiếp theo. Bạn không thể so sánh và cũng không sử dụng bất kỳ date aggregation operations và sau khi tổng hợp bạn sẽ có chuỗi (!). Tất cả những điều đó có thể được giải quyết bằng cách chiếu trường ngày ban đầu của bạn nhưng trong trường hợp đó bạn sẽ gặp một số khó khăn khi giữ lại nó thông qua giai đoạn groupping. Và sau cùng, đôi khi bạn chỉ muốn thao tác với đầu ngày, không phải với thời gian ban ngày tùy ý.Vì vậy, đây là phương pháp của tôi:

{'$project': { 
    'start_of_day': {'$subtract': [ 
     '$date', 
     {'$add': [ 
      {'$multiply': [{'$hour': '$date'}, 3600000]}, 
      {'$multiply': [{'$minute': '$date'}, 60000]}, 
      {'$multiply': [{'$second': '$date'}, 1000]}, 
      {'$millisecond': '$date'} 
     ]} 
    ]}, 
}} 

Nó cung cấp cho bạn điều này:

{ 
    "start_of_day" : ISODate("2015-12-03T00:00:00.000Z") 
}, 
{ 
    "start_of_day" : ISODate("2015-12-04T00:00:00.000Z") 
} 

không thể nói nếu nó nhanh hơn bất kỳ phương pháp user1083621 's.