2015-11-11 24 views
6

Nel mio aggregato, ogni documento nello stream avrà una data su di esso.

Ho bisogno di riassumere alcuni valori su intervalli di date ..

Ie.

{ 
    value: 3, 
    date: [SoME TIME STAMP] 
}, 
{ 
    value: 4, 
    date: [SoME TIME STAMP] 
}, 
{ 
    value: 1, 
    date: [SoME TIME STAMP] 
}, 
{ 
    value: -6, 
    date: [SoME TIME STAMP] 
} 

Desidero poter raggruppare questi documenti in base all'intervallo di date. IE: 1-7 giorni fa, 8-15 giorni fa. e 15-30 giorni fa.

db.Collection.aggregate([ 
{$match: {some matching}}, 
{$group: {What should i do here??}} 
]) 

Posso ovviamente fare 3 aggregati diversi con 3 diversi $ in date.

È possibile eseguire tutto il gruppo $ e sommare il campo "valore" in una corsa?

+0

Non penso che si possa fare tutto in una volta.Dovresti davvero andare per 3 conteggio –

+0

@MateoBarahona ovviamente puoi farlo tutto in una volta. Questo è in gran parte ciò che operatori come ['$ cond'] (https://docs.mongodb.org/v3.0/reference/operator/aggregation/cond/) sono in realtà per. –

+0

@Blakes Seven: Ho ragione, –

risposta

17

è necessario determinare in modo condizionale la chiave di raggruppamento in base a dove la data corrente cade tra la gamma. Questo è realizzato sostanzialmente mediante $cond con condtions nidificati e la variante logica di $lt:

// work out dates somehow 
var today = new Date(), 
    oneDay = (1000 * 60 * 60 * 24), 
    thirtyDays = new Date(today.valueOf() - (30 * oneDay)), 
    fifteenDays = new Date(today.valueOf() - (15 * oneDay)), 
    sevenDays = new Date(today.valueOf() - (7 * oneDay)); 

db.collection.aggregate([ 
    { "$match": { 
     "date": { "$gte": thirtyDays" } 
    }}, 
    { "$group": { 
     "_id": { 
      "$cond": [ 
       { "$lt": [ "$date", fifteenDays ] }, 
       "16-30", 
       { "$cond": [ 
        { "$lt": [ "$date", sevenDays ] }, 
        "08-15", 
        "01-07" 
       ]} 
      ] 
     }, 
     "count": { "$sum": 1 }, 
     "totalValue": { "$sum": "$value" } 
    }} 
]) 

Come $cond è un operatore ternario, la prima condizione è valutata per vedere se la condizione è vera, e quando vero il secondo argomento è restituito altrimenti il ​​terzo viene restituito quando falso. Quindi, nidificando un altro $cond nel caso di errore, si ottiene il test logico su dove cade la data, sia "meno che la data di 15 giorni" che significa che è nell'intervallo più vecchio, o "meno di 7 giorni" che significa la gamma media, o, naturalmente, è nella gamma più recente.

sto solo anteponendo i numeri qui meno di 10 con una 0 in modo che ti dà qualcosa di ordinare se si desidera, dal momento che l'uscita di "chiavi" in $group, non è di per sé ordinato.

Ma è così che si fa in una singola query. Devi solo capire quale dovrebbe essere la chiave di raggruppamento in base a dove cade la data e ad accumularsi per ogni chiave.

+0

Fantastico. Non ho ancora provato. Ma sembra molto promettente! – user1017674

+0

+1 Fantastico! Penso solo che dovresti scambiare le condizioni. Ho provato e funziona bene ... Bel lavoro Blakes! – Amir

3

Il primo passaggio sarà la creazione di oggetti data che rappresentano il tuo intervallo. Supponiamo che tu voglia eseguire la tua operazione di aggregazione per il gioco 8-15 giorni fa, questo significa che hai bisogno di due oggetti data, diciamo inizio e fine. start terrà la data un giorno fa e terminerà con la data di 8 giorni fa.La creazione di questi oggetti di data è facile come loro impostazione per il numero di giorni precedenti sottraendo n a partire dalla data in cui n è il numero di giorni fa:

var start = new Date(); 
start.setDate(start.getDate() - 8); 

var end = new Date(); 
end.setDate(end.getDate() - 15); 

o sottraendo dai millisecondi timestamp utilizzando .getTime() metodo restituisce un JavaScript standard timestamp (millisecondi dal Jan 1/1970) su cui è possibile utilizzare operazioni matematiche regolari, e reimmessa alla Data di oggetto direttamente:

var today = new Date(); 
var start = new Date(today.getTime() - 8*24*60*60*1000); 
var end = new Date(today.getTime() - 15*24*60*60*1000); 

Ora che avete gli oggetti data, è possibile poi utilizzare come $match criteri, utilizzando i $lte e $gte confronto operatori:

var pipeline = [ 
    { 
     "$match": { 
      "date": { "$lte": start, "$gte": end } 
     } 
    } 
] 

in corso l'aggregazione in questa fase vi darà tutti i documenti che hanno la data che cade nel range 8-15 giorni fa,

db.aggregate(pipeline); 

che è equivalente alla query find():

db.collection.find({ 
    "date": { "$lte": start, "$gte": end } 
}); 

Ora, alla fase successiva gasdotto, si avrebbe bisogno di creare un'operazione di aggregazione che specifica un gruppo _id di null, calcolo del valore totale e i conteggi per tutti i documenti della collezione utilizzando l'operatore $sum accumulatore :

var pipeline = [ 
    { 
     "$match": { 
      "date": { "$lte": start, "$gte": end } 
     } 
    }, 
    { 
     "$group": { 
      "_id": null, 
      "totalValues": { "$sum": "$value" }, 
      "count": { "$sum": 1 } 
     } 
    } 
] 

db.collection.aggregate(pipeline); 

Si può anche andare oltre per creare una funzione generica che restituisce il totale effettivo dall'operazione di aggregazione di cui sopra che prende in due parametri, il valore iniziale dell'intervallo di date e la fine:

var getTotalValues = function(start, end){ 
    var today = new Date(); 
    var startDate = new Date(today.getTime() - start*24*60*60*1000); 
    var endDate = new Date(today.getTime() - end*24*60*60*1000);  

    var pipeline = [ 
      { 
       "$match": { 
        "timestamp": { "$lte": startDate, "$gte": endDate } 
       } 
      }, 
      { 
       "$group": { 
        "_id": null, 
        "totalValues": { "$sum": "$value" },    
        "count": { "$sum": 1 } 
       } 
      } 
     ], 
     resultArray = db.collection.aggregate(pipeline).toArray(); 

    return resultArray[0].totalValues; 
} 

var total = getTotalValues(1, 8); 
printjson(total); // prints the total 
+0

Wow, grazie per aver scritto così tanto ... Ma stavo chiedendo se posso farlo o no in un colpo solo. Attualmente sto facendo quello che hai scritto. Grazie! – user1017674

Problemi correlati