2014-06-19 10 views
5

Ich verwende Mongodb-Aggregation, um Datenmenge zu aggregieren. Meine Situation ist ein bisschen komplex. Ich habe eine Sammlung, wie folgend:Mongodb-Aggregation nach Tag und dann Stunde

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

Jetzt möchte ich Daten auf eine andere Sammlung aggregieren, wie folgend:

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

Wie Sie die Datengruppe sehen bis zum Tag und dann nach Stunden. Ich habe folgende Aggregationsabfrage, um sie am Tag zu gruppieren, aber wie gruppiere ich sie um Stunden weiter? Irgendeine Idee?

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

Antwort

7

Was Sie im Grunde wollen, ist eine doppelte Gruppierung, aber bekommt man nicht das gesamte Date-Objekt zurück date aggregation operators verwenden, nur die relevanten Teile:

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

Die doppelte $group gibt Ihnen die Format, das Sie möchten, indem Sie die Ergebnisse pro Tag in ein Array einfügen. Einzelnes Dokument in der Probe, aber sie im Grunde erhalten Ergebnisse wie folgt aus:

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

Wenn Sie die Ergebnisse der Zeitpunkt Betreiber schwer zu behandeln oder ein vereinfachtes „Pass-Through“ Ergebnis für Datumsobjekte wollen, dann Sie könnten werfen als Epoche Zeitstempel statt:

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

Der Trick da drin ist, wenn Sie $subtract ein Date-Objekt von einem anderen Sie die „Epoche“ Wert zurück als Ergebnis erhalten. In diesem Fall verwenden wir das Startdatum "epoch", um den gesamten Zeitstempelwert zu erhalten, und stellen nur die "Datumsmathematik" bereit, um die Zeiten auf die erforderlichen Intervalle zu korrigieren. So das Ergebnis:

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

Welche für Sie sein könnte schmackhafter als das, was das Datum Betreiber bieten als Folge je nach Ihren Bedürfnissen.

Sie können auch eine kleine Abkürzung für diese mit MongoDB 2.6 über den $let Operator hinzufügen, die Sie erklären „Variablen“ für scoped Operationen erlaubt:

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

Auch fast vergaß ich für „ping, dass Ihre Werte zu erwähnen "und" link "sind eigentlich Zeichenfolgen, es sei denn, das ist ein Tippfehler. Wenn nicht, stellen Sie sicher, dass Sie sie zuerst als Zahlen konvertieren.

+0

Danke. Könnten Sie mir bitte weiter helfen? Ich habe einen Scheduler, der jede Stunde läuft, um die Daten der letzten Stunde wie folgt zu aggregieren: '{" _id ": { " customerId ": 123," day ": ISODate (" 2012-06-20: 00: 00 : 00Z ")}, " Stunden ": [{" Stunde ": ISODate (" 2012-06-20: 01: 00: 00Z ")," Pings ": 2," Links ": 3}]}' After Wenn ich die Aggregationsabfrage oben ausführe, muss ich diese zu dieser Sammlung zusammenführen/hochladen, um aggregierte Daten zu speichern. Wie kann ich das erreichen? Danke – user3756522

+0

@ user3756522 Das klingt wirklich nach einer anderen Frage und wird am besten als solche von einem neuen Beitrag gefragt, in dem du deine Absicht lieber richtig erklärst als in Kommentaren. Die Abfrage, wie in der Antwort gezeigt, aggregiert jeden Tag und jede Stunde für den Bereich, den Sie senden. Auch Ihr '$ match' sollte dabei immer die ** erste ** Stufe der Pipeline sein. Mit MongoDB 2.6 kann Ihre Gesamtausgabe ein Cursor sein, den Sie durchlaufen, um die Ergebnisse zu verarbeiten –

Verwandte Themen