Introduzione a MongoDB

mongodb

Salve a tutti,
il protagonista dell'articolo di oggi è MongoDB (link), un database NoSQL molto diffuso ed usato.

Caratteristiche

Partiamo dal concetto di database NoSQL: a prima vista può sembrare che sia un antagonista del classico e canonico SQL; in realtà, NoSQL significa Not only SQL, che invece sta ad indicare un ampliamento dello stesso con nuove funzionalità e nuove prospettive di sviluppi applicativi, a partire dalla eliminazione delle Relazioni, tanto care ai più comuni RDBMS.

MongoDB è un DBMS open source con la particolarità principale di gestire i dati in Documenti (Documents), rappresentati tramite JSON e organizzati in Collezioni (Collections).
A riguardo, è importante ricordare che in informatica col termine Documento si intende un'entità generica che porta con sé informazioni e dati.

Per chi è più familiare con i database Relazionali, la tabella seguente può aiutare a capire meglio la struttura di un database MongoDB:

RDBMS MongoDB
Table Collection
Row Document
Column Field
Relationship Link & Embedded Document

Alcune caratteristiche fondamentali di MongoDB:

  • "Cade" il concetto di Schema, inteso come struttura rigida ed obbligatoria: i Documents presenti all'interno di una stessa Collection possono avere campi e contenuti diversi
  • Allo stesso modo, le Join perdono di significato: gli oggetti necessari ad un determinato Document vengono inseriti (tipo Array JSON di Documents) all'interno dei campi del Document stesso; al massimo, se proprio volete comunque creare una Collection di questi oggetti associati, nell'Array JSON inserite un elenco di ObjectId (ne parleremo più avanti) relativi a tali Documents
  • Alta scalabilità orizzontale
  • Indici su ogni attributo
  • Adatto per la gestione di Big Data grazie ad un sistema di query sui Documents molto dinamico e "deep"

Esempio di Document

Prima di tutto, il campo "_id" è un campo di default che rappresenta, ovviamente, l'identificativo univoco di tipo ObjectId di ogni Document; quando inseriamo un nuovo Document all'interno di una Collection, non è necessario indicarlo fra i campi di input, viene creato automaticamente.

Procedendo, credo che sia abbastanza chiaro che questo Document possa far parte di una Collection che possiamo chiamare "People"; da notare il campo "vehicles": il signor Mario Rossi possiede tre (notevoli) veicoli, che noi abbiamo aggiunto come elementi (ovvero, degli Embedded Documents, in quanto non fanno parte di una Collection definita) dell'oggetto JSON relativo a tale campo; tali Embedded Documents hanno delle proprietà in comune, altre invece sono esclusive per un veicolo o per l'altro: ricordate il discorso degli Schema flessibili, con Documents aventi campi e contenuti differenti? Il concetto è lo stesso, e si applica anche per gli Embedded Documents.

Il campo "children" invece è formato da soli ObjectId relativi ad altri Documents: è logico pensare che i figli di Mario Rossi facciano parte anche loro della Collection "People", quindi al posto dell'elenco dettagliato che abbiamo visto prima per i "vehicles" è sufficiente indicare gli ObjectId dei Documents interessati.

Installazione

Per installarlo, scaricate il pacchetto da qui.
Io sono un felice (?) possessore di Windows, quindi riporto le istruzioni relative; per gli altri Sistemi Operativi comunque è sufficiente seguire le guide ufficiali presenti qui.

Windows

Doppio click sul file .msi appena scaricato: il wizard di installazione è molto semplice, e alla fine MongoDB verrà installato nel path di default che è il seguente (3.4 è la versione di MongoDB che sto usando in questo momento - Aprile 2017):
C:\Program Files\MongoDB\Server\3.4\
Se volete indicare un altro percorso, nel wizard è possibile selezione una installazione "Custom" dove potremo definire un path diverso:
wizard_custom
wizard_custom_path

Completata l'installazione, dobbiamo creare la directory sulla nostra macchina che conterrà il db; il path di default è C:\data\db, quindi se questa cartella non è già presente dovete crearla o attraverso la classica interfaccia grafica Windows Explorer o con i seguenti comandi da digitare nel prompt:
C:\>md data
C:\>md data\db

