Come usare il metodo Aggregate per analizzare i dati su MongoDB

Aggregate su MongoDB

Il metodo Aggregate di MongoDB permette di definire una vasta serie di operazioni per effettuare tutte le principali operazioni di trasformazione e analisi dei dati. Possiamo definire infatti una pipeline di operazioni, dove ogni stage ha un compito ben specifico. In questo articolo vedremo le funzionalità principali dell’Aggregate secondo la sintassi della Mongo Shell, utilizzabile ad esempio all’interno di MongoDB Compass.

 

La sintassi di base di Aggregate

Iniziamo selezionando il  database da interrogare tramite l’istruzione

use nome_database

La sintassi di base per scrivere una query su MongoDB con l’aggregate è

db.<nome collezione>.aggregate(
   [{operazione 1},
    {operazione 2},
    .....
    {operazione n}]);

dove con i vari operatori (stage) possiamo codificare operazioni di filtro, raggruppamento, formule matematiche, ordinamenti, eccetera. Nei prossimi paragrafi introdurremo uno a uno gli operatori utilizzati più frequentemente, a questo link potete trovare la lista completa https://www.mongodb.com/docs/manual/reference/operator/aggregation-pipeline/.

Prima di passare a descrivere gli operatori vi ricordo di nuovo che vi presenterò la sintassi NoSQL della Mongo Shell, fate attenzione perché essa può essere più o meno distante da quella degli specifici driver di Java, Python, Node, eccetera.

 

Operatore $group

Il mio operatore preferito è il $group che permette di implementare facilmente operazioni di raggruppamento e aggregazioni simili alla Group By dell’SQL. La sintassi di $group è abbastanza semplice, occorre indicare:

  1. una chiave obbligatoria di nome _id per specificare le dimensioni di analisi su cui vogliamo raggruppare i nostri dati;
  2. altre chiavi contenenti informazioni aggregate come somme e medie di numeri, concatenazione di stringe, eccetera.

Se ad esempio dalla collezione delle fatture vogliamo calcolare la somma degli importi per ogni cliente, potremmo scrivere

db.fatture.aggregate(
   [{$group:{
     "_id":"$id_cliente",
     "importo_totale":{$sum:"$importo"}
           }
     }]);

e otterremo un risultato come questo in figura

output restituito dall'operatore group del metodo aggregate di mongodb

Per avere più flessibilità nei raggruppamenti possiamo valorizzare la chiave “_id” con un sotto-documento. Ad esempio potremmo scrivere:

"_id":{"cliente":"$id_cliente",
       "tipologia":"$tipologia"}

 

Operatore $match

Con l’operatore $match possiamo eseguire dei filtri per visualizzare soltanto i documenti di una collezione che rispettano determinate condizioni. A differenza dell’SQL, non abbiamo due clausole distinte come la WHERE e l’HAVING in base all’ordine con cui effettuare il filtro rispetto al raggruppamento. Su MongoDB sarà la posizione del $match all’interno dell’aggregate a guidare la tipologia di filtro effettuata.

Vediamo un esempio che poi commenteremo insieme.

db.fatture.aggregate([
   {$match:{"tipologia":"A"}
   },
   {$group:{"_id":"$id_cliente",
            "importo_medio":{$avg:"$importo"}
           }
    },
   {$match:{"importo_medio":{$gt:50}}
    }
    ]);

Analizziamo la query:

  • con il primo $match limitiamo la nostra analisi alle fatture di tipologia “A”, analogamente a quello che faremo con una WHERE;
  • con il $group raggruppiamo i dati per id_cliente e calcoliamo l’importo medio delle fatture;
  • con l’ultimo $match effettuiamo un ulteriore filtro sui dati già raggruppati, similmente al compito della clausola HAVING nel linguaggio SQL.

Nello specifico, il $match può essere implementato con le usuali regole del metodo find.

 

Operatore $lookup

Tramite il $lookup possiamo combinare i dati appartenenti a più collezioni, effettuando un’operazione che ricorda la JOIN del linguaggio SQL, seppur con le dovute differenze. Le regole da seguire per scrivere una $lookup sono un po’ più rigide degli altri operatori, in particolare serve configurare le seguenti chiavi:

  • from: per indicare la collezione da combinare con quella specificata all’inizio della query con l’aggregate
  • localField e foreignField per indicare i nomi delle chiavi utilizzate per combinare le collezioni. In particolare:
    • localField fa riferimento alla collezione definita all’inizio della query (fuori della $lookup)
    • foreignField fa riferimento alla collezione specificata nella chiave from
  • as: per indicare il nome della chiave che conterrà il risultato della $lookup

Ad esempio scriveremo:

db.clienti.aggregate([
   {$lookup:{from:"fatture",
	     localField:"id_cliente", 
	     foreignField:"id_cliente", 
	     as: "fatture" }
    }
    ]);

In questo caso specifico il localField fa riferimento al nome della chiave della collezioni clienti, il foreignField a quello della collezione fatture, attenzione a non fare confusione!

Con questa query avremo in output tutti i documenti della collezione clienti con in aggiunta la chiave fatture (cioè il nome specificato nell’as) di tipo array, dove troverò i vari sotto-documenti provenienti dalla collezione fatture separati da una virgola. Se un cliente non avesse fatture associate, allora sarà presente un array vuoto, come nell’immagine in basso.

array vuoto generato da una lookup

 

Operatore $addFields

Con $addFields possiamo calcolare varie funzioni, il cui output potrà essere utilizzato negli step successivi dell’operatore aggregateSupponiamo ad esempio di voler raggruppare le fatture per anno: allora potremmo preliminarmente calcolare una nuova chiave nello step addFields applicando la funzione $year. In alcuni casi può essere particolarmente utile utilizzare la funzione $cond per implementare condizioni simili a quelle codificate dall’operatore CASE WHEN di SQL. Analizziamo la query seguente:

db.clienti.aggregate([
   {$addFields:{
      "test_data":{
          $cond: { if: {$eq: ["$data_pagamento", 
                              "$data_fattura"]}, 
                   then: 1, 
                   else: 0 
                 }
                  }
               }
    },
    {$match:{"test_data":1}}
]);

Abbiamo utilizzato prima $addFields per valorizzare una chiave test_data con il numero 1 quando il contenuto delle chiavi data_pagamento e data_fattura è uguale. In seguito utilizzeremo un $match per controllare per quali documenti questo accada. Facciamo attenzione però al fatto che la $cond scritta in questo modo restituisce il numero 1 anche quando le due chiavi sono entrambe assenti o entrambe null (ma restituirà zero se una è essente e l’altra è null).

 

Continua a imparare

  • A questo link trovi una serie di esercizi svolti su MongoDB
  • Qui trovi la scheda del mio video corso sull’analisi dei dati su MongoDB
Scroll to Top
Torna su