www.errorediridondanzaciclicodotcom

  • Home
  • Mongo aggregate
  • BACK P.P.
  • Il metodo $aggregate in MongoDB

    Quello che segue è la spiegazione di come aggregare e filtrare dei dati. Questo esercizio l'ho fatto su una collezione che raccoglie i litri consumati per ogni doccia. Ogni giorno, quindi ci posso essere più docce.
    Il tutto gira su un Raspberry Pi 4 con OS a 64 bit.
    Questi sono i dati prima di essere aggregati (per semplicità di lettura ho nascosto l'id):

    Ogni ora (intervallo in cui inserisco i dati) si trovano i litri registrati. Il mio obiettivo è quello invece di avere solo un dato per ogni consumo, in ordine di data.

    Ecco qui sotto il comando da impartire sulla shell di Mongo:

    > db."mycollection"".aggregate

    ([

    {$match: {"sensor": "LTLASTSHOWER"}},

    {$project: {

    "month": {"$month": "$timestamp"},
    "day": {"$dayOfMonth": "$timestamp"},
    "value": "$value"}},

    {$group: {

    _id: {

    month: "$month",
    day: "$day",
    value: "$value"}}},

    {$sort: {

    "_id.month": -1,
    "_id.day": -1}}

    ])



    La funzione aggregate può essere composta di più sezioni. Nel mio caso di esempio la prima serve ad escludere i dati che non mi interessano per questa query ed isola, quindi, i documenti che sono relativi ai LTLASTSHOWER, cioè i litri dell'ultima doccia.
    In Mongo nelle operazioni CRUD viene rispettata la sequenza che si vede nella shell: la pipeline opera in sequenza su ciò che viene elaborato nello stage precedente.
    Lo stage successivo che si vede qui sotto, $project, prenderà i campi (e farà delle operazioni su di essi), che arrivano dallo stage precedente.

    Il metodo $project prenderà, quindi, i dati isolati dal metodo $match (i documenti relativi ai litri utilizzati) e passerà allo stage successivo ($group) i campi specificati, che possono essere sia campi esistenti (tali e quali) che nuovi campi ottenuti per elaborazione.
    Vediamo il mio caso: ho creato un nuovo campo, "month" che estrae dal mio campo esistente, "$timestamp", il mese (funzione $month:). Lo stesso lo farà per il giorno. Infine includo il campo value tale e quale, per questo motivo si vede come una classica notazione JSON:

    "value": "$value"

    Per la visualizzazione corretta ho aggiunto anche un $sort. Notare i campi da utilizzare per il sort che sono "month" e "day", vedremo poi la differenza più avanti con lo stage $group.

    A questo punto aggiungo un nuovo stage, $group, per raggruppare i dati ed evitare duplicati inutili ad un'analisi statistica (ricordo che il mio è un esempio banale e banalizzato ma l'uso in indagini importanti è molto più ampio).

    Nell'immagine sopra si vede il metodo $group applicato da solo, senza $project, quindi su tutti i risultati ottenuti da $match. Il metodo così applicato raggruppa per le "ISODate" (il mio campo "timestamp") e, successivamente, raggruppa per il "value" dei litri. Si vede che ogni ISODate è in realtà ogni ora; raggruppare poi per il valore dei litri all'interno di ogni ora, in questo caso, non ha senso. Infatti il risultato non è quello che volevo. Questo è il motivo per il quale utilizzo prima il metodo $project.
    Notare il sort in questo caso. Quando si usa $group, si crea un nuovo "id", che nel mio esempio include due campi. Nella notazione di $sort bisogna utilizzare il costrutto "_id.timestamp" perchè è necessario riferirsi al campo "timestamp" del nuovo "id". Ciò differisce da quanto più sopra visto e, in generale, per i sort su liste non raggruppate.

    Unendo tutti gli stage, ottengo quanto sotto:

    Riassumendo:

    1) con $match isolo i campi su cui lavorare
    2) con $project elaboro alcuni campi ("month" e "day") e li presento, insieme ad altri   ($value), per lo stage successivo.
    3) con $group raggruppo i dati per mese, poi per giorno ed infine per valore.
    4) con $sort riordino i dati per visualizzarli in ordine decrescente di data.

    Questo è un esempio di utilizzo di $aggregate.