Il tipo più semplice di indice supportato nativamente da Neo4j è quello su singola proprietà, single-property index, con cui viene indicizzato il valore di una certa proprietà per tutti i nodi aventi una certa Label. Corrisponde grossomodo all'indice su singolo campo di una certa tabella di un database relazionale.
Prima di vederlo in azione, ricapitoliamo il funzionamento di un indice. Analogamente agli altri database, un indice consiste fondamentalmente in un elenco ordinato di valori di una (o più) proprietà, ognuno dei quali è collegato ad una lista di riferimenti ai nodi. Questo significa che ogni volta che facciamo una ricerca filtrando su quella proprietà, il database in realtà cerca il valore nell'indice e, essendo l'indice già ordinato, lo trova molto velocemente. Inoltre se, al termine di una query, ordiniamo i risultati in base al valore della proprietà indicizzata, il database non confronterà tutti i risultati dei dati trovati, ma semplicemente li ordinerà in base all'ordine già indicato nell'indice; ciò significa anche che quando creiamo, modifichiamo o eliminiamo un nodo avente una Label indicizzata, l'indice verrà aggiornato. Di conseguenza, l'effetto dell'indicizzazione è di velocizzare drasticamente le ricerche, al prezzo di ridurre, seppur in modo contenuto, le performance delle scritture. Quest'ultimo degrado ovviamente è più evidente in caso di scritture massive di molti dati. Ci sono varie contromisure che si possono adottare in questi casi. Nella maggioranza delle applicazioni, è noto, le letture sono molto più frequenti delle scritture, ma il giusto compromesso per avere delle buone performance dipende fortemente dal tipo di applicazione che stiamo sviluppando.
Indice semplice
Il comando per creare un indice è CREATE INDEX
. Ad esempio:
CREATE INDEX ON :User(name)
Questo esempio crea un indice sulla proprietà name per tutti i nodi con la Label User. Neo4j crea quindi un indice che per ogni valore del nome del'utente punta ad una lista di nodi proprio con quella proprietà.
Se quindi facciamo una query come questa:
MATCH (u:User)
WHERE u.name ENDS WITH "@html.it"
RETURN u
ORDER BY u.name
l'indice viene utilizzato in due modi: per cercare gli utenti aventi il nome con il suffisso specificato, e poi per ordinare i risultati. Attualmente Neo4j supporta l'utilizzo degli indici per questi predicati:
=
(uguaglianza): per cercare un valore esatto>
e<
(confronto): per cercare valori in certi intervalliIN
: per cercare valori di un certo insiemeEXISTS
: per cercare i nodi che hanno la proprietà valorizzataSTARTS WITH
eENDS WITH
: per le proprietà di tipo testuale, vengono ricercati i valori che iniziano o finiscono con un certo testo
Per eliminare un indice la sintassi è simile, con la differenza che si usa la parola chiave DROP
:
DROP INDEX ON :User(name)
Indici composti
Neo4j, dalla versione 3.2, permette di creare anche indici composti da più di una proprietà. Questo tipo di indice è utile quando una proprietà è subordinata ad un'altra per le ricerche e gli ordinamenti (tipicamente, il cognome e il nome di una persona, oppure anno, mese e giorno di una data):
CREATE INDEX ON :CheckIn(year, month, day)
Questo indice verrà usato in query del tipo:
MATCH (c:CheckIn) WHERE year = 2017
MATCH (c:CheckIn) WHERE year = 2017 and month = 5 and day = 18
Al momento (versione 3.3) per questo tipo di indice è supportato solo l'operatore di uguaglianza.
Indici full-text
Analogamente ad altri database, Neo4j permette di creare indici cosìddetti "full text" per fare ricerche veloci non solo basate sul valore esatto delle stringhe testuali, ma anche in base a porzioni di testi. Per esempio, supponiamo di effettuare una ricerca usando le espressioni regolari:
MATCH (u:User)
WHERE u.name =~ ".*html\\.[a-z]{2}.*"
RETURN u
In questo caso non verra usato l'indice, in quanto tutti i nodi verranno scansionati per verificare quali corrispondono all'espressione regolare indicata dopo l'operatore previsto allo scopo: =~
. Evidentemente ciò può causare un grave degrado delle performance su grandi moli di dati.
Per ovviare a ciò è possibile attivare, attraverso la configurazione, gli indici full text, che si basano su Lucene.
Nel file di configurazione neo4j.properties, andiamo a impostare questi parametri e riavviamo il servizio:
node_auto_indexing=true
node_keys_indexable=name
Nel primo abbiamo attivato l'indicizzazione tramite Lucene, nel secondo abbiamo indicato di indicizzare la proprietà name. Eventuali altre proprietà possono essere aggiunte, separate da virgola. A questo punto resta ancora un passaggio: dobbiamo abilitare l'indice full text. Lanciamo il seguente comanda tramite Neo4j Shell:
index --set-config node_auto_index type fulltext
Ora possiamo usare l'indice, tramite una sintessi dedicata:
START n=node:node_auto_index(name="html") RETURN n