Nel caso invece vogliate usare un altro percorso, create le directory data e data\db nel path desiderato.

A questo punto siamo pronti per far partire il servizio mongod: con il Command Prompt, posizioniamoci nel percorso di installazione di MongoDB
cd "C:\Program Files\MongoDB\Server\3.4\"
poi spostiamoci nella directory bin
cd bin
e facciamo partire il servizio mongod
mongod.exe
Attenzione: se volete utilizzare un altro percorso per il db, dovete passarlo al comando usando il flag --dbpath
mongod.exe --dbpath "C:\altro\percorso\data"
mongod

Benissimo! A questo punto il nostro servizio è partito correttamente, quindi non ci resta che accedere alla cosiddetta shell di MongoDB!
Apriamo una nuova finestra del Command Prompt (quella del servizio deve rimanere aperta) e spostiamoci di nuovo nella directory bin dell'installazione:
cd "C:\Program Files\MongoDB\Server\3.4\bin"
Digitiamo mongo e, se abbiamo fatto tutto per bene, avremo finalmente la nostra shell connessa al db (localhost:27017)!
mongo

...e adesso inizia il divertimento!

Comandi principali

Ovviamente il nostro db inizialmente è vuoto: se digitiamo
db
ci viene restituito il database che stiamo utilizzando in questo momento (nello specifico, quello di default: test)
db
Se però proviamo a fare il comando
show dbs
che ci restituisce l'elenco dei database presenti, notiamo che 'test' non è presente:
show_dbs_empty
Questo perché è ancora vuoto, non ha Documents nè soprattutto Collections al suo interno.
Risolviamo subito creando la nostra Collection 'myCollection':
db.createCollection('myCollection')
createCollection
Come potete vedere, adesso il nostro db 'test' è presente nell'elenco.
Inseriamo un Document all'interno di myCollection (SHIFT+Invio per andare a capo nella shell):

insert
Abbiamo anche ottenuto un feedback di corretto inserimento, che ci indica il numero di righe inserite "nInserted" : 1.

Con il comando find() andiamo ad interrogare la nostra myCollection per verificare l'inserimento:
db.myCollection.find()
find
Se non viene specificato nel comando di inserimento, il campo "_id" viene automaticamente creato e valorizzato con un ObjectId("...").
Aggiungendo il suffisso .pretty(), il risultato della query ci viene restituito in un formato un po' più carino da leggere:
pretty

Vogliamo cambiare db? 'test' non ci piace più? Nessun problema:
use my_db
Nel caso 'my_db' non esista, viene creato all'istante e diventa il tuo db di riferimento
user
Lo stesso dicasi per le Collections: se non esistono, vengono create nel momento in cui vuoi fare un'operazione (tipo di inserimento) su tale "futura" Collection; nell'esempio sotto, abbiamo inserito un Document all'interno della Collection 'allafine' che però ancora non avevamo creato nel nostro db 'my_db':
insert_new

Cancellazione Document:
db.COLLECTION_NAME.remove(FILTER)
remove
Abbiamo appena cancellato il Document creato poco fa; spiegazione veloce: sono stati cancellati tutti i Documents che rispondevano alla query di ricerca 'allafine':'mongodb'; se aggiungiamo il flag justOne settato a 1 nel metodo remove(), forziamo il comando a cancellare solo 1 Document fra quelli che rispondono alla query:
db.COLLECTION_NAME.remove(FILTER,1)
Ovviamente, se non viene indicata nessuna query di ricerca, remove() elimina tutti i Documents della Collection (è l'equivalente dell'SQL TRUNCATE)

Cancellazione Collection:
db.COLLECTION_NAME.drop()

Cancellazione DB:
db.dropDatabase()
In questo modo abbiamo appena cancellato il db di riferimento in cui stavamo lavorando.

Il comando .find() e la ricerca dei Documents

