In questa lezione conclusiva, vedremo un esempio pratico in cui realizzeremo un database in Redis che assolva i seguenti compiti:
- deve rappresentare un contenitore di notizie. Tali notizie devono essere filtrabili in base ai seguenti parametri:
lingua, settore, accessibilità privata o pubblica. Ogni notizia sarà un oggetto composto da titolo,
descrizione, link all'articolo completo, data di pubblicazione; - gli utenti possono accedere anonimamente (visualizzando così solo notizie pubbliche) o effettuando un'autenticazione
che dia accesso anche ai contenuti privati. Ogni utente possiede uno username ed una password, ed il database registra per ognuno di essi
nome, cognome e data di creazione di profilo. Una volta eseguito il login, verrà generata dal sistema una chiave di sessione della durata
di un'ora da restituire al client. A tale chiave rimarrà associato nel database l'identificativo dell'utente; - gli utenti che hanno effettuato il login potranno inviare commenti alle notizie. Per ogni commento verrà
memorizzato un titolo, un testo, data e ora e l'autore.
Per realizzare un database di questo tipo dovremo scegliere come distribuire le diverse informazioni tra chiavi e valori. Alcune riguardano
l'identificazione dei contenuti, quindi saranno squisitamente di competenza delle chiavi, altre sono veri e propri raggruppamenti di dati, e pertanto saranno
destinate alle strutture contenute nei valori. I prossimi paragrafi descrivono la soluzione da noi adottata. Esemplificheremo il funzionamento con
istruzioni da riga di comando che emulano le interazioni che svolgerebbe l'applicazione che gestisce il database in favore di client esterni
(web, mobile, etc.).
Autenticazione
Ogni utente verrà registrato nel database con una chiave di prefisso "utenti:" seguita dallo username prescelto. Tale elemento
verrà salvato nel momento in cui il nuovo iscritto effettua la registrazione. Il valore collegato alla chiave sarà una hashmap
contenente i dati. Registriamo il nuovo utente "diabolik" corrispondente al signor Gianluca Verdi:
> HMSET utenti:diabolik nome Gianluca cognome Verdi password ciao1234 creazione 1227225599
OK
> HGETALL utenti:diabolik
1) "nome,"
2) "Gianluca,"
3) "cognome,"
4) "Verdi,"
5) "password,"
6) "amoremio,"
7) "creazione,"
8) "1227225599"
Nel momento in cui l'utente dovesse svolgere un'azione di login ed immettere correttamente le sue credenziali (username "diabolik" e
password "amoremio") l'applicazione genererebbe una nuova chiave di sessione (supponiamo essere in questo caso "ABCD1234") e la salverebbe
nel database. Per le chiavi di sessione, abbiamo scelto di usare il prefisso "sessione:". La durata di un'ora della chiave sarà
gestita salvandola come chiave a scadenza (3600 secondi):
> SETEX sessione:ABCD1234 3600 utenti:diabolik
OK
Il valore legato a questa chiave è lo username dell'utente. Ogni volta che il client vorrà svolgere un'azione per conto di un utente che
ha già eseguito il login dovrà inviare di nuovo la chiave di sessione. Con questa, dal lato server sarà possibile ricavare con GET il nome utente
(e da qui, indirettamente, i suoi dati) e con TTL il tempo residuo prima della scadenza:
> TTL sessione:ABCD1234
(integer) 3245
> GET sessione:ABCD1234
"utenti:diabolik"
Vale la pena precisare due aspetti. In primis, sarà cura di chi sviluppa l'applicazione scegliere se al client debba essere fornita
la chiave di sessione priva o meno del prefisso, vale a dire se il client debba custodire "sessione:ABCD1234" o semplicemente
"ABCD1234" in quanto il prefisso sarà comunque noto dal lato server. In secondo luogo, nel caso in cui la sessione risulti scaduta
(non sarà trovata alcuna chiave) sarà ancora a discrezione di chi progetta l'applicazione reindirizzare l'utente verso un'eventuale pagina di login
o produrre in automatico una nuova chiave. Tali aspetti sono fondamentali ai fini del buon funzionamento del client e pertanto vanno
menzionati, ma non influiscono sulla strutturazione interna del database.
Notizie
Le notizie, indipendentemente dalla tematica affrontata, saranno identificate da un numero progressivo. Ciò può essere gestito in
Redis mediante il comando INCR che esegue un incremento unitario:
> INCR id_notizia
(integer) 24
Tra l'altro, si ricordi che tale comando eseguito su una chiave non ancora esistente ne imposterà il valore a 1 senza restituire
alcun errore. Per la gestione delle notizie, si potrà optare tra più soluzioni alternative, tra cui: memorizzare chiavi più semplici per ogni notizia
e raccoglierle in liste e insiemi per il recupero, oppure memorizzare chiavi più complesse per le notizie idonee per eseguire filtri
grazie alla loro segmentazione. Seguiamo questo ultimo metodo specificando il prefisso "notizia:" ed una sequenza di segmenti in
grado di specificare settore, lingua, e livello di accesso. Ad esempio, prendiamo due notizie di sport:
> HMSET notizia:sport:it:pub:25 titolo "Vince il Milan" descrizione "Milan-Ascoli 3-2" dataora 1534949622 link "www.miositonotizieesempio.it/notizia/milan/12344567"
OK
> HMSET notizia:sport:it:priv:27 titolo "Esonerato Walter Verdi" descrizione "Presto nuovo allenatore per l'Atletico TreCase" dataora 1534948432 link "www.miositonotizieesempio.it/notizia/calcio/1234568"
OK
La prima ha id "notizia:sport:it:pub:25" ed abbiamo inserito nella chiave il segmento "pub" per ricordarci che la sua lettura non
richiede login, la seconda è privata ed ha chiave "notizia:sport:it:priv:27". Abbiamo assegnato la stessa struttura ad entrambe
con i campi titolo, descrizione, dataora (legata ad un timestamp) ed il link dell'articolo (un URL fittizio a scopo di esempio):
> HGETALL notizia:sport:it:priv:27
1) "titolo"
2) "Esonerato Walter Verdi"
3) "descrizione"
4) "Presto nuovo allenatore per l'Atletico TreCase"
5) "dataora"
6) "1534948432"
7) "link"
8) "www.miositonotizieesempio.it/notizia/calcio/1234568"
Una tale segmentazione delle chiavi permette di eseguire ricerche di tutte le notizie di sport:
> KEYS *:sport:*
1) "notizia:sport:it:pub:25"
2) "notizia:sport:it:priv:27"
o delle sole pubbliche
> KEYS *:sport:*pub*
1) "notizia:sport:it:pub:25"
o delle sole private
> KEYS *:sport:*priv*
1) "notizia:sport:it:priv:27"
Commenti
Per i commenti, abbiamo necessità più articolate in quanto dobbiamo salvare il singolo commento con tutti i suoi dati e, successivamente,
legare tra loro tutti quelli che fanno capo alla stessa notizia. Pertanto, scegliamo di istituire un nuovo contatore che distingua i commenti
tra di loro:
> INCR id_commento
(integer) 15
> HMSET commento:15 titolo "Mi dispiace" testo "Grazie Walter per il tuo lavoro!" dataora 1534948899 utente diabolik
> INCR id_commento
(integer) 16
> HMSET commento:16 titolo "Sono contento" testo "Meglio uno più vincente!" dataora 1534949150 utente pippo
A questo punto i commenti saranno inseriti in una lista per essere raggruppati attorno alla notizia con id 27:
> RPUSH lista_commenti:27 commento:15
> RPUSH lista_commenti:27 commento:16
Abbiamo usato RPUSH
in modo che dall'indice 0 in poi venga rispettato l'ordine cronologico di inserimento:
> LRANGE lista_commenti:27 0 -1
1) "commento:15"
2) "commento:16"
e con una ricerca basata sull'id 27 troveremmo sia la notizia che la lista dei commenti:
> keys *:27
1) "lista_commenti:27"
2) "notizia:sport:it:priv:27"