Nel panorama NoSQL, un posto di particolare rilievo è riservato ai database a grafo. Questi, infatti, si differenziano dai comuni RDBMS non tanto per il modello di concorrenza adottato, bensì dal modello di dati. Neo4j, progetto nato nel 2003, è attualmente il database a grafo più utilizzato, seguito dall’ottimo OrientDB.
In queste lezioni, guideremo il lettore all'uso di Neo4j. Vediamone quindi subito le caratteristiche principali, che andremo ad approfondire in seguito:
- modello di dati a grafo orientato con proprietà chiave-valore, supportate sia su nodi sia su relazioni. Questo modello è detto Property Graph;
- due licenze: Community ed Enterprise. La prima è open source (GPL 3): il codice è scaricabile da GitHub. La versione Enterprise ha in più alcune funzionalità riguardanti monitoraggio, backup e gestione della concorrenza;
- transazioni ACID (Atomiche, Coerenti, Isolate, Durabili). Quindi un modello di concorrenza robusto e affidabile anche per applicazioni enterprise – come per i tradizionali RBMS;
- linguaggio di query dichiarativo (simile a SQL) denominato Cypher. Vedremo molti esempi su questo linguaggio, poiché rappresenta il modo più semplice ed espressivo per interagire con il database;
- driver per interagire con i più diffusi linguaggi di programmazione;
- possibilità di essere eseguito come server stand-alone, oppure integrato all’interno di una applicazione Java o altro linguaggio JVM (embedded).
Il modello Property Graph
In Neo4j possiamo memorizzare nodi e relazioni. Inoltre, entrambi possono avere un elenco di proprietà chiave-valore. La chiave è tipicamente una stringa che rappresenta il nome della proprietà, univoco per l’elemento di appartenenza, mentre il valore può essere:
- un numero (intero o reale);
- una stringa;
- un array di numeri o di stringhe.
Per rappresentare le date, bisogna convertirle in stringa o in numeri (ad esempio il giorno giuliano), la scelta migliore dipende dal contesto di utilizzo. Per i dati binari si possono usare array di byte, anche se tipicamente si preferisce salvare i file in qualche punto raggiungibile e inserire in Neo4j una URL o un path.
Per quanto riguarda le caratteristiche dei nodi, il modello prevede che ognuno di essi:
-
abbia un ID univoco assegnato automaticamente da Neo4j al
momento della creazione; -
possa avere una o più Label (etichette) che servono a
classificare i nodi e indicizzarli. Generalmente, le Label si usano per
raggruppare entità dello stesso tipo: ad esempi, se uso Neo4j come
database di un social network, potrei avere dei nodi con Label “Utente”,
altri con label “Gruppi”, “Messaggi”, etc. Un nodo può avere più Label,
aprendo la strada ad applicazioni interessanti, implementando una sorta
di polimorfismo. I nodi con una certa Label possono essere
indicizzati su certe proprietà, per velocizzarne la ricerca (come
vedremo più avanti).
Ogni relazione invece:
-
ha necessariamente un tipo, che deve essere
specificato dall’utente al momento della creazione. I tipi sono liberi,
ad esempio posso creare tra due nodi una relazione di tipo AMICO_DI senza dover dichiarare tale tipo in precedenza; -
ha necessariamente una direzione, ossia una relazione
va da un nodo ad un altro e non è possibile creare relazioni senza
verso. È possibile però fare query su relazioni indipendentemente dalla
direzione delle relazioni, ad esempio per trovare i nodi che hanno la
relazione AMICO_DI un certo nodo, indipendentemente dal fatto
che le relazioni arrivino o partano da tale nodo.
Scenari di utilizzo
Il modello si presta a molti scenari di utilizzo. Innanzitutto, esso si presta a
modellare situazioni che hanno intrinsecamente un modello a grafo, come ad
esempio un’infrastruttura di una rete aziendale. Inoltre, grazie alla
facilità di navigazione all’interno del grafo, questo modello è adatto a casi in cui siano necessarie ricerche semantiche, ad
esempio nei sistemi di rilevazione di frodi. Segnaliamo che il sito GraphGist contiene molti esempi di grafi per Neo4j, completi di codice e interrogazioni per ricreare i
database.
Se il modello si adatta bene a questi scenari in cui le strutture dati non
sono facilmente inquadrabili in pochi schemi predefiniti, viceversa esso ha
meno utilità quando ci sono molti dati ma in schemi molto omogenei, ad
esempio un database dei movimenti di conto-corrente bancari oppure di
carico-scarico magazzino.
Esempi
Consideriamo un database di un sito di e-learning. Gli utenti potrebbero
essere rappresentati da nodi aventi la Label Utente, e le
proprietà username e password. Nella sintassi di Cypher, ciò
si scrive così:
(:Utente { username: 'htmluser@html.it', password: 'A4_2j4=' })
Come vedremo più in dettaglio in seguito, i nodi sono sempre rappresentati
da parentesi tonde. Le proprietà si scrivono invece con la stessa sintassi
di JSON: un elenco di attributi chiave:valore separati da virgola e
racchiusi da parentesi graffe. Come impostazione predefinita, Neo4j non
richiede che un nodo debba avere obbligatoriamente qualche proprietà, per
cui è perfettamente legale creare un nodo senza etichette né proprietà.
Però, come vedremo in seguito, è possibile introdurre vincoli, per cui
possiamo richiedere che tutti i nodi con etichetta Utente abbiano
una proprietà username o anche che tale proprietà sia univoca,
ossia non esistano due utenti con lo stesso username.
Per indicare che un utente è iscritto ad un corso da effettuare entro una
certa data possiamo, rappresentare l’iscrizione come relazione e il corso
come un nodo con un’altra etichetta:
(u)-[:ISCRITTO_A {scadenza: '2018-02-08'}]->(:Corso {nome: 'Corso di Neo4j'})
Abbiamo indicato la scadenza come una proprietà della relazione. Vediamo
che con il segno della freccia ->
abbiamo indicato la direzione della
relazione.
A questo punto è facile realizzare query per trovare tutti gli utenti
iscritti ad un corso, oppure gli utenti iscritti nel 2016:
MATCH (u)-[i:ISCRITTO_A]->(:Corso {nome: 'Corso di Neo4j'})
WHERE i.scadenza < '2016'
RETURN u,i
Come si può intuire, il modello di dati è molto potente perché possiamo estendere il grafo in tantissimi modi creando nuovi tipi di relazioni tra nodi, oppure possiamo navigare tra le informazioni presenti nel grafo in modo molto semplice, percorrendo le relazioni.
Nelle prossime lezioni vedremo quali sono le potenzialità del modello e che uso se ne può fare grazie a Cypher.