2014-06-19 20 views
5

Sto utilizzando l'aggregazione mongodb per aggregare un insieme di dati. La mia situazione è un po 'complessa. Ho una collezione come segue:Aggregazione Mongodb per giorno, quindi Ora

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

Ora voglio aggregare i dati per un'altra raccolta come segue:

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

Come potete vedere i dati è gruppo di giorno e poi per ore. Ho seguito la query di aggregazione per raggrupparli di giorno ma come raggrupparli ulteriormente per ore? Qualche idea?

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

risposta

7

Quello che fondamentalmente vuole è un doppio raggruppamento, ma non si ottiene l'intero oggetto risalgono utilizzando il date aggregation operators, solo le parti rilevanti:

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

Il doppio $group ti dà la formato desiderato inserendo i risultati in una matrice al giorno. Documento unico nel campione, ma è fondamentalmente ottenere risultati come questo:

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

Se trovate i risultati degli operatori data per difficile da affrontare o vogliono un risultato semplificato "pass-through" per gli oggetti di data, quindi si potrebbe cast come un'epoca timestamp invece:

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

il trucco in là è quando si $subtract una data oggetto da un altro si ottiene il valore di "epoca" indietro come un risultato. In questo caso usiamo la data di inizio "epoch" per ottenere l'intero valore di timestamp e forniamo semplicemente la "data math" per correggere i tempi agli intervalli richiesti. Così il risultato:

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

che potrebbe essere più appetibile per voi di quello che gli operatori di data forniscono come risultato a seconda delle esigenze.

si può anche aggiungere un po 'di scorciatoia per questo con MongoDB 2.6 tramite l'operatore $let che permette di dichiarare "variabili" per le operazioni con ambito:

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

Inoltre ho quasi dimenticato di dire che i valori di "ping "e" link "sono in realtà stringhe a meno che non sia un errore di battitura. Ma se no, assicurati di convertirli prima come numeri.

+0

Grazie. Potresti aiutarmi ulteriormente per favore? Ho uno schedulatore che verrà eseguito ogni ora per aggregare i dati dell'ultima ora come segue: '{" _id ": { " customerId ": 123," giorno ": ISODate (" 2012-06-20: 00: 00 : 00Z ")}, " ore ": [{" ora ": ISODate (" 2012-06-20: 01: 00: 00Z ")," ping ": 2," collegamenti ": 3}]}' Dopo eseguendo query di aggregazione sopra, ho bisogno di unire/inoltrare questa raccolta per archiviare i dati aggregati. Come posso ottenerlo? Grazie – user3756522

+0

@ user3756522 Questo suona davvero come un'altra domanda ed è meglio chiederlo in quanto tale da un nuovo post in cui spieghi le tue intenzioni correttamente piuttosto che nei commenti. La query mostrata nella risposta verrà aggregata ogni giorno e ogni ora per l'intervallo che l'invio. Anche il tuo '$ match' per questo dovrebbe essere sempre il ** primo ** stadio della pipeline. Con MongoDB 2.6 l'output aggregato può essere un cursore che si itera per elaborare i risultati –

Problemi correlati