2014-06-19 18 views
5

Używam agregacji mongodb do agregowania zestawu danych. Moja sytuacja jest nieco skomplikowana. Mam kolekcję w następujący sposób:Agregacja Mongodb według dnia, następnie Godzina

{ 
    startTime: ISODate("2014-12-31T10:20:30Z"), 
    customerId: 123, 
    ping: "2", 
    link: "3" 
} 

Teraz chcę do agregowania danych do innej kolekcji w następujący sposób:

{ 
_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 
    } 
] 
} 

Jak widać dane jest grupa na dzień, a potem przez godziny. Mam następujące zapytanie o agregację, aby je pogrupować według dnia, ale jak pogrupować je według godzin? Dowolny pomysł?

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

Odpowiedz

7

Co w zasadzie chcą się podwójne zgrupowanie, ale nie dostaniesz cały obiekt Date powrotem używając date aggregation operators, tylko odpowiednie części:

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

Podwójna $group daje żądany format, umieszczając wyniki w tablicy na dzień. Pojedynczy dokument w próbce, ale można w zasadzie uzyskać wyniki tak:

{ 
    "_id" : { 
      "customerId" : 123, 
      "day" : 365 
    }, 
    "hours" : [ 
      { 
        "hour" : 10, 
        "pings" : 2, 
        "links" : 3 
      } 
    ] 
} 

Jeśli znajdziesz wyniki od daty operatorów do trudną do pokonania lub chcą uproszczony wynik „pass-through” DATE obiektów, a następnie można rzucić jako epoka znacznika czasu zamiast:

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

Sztuką tam jest, kiedy $subtract jeden obiekt z innego data pojawi się wartość „epoki” z powrotem w wyniku. W tym przypadku używamy daty początkowej "epoki", aby uzyskać całą wartość datownika i po prostu podać "matematykę daty", aby skorygować czasy do wymaganych przedziałów. Tak więc wynik:

{ 
    "_id" : { 
      "customerId" : 123, 
      "day" : NumberLong("1419984000000") 
    }, 
    "hours" : [ 
      { 
        "hour" : NumberLong("1420020000000"), 
        "pings" : 2, 
        "links" : 3 
      } 
    ] 
} 

Co może być dla ciebie bardziej smaczne niż to, co dana jest przez operatorów w zależności od Twoich potrzeb.

Można też dodać trochę skrót do tego z MongoDB 2.6 poprzez operatora $let który pozwala zadeklarować „zmienne” dla scoped operacji:

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

Także ja prawie zapomniałem wspomnieć, że wartości dla „ping "i" link "są w rzeczywistości łańcuchami, chyba że jest to literówka. Ale jeśli nie, to najpierw przekonwertuj je na liczby.

+0

Dzięki. Czy możesz mi jeszcze bardziej pomóc? Mam harmonogram, który będzie działał co godzinę, aby zebrać dane z ostatniej godziny w następujący sposób: '{" _id ": { " customerId ": 123," day ": ISODate (" 2012-06-20: 00: 00 : 00Z ")}, " godziny ": [{" godzina ": ISODate (" 2012-06-20: 01: 00: 00Z ")," pingi ": 2," linki ": 3}]}' Po z powyższym zapytaniem dotyczącym agregacji, muszę scalić/upsert to do tej kolekcji, aby przechowywać zagregowane dane. Jak mogę to osiągnąć? Dzięki – user3756522

+0

@ user3756522 To naprawdę brzmi jak inne pytanie i jest najlepiej zadawane jako nowy post, w którym wyjaśnisz swój zamiar właściwie, a nie w komentarzach. Kwerenda pokazana w odpowiedzi będzie agregować według każdego dnia i każdej godziny dla zakresu, który wysłałeś. Również twój '$ match' dla tego powinien zawsze być ** pierwszym ** etapem potoku. Z MongoDB 2.6 Twoje dane wyjściowe mogą być kursorem, który możesz iterować w celu przetworzenia wyników –

Powiązane problemy