I database a documenti, per certi versi, ricordano il modello degli oggetti, che domina gli scenari dello sviluppo software. Questo modello, infatti, consente di strutturare le informazioni in aggregazioni di dati, dette appunto documenti, dalla forte valenza descrittiva: a seconda delle problematiche da gestire avremo documenti che rappresentano utenti di un sistema, libri di una biblioteca, quotazione di titoli finanziari e quant'altro. I documenti sono disposti in strutture dati lineari dette collection o collezioni, e potranno essere collegati tra loro mediante riferimenti. Il progettista viene pertanto messo in condizione di modellare il sistema informativo creando gerarchie di informazioni congruenti con le esigenze specifiche.
Cercheremo in questa lezione di tracciare alcune linee guida per organizzare un database a documenti. In effetti, non esistono vere e proprie "procedure ufficiali", ma ciò non costituisce necessariamente uno svantaggio ed, comunque, offre maggiori margini di libertà.
Dal punto di vista pratico, utilizzeremo come strumento MongoDB, il più diffuso DBMS a documenti, che abbiamo peraltro già trattato opportunamente in una guida dedicata su HTML.it.
Modellazione del sistema informativo a piccoli passi
Come faremmo per un'applicazione basata sulla programmazione a oggetti, la progettazione inizia con l'individuazione delle possibili entità, ossia gli elementi concettuali che popoleranno il nostro sistema. Se, ad esempio, dobbiamo modellare il sistema informativo di una biblioteca, i documenti rappresenteranno libri, autori, pubblicazioni, prestiti, utenti e così via.
Altra cosa utile sarebbe, nei limiti del prevedibile, selezionare un insieme di proprietà che dovrebbero apparire in questi documenti. Queste prime attività non sono obbligatorie, in quanto Mongo DB e molte altre realtà NoSQL non richiedono una definizione a priori della struttura interna dei documenti, cosa che invece accadrebbe per le tabelle dei database relazionali o le classi di un
linguaggio ad oggetti fortemente tipizzato come Java. Ciò risulta comunque utile a livello progettuale soprattutto quando si lavora in gruppo e si vuole cercare di avere un'idea di massima sulla varietà di informazioni che si dovranno gestire.
Poichè, su MongoDB, i documenti sono rappresentati in BSON (un formato binario derivato e molto simile a JSON), utilizzeremo questo stesso formato nel seguito di questa lezione. Ogni documento, inoltre, sarà identificato da un valore univoco, detto ObjectID.
BSON permette anche che le proprietà di un documento abbiano come valore un altro oggetto BSON o un array di oggetti BSON. Ciò è molto importante in quanto conferisce "profondità" al nostro documento e pone la necessità, a questo punto, di affrontare un nuovo step nella progettazione: l'individuazione dei documenti da innestare in altri. Molto spesso, infatti, capita di memorizzare ObjectID o oggetti embedded all'interno di un documento, e dover recuperare informazioni tramite questi.
Un nodo embedded ci permette di scrivere un insieme di dati tra loro connessi all'interno di un un altro documento, come si vede di seguito:
{
"_id" : ObjectId("57bdd4a1eb426816499fb1be"),
"Titolo" : "Promessi Sposi",
"Autore" : "Alessandro Manzoni",
"Pubblicazione" : {
"Editore" : "PremiateEdizioni",
"Anno_pubblicazione" : 2012,
"Numero_pagine" : 732
}
}
Nell'esempio precedente, abbiamo incluso in un unico documento che rappresenta un libro, anche tutte le informazioni sulla sua pubblicazione fisica, raccolte all'interno di un ulteriore oggetto innestato. Questa pratica risulta comoda per motivi di efficienza, in quanto con il recupero del documento-libro avremo implicitamente il recupero delle informazioni relative alla pubblicazione. Tuttavia, ciò potrebbe comportare la presenza di oggetti innestati simili in documenti diversi, con conseguente ridondanza dei dati - il male da cui i database relazionali hanno sempre tentato di fuggire. Ciò può risultare comunque accettabile in molti casi, perciò talvolta risulta una soluzione più che valida.
A seconda del rapporto tra il documento principale e quello embedded, si potrebbe avere una molteplicità di documenti innestati simili, raggruppandoli in un array. Ripensiamo all'esempio di prima: tipicamente, un'opera verrà pubblicata in più edizioni, in anni diversi e con formati differenti. In questo caso potremo avere un elenco di pubblicazioni fisiche costituito da una lista di documenti.
{
"_id" : ObjectId("57bdd826eb426816499fb1bf"),
"Titolo" : "Promessi Sposi",
"Autore" : "Alessandro Manzoni",
"Pubblicazione" : [
{
"Editore" : "PremiateEdizioni",
"Anno_pubblicazione" : 2012,
"Numero_pagine" : 732
},
{
"Editore" : "EdizioniItalianeLibere",
"Anno_pubblicazione" : 1999,
"Numero_pagine" : 678
},
{
"Editore" : "MieEdizioni",
"Anno_pubblicazione" : 2003,
"Numero_pagine" : 820
}
]
}
In alternativa ai documenti embedded, si possono stabilire collegamenti con riferimenti a documenti resi con valori di tipo ObjectId, utilizzato da BSON.
In base alla strategia da utilizzare, cambierà la navigazione tra i nostri dati, o meglio varierà la semplicità con cui potremo recuperare direttamente ulteriori informazioni a partire da un nodo. Riprendendo gli esempi visti prima, scoprire i dettagli di pubblicazione da un documento che rappresenta un libro sarà immediato:
db.getCollection('libri').find({})
Sarà anche possibile filtrare tutti quelli che presentano determinate caratteristiche anche in un documento innestato:
db.getCollection('libri').find({"Pubblicazione.Anno_pubblicazione":1999})
L'utilizzo di ObjectId al posto di oggetti innestati è consigliabile quando si vuole mantenere l'indipendenza degli oggetti collegati, soprattutto nel caso in cui questi debbano essere aggiornati periodicamente. Con gli ObjectId, si può creare una navigabilità su più livelli. Se un documento che rappresenta un libro contiene il riferimento al documento che ne rappresenta l'autore,
"passare" da un libro al suo autore sarà immediato. Il contrario non è impossibile ma richiederebbe una ricerca in più passi: recuperare prima l'ID dell'autore, e successivamente effettuare con questo una ricerca nella collection dei libri. Per evitare un sovraccarico di lavoro, si possono in alternativa creare collegamenti bidirezionali, memorizzando anche l'ObjectID dei libri realizzati nel documento relativo all'autore.
Considerazioni finali
MongoDB è fortemente orientato all'interazione con le applicazioni, e non contempla alcune funzionalità come il controllo sui valori inseriti e la loro validazione, e la stessa navigazione attraverso gli ObjectId può essere organizzata in maniera efficiente attraverso il codice dell'applicazione che sfrutterà il database. Anche nella progettazione del database dovremo sempre considerare cosa vogliamo che sia insito nella struttura del sistema informativo, e cosa dovrà invece essere risolto a livello applicativo. Nella prossima
lezione, metteremo in pratica i concetti visti sinora.