Abbiamo già visto velocemente un primo esempio per cercare i Documents, a questo punto però vogliamo andare più nel dettaglio e specificare i vari criteri di ricerca; la tabella seguente riassume i casi più comuni:

Operazione Sintassi Esempio
= {key:value} oppure {key:{$eq:value}} db.my_col.find({'value':'test'}) oppure ({'value':{$eq:'test'}})
!= {key:{$ne:value}} db.my_col.find({'value':{$ne:'test'}})
< {key:{$lt:value}} db.my_col.find({'years':{$lt:30}})
<= {key:{$lte:value}} db.my_col.find({'years':{$lte:30}})
> {key:{$gt:value}} db.my_col.find({'years':{$gt:30}})
>= {key:{$gte:value}} db.my_col.find({'years':{$gte:30}})
IN {key:{$in:[values]}} db.my_col.find({'years':{$in:[2017,2018]}})
NOT IN {key:{$nin:[values]}} db.my_col.find({'years':{$nin:[2017,2018]}})

E gli AND e gli OR presenti nei RDBMS?
Se vogliamo mettere in AND dei parametri, basta elencarli separati dalla virgola:

C'è anche una sistassi un po' più articolata, ma in presenza di query complesse può dare un po' più di ordine:

Per quanto riguarda invece l'OR, la sintassi è simile:

E se volessi fare qualcosa del tipo ((A AND B) OR (C AND D))? La struttura sarebbe questa:

Altro punto: nei RDBMS potevamo scegliere quali colonne farci restituire (SELECT col1, col2 FROM ...); in MongoDB la sintassi è la seguente:

Oltre ai quelli indicati, il comando .find() ritorna sempre il campo _id; se non vogliamo che venga restituito, dobbiamo indicarlo esplicitamente impostandolo a 0:

Inserimento e aggiornamento dei Documents

Per inserire un Document, abbiamo già visto il metodo insert():

Per gli inserimenti multipli, basta passare un array di elementi:

Attenzione: dalla versione 3.2 sono presenti anche altri due metodi equivalenti ai due casi appena descritti:

Anche se non è necessario scriverlo, possiamo anche indicare esplicitamente l'_id da associare all'elemento inserito, invece di farlo generare automaticamente:

Questo però può comportare un problema: nel caso sia già presente un Document con lo stesso identico _id, l'inserimento va in errore (duplicated key):
insert_error

Esiste anche il metodo save() che ha la stessa sintassi di insert() ma un comportamento leggermente diverso: infatti, se indichiamo esplicitamente l'_id e questi è già presente, sovrascrive il Document relativo; altrimenti lo inserisce nella Collection.

Per quanto riguarda l'aggiornamento di un Document, esiste il comando update({FILTER},{$set:{NEW_VALUES}}):

update
Nell'esempio, ho prima mostrato la situazione di partenza con un find(), poi ho eseguito l'update mettendo come criterio di ricerca {'delpalo':'mongodb'}, e poi ho di nuovo ricercato i Documents; notate niente di strano?
E' stata modificata solo una riga (la prima), nonostante ci siano due Documents con {'delpalo':'mongodb'}: di base, MongoDB aggiorna solo una riga per volta; per effettuare un aggiornamento multiplo, si deve impostare il parametro multi a true (che ovviamente di default è false):

Se non inseriamo il termine $set nel comando di update (update({FILTER},{NEW_VALUES})), tutto il contenuto del Document viene sovrascritto con {NEW_VALUES}.
update_no_set
Normalmente, se il criterio di ricerca non corrisponde a nessun Document, l'update non esegue nessun aggiornamento; se invece viene impostato il parametro upsert a true, allora viene inserito un nuovo Document con i valori presenti nel comando:

Per tutti gli altri comandi e particolarità, questo è il link al manuale di MongoDB.

Desktop client: Robomongo

