2013-04-11 10 views
33

mam pewne dokumenty w Mongo, która wygląda mniej więcej tak:MongoDB kruszywo ciągu codziennej ugrupowania

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

Idealnie chciałabym móc liczyć, według marki, liczba sprzedanych pojazdów dziennie. Chciałbym zobaczyć dzisiaj albo takie okno jak dziś przez ostatnie siedem dni.

udało mi się zrealizować codzienny widok z jakiegoś brzydkiego kodu

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 } } } 
) 

które następnie daje wyniki

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

tak, że jest w porządku, ale byłoby znacznie wolą nie mieć do zmian dwie wartości datetime w zapytaniu. Następnie, jak wspomniałem powyżej, chciałbym móc uruchomić tę kwerendę (ponownie, bez konieczności modyfikowania jej za każdym razem) i zobaczyć te same wyniki w ciągu dnia w ciągu ostatniego tygodnia.

No i oto przykładowe dane używam dla zapytania

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")}); 

góry dzięki, Kevin

+0

Jakie jest znaczenie 676Z? –

+0

Po prostu aktualizacja (od 2017, wow to pytanie jest stare ..), zmieniłem zaakceptowaną odpowiedź, ponieważ ramy Mongo ewoluowały, aby rozwiązać to znacznie łatwiej. Mimo to Asya odpowiada za jej oryginalną odpowiedź. @AboozarRajabi, "676Z" jest opcjonalną częścią formatu czasu [ISO 8601] (https://en.wikipedia.org/wiki/ISO_8601), w tym przypadku "676Z" reprezentuje dwie części 2013-04-10T11: 35: 50.676Z, pierwsze 676 to milisekundy, a "Z" to skrót oznaczający strefę czasową UTC. – Kevin

Odpowiedz

48

W Mongo 2,8 RC2 jest nowy podmiot agregacji danych: $dateToString, które mogą być stosowane do grupy przez jeden dzień, a po prostu do „YYYY-MM-DD” w wyniku:

przykład z dokumentów:

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

spowoduje:

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

Czystsze rozwiązanie! Dobra odpowiedź! Powinien zostać zaktualizowany, ponieważ górny jest zdecydowanie brudny. – wegginho

+4

Zgodnie z dokumentacją, $ dateToString zwraca ciąg, więc możesz utracić pewną możliwość, którą będziesz mieć z obiektem daty –

+0

, który jest świetnym rozwiązaniem do grupowania w dzień: D –

39

Czasami warto spojrzeć na moim blogu wpis na temat radzić sobie z różnymi manipulacjami datami w ramach Aggregation Framework here.

Co można zrobić, to użyć fazy $project, aby skrócić daty do codziennej rozdzielczości, a następnie uruchomić agregację w całym zestawie danych (lub tylko jej części) i zagregować według daty i marki.

z danymi przykładowymi, że chcesz wiedzieć, ile pojazdów sprzedałeś według marki, według daty w tym roku:

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 
     } 
    } 
}; 

Teraz działa agregację daje:

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 
} 

Można dodaj kolejną fazę {$ project}, aby ładować dane wyjściowe i możesz dodać krok {$ sort}, ale w zasadzie dla każdej daty, dla każdego z nich uzyskasz liczbę sprzedanych produktów.

+1

Asiu, dziękuję, to jest dokładnie to, czego szukałem. Miałem problemy z wyłamywaniem funkcji, więc to naprawdę pomaga. Jeszcze raz dziękuję. – Kevin

+0

Dla przyszłych czytelników, celem pierwszej projekcji jest wyodrębnienie godzin/minut/sekund, a drugim jest odjęcie ich od pierwotnych dat - porzucenie zaokrąglonych dat – ZECTBynmo

2

Lubię user1083621 „s odpowiedzi, ale ta metoda powoduje pewne ograniczenia w następujących operacji dowcip h to pole - ponieważ nie można go użyć jako pola daty w (na przykład) kolejnych etapach potoku agregacji. Nie możesz porównywać ani używać żadnego date aggregation operations, a po agregacji będziesz miał ciągi (!). Wszystko to można rozwiązać, wyświetlając swoje oryginalne pole daty, ale w takim przypadku będziesz miał trudności z zachowaniem go na etapie grupowania. I w końcu, czasami po prostu chcesz manipulować z początkiem dnia, a nie z dowolnym czasem dnia.Więc oto moja metoda:

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

Daje to:

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

nie mogę powiedzieć czy to szybciej niż metodą user1083621 „s.

Powiązane problemy