I database NoSQL rappresentano ormai una valida alternativa ai database relazionali. Capita sempre più spesso che le aziende scelgano
soluzioni NoSQL come base dei propri progetti, sia per nuovi archivi che come approdo di un processo di migrazione.
Questo articolo mette a confronto i fondamenti della teoria relazionale con quelli dei DBMS NoSQL “a documenti”, anche tramite un semplice esempio che
mostra gli accorgimenti necessari per convertire una database relazionale nella corrispondente versione NoSQL.
Quanto verrà illustrato può essere sperimentato, per la parte relazionale, su MySQL o altro DBMS di questa categoria, mentre per il NoSQL a documenti, la soluzione più diffusa al mondo è MongoDB.
DBMS relazionali e non: elementi a confronto
La conoscenza degli elementi di base di un database relazionale fa parte della cultura tecnica di buona parte degli sviluppatori ed
amministratori di sistema. Riepilogando brevemente, tutto ruota attorno al concetto di tabella. Ne esisterà una per ogni tipo di
informazione da trattare, ed ognuna sarà costituita da colonne, una per ogni aspetto dei dati.
Una tabella dovrebbe avere una o più colonne che svolgono il ruolo di chiave primaria, una sorta di indice che permette di riconoscere
univocamente quella riga rispetto a tutte le altre. Tra le tabelle di un database relazionale, inoltre, possono esistere alcune relazioni.
Una riga di una tabella A può fare riferimento ad un’altra riga di un'altra tabella B, e ciò può essere espresso inserendo la chiave primaria della riga di
B tra i dati di quella di B.
Caratteristiche di SQL
Il linguaggio fondamentale usato per impartire comandi ad un DBMS relazionale è SQL ed una delle operazioni più comuni nonché più onerose
in termini di risorse è il cosiddetto JOIN, un incrocio di tabelle in relazione tra loro che permette di offrire un set di dati unico con
le informazioni complete.
Alla base di un database relazionale, c'è la progettazione della struttura interna, fatta di tabelle e relazioni. Questo lavoro concettuale iniziale
condizionerà lo svolgimento di ogni operazione sui DB, dall'inserimento dei dati all'interrogazione, alla definizione dei JOIN.
Caratteristiche di NoSQL
La strutturazione rigida dei contenuti, tipica dei database relazionali fin qui descritti, è un elemento assente nei database NoSQL, e
proprio tale assenza è uno degli aspetti che maggiormente ne hanno permesso il successo.
Le informazioni non troveranno più posto in righe elencate in tabelle, ma in oggetti completamente diversi e non necessariamente strutturati, come ad
esempio documenti archiviati in collezioni. Su MongoDB, un database NoSQL basato sui documenti, il formato del documento
è una delle caratteristiche più rilevanti. Si tratta di BSON (Binary JSON), una variante di JSON che
include diversi tipi di dati.
Se, ad esempio, dovessimo inserire le informazioni di una persona (nome, cognome, età) in un database relazionale, creeremmo una tabella con tre colonne.
In JSON, gli stessi dati verrebbero resi in un formato testuale come il seguente:
{"cognome":"Rossi","nome":"Carlo","eta":25}
Più oggetti JSON racchiusi tra parentesi quadre costituiscono invece una lista di oggetti.
Un oggetto JSON, complesso e articolato come vogliamo, rappresenta un documento da inserire nel database. Non esisteranno più tabelle, ma collezioni di documenti.
La chiave primaria del documento è un campo denominato _id che, se non viene fornito in fase di inserimento, verrà aggiunto
automaticamente dal DBMS.
Nei DBMS NoSQL a documenti, è significativa l'assenza delle relazioni. I meccanismi con cui vengono collegate le informazioni sono infatti
due:
- Embedding: significa annidare un oggetto JSON all'interno di un altro. Questa tecnica sostituisce molto spesso le relazioni 1-a-1 e
1-a-molti. È tuttavia sconsigliabile utilizzarla quando i documenti (quello annidato e quello che lo contiene) crescono di dimensione in maniera
sproporzionata tra loro, oppure se la frequenza di accesso ad uno dei due è molto minore di quella dell'altro; - Referencing: somiglia molto alle relazioni dei RDBMS, e consiste nel fare in modo che un documento contenga, tra i suoi dati, l'id di un
altro documento. Molto utile per realizzare strutture complesse, relazioni molti-a-molti oppure casistiche non rientranti tra quelle consigliate per
l'embedding al punto precedente.
Un caso di studio
Vediamo quanto illustrato finora con un esempio. Immaginiamo un caso non troppo complesso in cui ci siano poche tipologie di informazioni, ma che offra
ugualmente uno spunto di riflessione sul modo in cui esse si relazionano.
Prendiamo il caso di un blog in cui vengono raccolti dei post.
Le regole sono queste: ogni post può essere commentato più volte e viene classificato con dei tag, delle etichette che specificano gli argomenti salienti.
Ogni post può essere associato a più tag e, viceversa, ogni tag può essere usato per più post.
Affinché il database venga progettato in maniera coerente con la programmazione che vi verrà applicata, dobbiamo tentare di schematizzare sin da subito le
modalità di accesso ai dati. In progetti reali, di dimensioni considerevoli, questo non è sempre facile, ma nel nostro esempio possiamo assumere che:
- i commenti verranno letti e modificati solo in congiunzione con un post;
- quando si carica un post dal database questo viene raccolto con tutta la sua lista di commenti;
- la ricerca dei post e dei tag deve essere indipendente. Può capitare di leggere un tag e recuperare i post ad esso collegati. Viceversa, può essere
necessario leggere un post e recuperare tutti i tag relativi.
In un database relazionale, una classica rappresentazione del problema potrebbe essere la seguente:
I rettangoli in figura rappresentano le tabelle del database.
Le relazioni coinvolte saranno:
- una tra Post e Commenti, di tipo uno-a-molti in quanto ad un post appartengono più commenti;
- una tra Post e Tag, di tipo molti-a-molti in quanto ad un post possono corrispondere più tag ed ad un tag possono corrispondere più
post.
Il seguente script SQL realizza la struttura. Si noti che viene indicata una tabella in più, Rel_Tag_Post, che realizza il collegamento tra i tag
ed i post:
CREATE DATABASE IF NOT EXISTS Blog;
USE Blog;
CREATE TABLE Post
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
titolo VARCHAR(50),
testo TEXT
);
CREATE TABLE Commenti
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
testo TEXT,
id_post INT
);
CREATE TABLE Tag
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(50)
);
CREATE TABLE Rel_Tag_Post
(
id_post INT,
id_tag INT
);
Vista la duttilità dei JOIN, una struttura relazionale simile si presta ad ogni tipo di ricerca. Realizzati gli opportuni collegamenti tra tabelle,
un’opportuna operazione di JOIN potrà allineare tutti i dati collegati.
Convertendo una struttura simile in un database MongoDB
si hanno a disposizione più possibilità.
La conversione della tabella in NoSQL offre i seguenti spunti:
- i Post saranno dei documenti. La collection che li conterrà sarà la più manipolata del sistema;
- la relazione con i commenti può essere resa con l'embedding. Uno dei motivi che ispirerebbe questo è che si tratta di una relazione uno-a-molti. Inoltre,
abbiamo assunto che i commenti abbiano senso solo se abbinati ad un post. Se ogni post contiene una collezione di commenti questi verranno recuperati tutti
al momento del caricamento del post; - la relazione molti-a-molti tra Post e Tag si offre a diverse soluzioni. Una a cui si potrebbe pensare li vede archiviati in collection
differenti collegando i singoli oggetti tramite referencing. Un tag potrebbe contenere tra i propri dati l'elenco degli id dei post relativi mentre ogni
post vedrebbe al suo interno l'elenco dei tag associati.
Non ha senso discutere di una struttura del database NoSQL, in quanto i dati verranno inseriti direttamente.
NoSQL JSON database: inserimento documenti
I documenti dovranno essere forniti in formato JSON.
Si può interagire con un server MongoDB tramite il client da riga di comando, denominato mongo. Non scenderemo nei dettagli dell’utilizzo
di MongoDB; ma è bene spiegare che le operazioni fondamentali per inserimento e recupero dei dati sono, rispettivamente, insert e find. Si potrà scegliere il nome della collection in cui archiviare i documenti, ed utilizzarla per invocare i comandi. Potremmo, ad
esempio, inserire un post con:
[code]db.post.insert( documento JSON )[/code]
Nel precedente comando, il documento JSON potrebbe essere così costituito:
{
"titolo": "10 modi per cucinare la trota",
"autore": "Matteo Bianchi",
"commenti": [...],
"elenco_tag": [...]
}
Come si vede, senza aver dettato una struttura a priori, un documento JSON contiene in sé tutte le sue informazioni.
Alcuni parametri sono elementi semplici come, titolo e autore, valorizzati solo con stringhe. Altri sono composti da liste di elementi – commenti ed elenco_tag – di cui si è omesso il contenuto.
Secondo le considerazioni fatte sopra, il parametro commenti conterrebbe oggetti JSON embedded, riportanti a loro volta, ad esempio, testo ed autore di un
commento.
Il parametro elenco_tag potrebbe essere costituito invece da una lista di id di oggetti tag per implementare il cosiddetto referencing.