Per quanto uno possa avere simpatia per la shell di MongoDB, l'utilizzo di un client con un'interfaccia grafica più "accattivante" ha sicuramente molti aspetti positivi.
Fra tutti i vari software presenti, io utilizzo e consiglio Robomongo (Windows, OSX, Linux).
Una volta installato, connettetevi al vostro db locale (vi ricordo: localhost:27017) cliccando sul link "Create" dentro il pannello "MongoDB Connections":
robomongo_connection
Una volta connessi, potete navigare tranquillamente fra i vostri DB e le vostre Collections nel pannello di sinistra, mentre nella parte centrale potete visualizzare tutti i vostri Documents ed effettuare i vostri comandi nella shell in alto (che si espande verso il basso man mano che scrivete)

Fra le varie funzionalità, molto utile per esempio è l'autocompletamento dei comandi, oppure la possibilità di visualizzare i risultati sotto forma di "albero", tabella SQL-like o testo (tipo shell).

Pro e Contro di MongoDB

Riassumendo, quali sono i vantaggi e gli svantaggi di MongoDB?

Pro

  • Flessibilità: l'essere "Schema-less" permette di poter svincolare la struttura della tabella (Collection) dall'effettiva istanza del Document; se successivamente alla creazione iniziale hai bisogno di utilizzare campi in più e/o di diverso tipo, non hai bisogno di aggiornare tutta la struttura della Collection e dei Documents già presenti
  • Velocità: MongoDB è molto performante (con le dovute eccezioni, sia chiaro) nella gestione dei Documents, anche (e soprattutto) in presenza di grandi quantità di dati
  • Scalabilità orizzontale: MongoDB (e in generale i vari database NoSQL) utilizzano il meccanismo di Sharding che distribuisce i dati su più server in modo automatico senza che l'applicazione sappia la composizione del cluster; se abbiamo bisogno di migliorare le performance, basta aggiungere delle macchine in più (MongoDB "sfrutta" la RAM dei server)
  • Replica: il database viene replicato su tutti i server, con una gestione "Primary-Secondary(ies)"; in caso di fail del "Primary", uno dei server "Secondaries" prende il suo posto (c'è un meccanismo di "negoziazione" per decidere chi), dando quindi poi la possibilità (e il tempo) di ripristinare l'ex-"Primary" che andrà a far parte della batteria dei "Secondaries"
  • Document oriented: il protagonista del database è il Document (il dato) e non la struttura e le Relationships che ci sono fra le varie tabelle

Contro

  • No ACID: le transazioni non sono supportate, e questo comporta un limite in situazioni in cui è necessario effettuare più operazioni "atomiche" sul database prima di poter definire conclusa una transazione (e eventualmente fare rollback); scenari delicati come movimenti bancari o compra-vendita su e-commerce non possono per esempio prescindere da una gestione di questo tipo
  • No JOIN: come già accennato, è la natura stessa dei NoSQL che non permette (o almeno le scoraggia) JOIN fra più Collection;
    non vi è la possibilità (se non con accrocchi e complicazioni strane) di creare query che vadano ad interrogare contemporaneamente più Collections
  • No Foreign Keys: tale meccanismo utile per rafforzare la consistenza dei dati non è presente
  • Data size maggiore: le informazioni sono salvate come coppie "chiave"-"valore" (ovvero il formato JSON), occupando quindi più spazio rispetto ad una corrispondente struttura RDBMS
  • Tecnologia "giovane": banalmente, i database relazionali sono lo standard da ormai 40 e più anni, mentre MongoDB e i NoSQL in generale sono database nuovi, nati da poco e quindi ancora non possono garantire la stessa affidabilità di un caro e vecchio RDBMS (ma diamo tempo al tempo... 😉 )

Con questo ho concluso.
Sperando di avervi incuriosito verso questa nuova tecnologia, vi invito ad approfondirla ed a sfruttare i commenti sottostanti per domande ed eventuali correzioni su ciò che ho erroneamente scritto.

Che il MongoDB sia con voi!

Paolo Gerini
Umpa Lumpa della scienza, studente a tempo perso e ingegnere solo quando capita. Appassionato di sport e di musica (suona il pianoforte per far colpo sulle tipe), sembra anche che ogni tanto lavori ma pare più una leggenda.
Introduzione a MongoDB ultima modifica: 2017-04-18T11:08:14+00:00 da Paolo Gerini


Advertisment ad adsense adlogger