Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Introduzione a CouchDB, il dbms document-oriented da interrogare con JavaScript

Un dbms server document-oriented, da interrogare con JavaScript
Un dbms server document-oriented, da interrogare con JavaScript
Link copiato negli appunti

Il concetto di database relazionale affonda talmente in profondità nell'esperienza quotidiana di uno sviluppatore Web, che alle volte si è portati a pensare non esistano opzioni diverse a questo sistema di persistenza dei dati. Ovviamente non è cosi. Quest'articolo si pone come obiettivo un'introduzione esauriente di CouchDB, un DBMS non relazionale, costruito e pensato per offrirsi come valida alternativa a strumenti più classici (mysql, sql-server, etc.) che il più delle volte impongono impegnativi aggiustamenti alle strutture dati delle nostre applicazioni Web.

CouchDB: un database document-oriented

In CouchDB non esistono tabelle. Il contenuto informativo è suddiviso in diversi database che fungono da contenitori di documenti con strutture potenzialmente disomogenee tra di loro. Il documento è il fulcro di questo software (e di quelli che come lui condividono l'approccio document-oriented); un documento è formato da un insieme di coppie chiave-valore, ad esempio:

{
  "_id":          "9a826400d60073813c6f62508f9260e5",
  "_rev":         "1-1915117622",
  "type":         "Travel",
  "destination":  "Tokio",
  "dates":        {
                    "from": "15/07/2009",
                    "to":   "10/08/2009"
                  },
  "participants": [ "sandro", "francesca", "luca" ]
}

rappresenta un documento contenente le informazioni su di un viaggio; come si vede il valore associato ad ogni chiave può essere una stringa (come nel caso destination), un hash (in dates ad esempio), un array (partecipants) o una qualsiasi altra classe a patto che la stessa contenga informazioni su come serializzare e deserializzare il proprio contenuto.

Figura 1. Logo CouchDB
Logo CouchDB

A differenza dei database relazionali CouchDB non possiede il concetto di schema e quindi ogni documento in ogni database può essere strutturato in modo diverso dagli altri (ad esempio potrei avere un altro documento simile a quello appena illustrato ma con una chiave price_per_person in più).

Le uniche due chiavi obbligatorie sono:

Chiave obbligatoria Descrizione
_id serve per identificare univocamente il documento (è comparabile, semanticamente, alla chiave primaria dei database relazionali)
_rev viene utilizzata per la gestione delle revisioni, ad ogni operazione di modifica infatti la chiave _rev viene aggiornata (questo meccanismo è alla base della prevenzione dei conflitti in fase di salvataggio su CouchDB)

Questo accorgimento permette inoltre di poter interrogare il DBMS su versioni del documento non più attuali in quanto, con un approccio simile a quello di Subversion, CouchDB mantiene memoria di ogni revisione, da quella iniziale alla più recente.

L'altra grande differenza rispetto ai tradizionali RDBMS è il meccanismo di gestione delle query. Nei database relazionali una volta specificate e popolate le tabelle è possibile eseguire query utilizzando un linguaggio conosciuto come SQL (ne esistono vari dialetti ma il concetto non cambia nell'essenza); a fronte di una query il RDBMS utilizza indici interni e le proprie relazioni per costruire in tempo reale una tabella di risultato (che può, nelle query più semplici, essere un sottoinsieme della tabella di partenza).

Questa soluzione riesce ad essere performante in quanto i dati sono strutturati con questo preciso scopo; inoltre il database non deve conoscere a priori le query che verranno eseguite ma può rispondere a qualsiasi interrogazione, purché sia stilata usando SQL valido.

Il modello map-reduce

In CouchDB l'approccio classico porterebbe irrimediabilmente a problemi di performance poiché l'organizzazione a documenti, con ogni documento potenzialmente diverso nella struttura dal precedente, non si presta ai processi di indicizzazione tipici di un RDBMS. Per ovviare a questa difficoltà CouchDB utilizza un modello di programmazione chiamato map-reduce che, in sintesi, funziona nel seguente modo:

Fase di map

Durante questa fase l'intero insieme dei documenti di un database è analizzato con l'obiettivo di estrarre quelli che rispondono a particolari ed arbitrarie regole di selezione. In CouchDB la fase di map si esplicita attraverso una funzione in linguaggio JavaScript che il DBMS applicherà ad ogni documento nel db.

Il risultato dell'esecuzione di questa fase sarà un array associativo composto dalle coppie chiave valore notificate dalla funzione emit (dove la chiave è il primo parametro ed il valore il secondo). Ad esempio la seguente funzione seleziona solamente i documenti il cui valore della chiave type sia Travel (come il documento presentato in precedenza) accodandoli in un hash che ha per chiave l'attributo destination del documento e per valore il documento stesso.

function(doc) {
  if (doc.type == "Travel") {
    emit(doc.destination, doc);
  }
}

Fase di reduce

Questa fase (opzionale) riceve in input l'hash prodotto dalla fase di map dando la possibilità di operare sui dati aggregati, ad esempio contandoli. Se la fase di reduce non è presente il valore di ritorno dell'algoritmo sarà quello della fase di map.

Il modello appena descritto riesce ad avere buone performance a patto di non dover ricalcolare la fase di map ad ogni richiesta, per questo CouchDB necessita di conoscere in anticipo tutte le fasi di map (e di reduce) eseguiremo nella nostra applicazione, in modo da poterne precalcolare i risultati.

Installazione: dal divano al Futon

Prima di continuare con l'analisi di alcune funzioni map-reduce di prova soffermiamoci un istante ad installare CouchDB. Sfortunatamente l'installazione differisce enormemente a seconda del tipo di piattaforma target, è però disponibile un'ottima sezione del wiki ufficiale, che tratta nel dettaglio le varie procedure.

Una volta completato il procedimento possiamo eseguire CouchDB digitando da un prompt couchdb o sudo couchdb a seconda dei propri privilegi di utenza (attenzione: su Windows il comando di avvio è leggermente diverso ma è comunque documentato in coda alla procedura di installazione).

Una volta eseguito CouchDB possiamo puntare il browser all'indirizzo http://localhost:5984/_utils per accedere a Futon, la console di gestione di questo DBMS. Da qui è possibile interagire in modo guidato con le principali funzionalità di questo software.

Figura 2. Futon: creare un database
Futon: creare un database

Cominciamo con il creare un database cliccando su Create database ... e digitando travels nel campo preposto al nome, quindi seguiamo il link Create document ... e lasciamo in bianco il Document ID in modo che il sistema lo generi in automatico. Cliccando su Create potremo apprezzare la vista del nostro primo documento in CouchDB composto, per il momento, soltanto dai campi obbligatori _id e _rev.

Figura 3. Il primo documento
Il primo documento

Utilizziamo ora il pulsante Add Field per ricreare gli stessi campi dell'esempio di documento proposto in precedenza prestando attenzione ad inserire come value del codice in sintassi Javascript, questo significa che:

  • una stringa deve essere racchiusa tra le virgolette (es: "Tokio")
  • un array deve essere specificato usando le parentesi quadre (es: ["sandro", "francesca", "luca"])
  • un hash deve essere racchiuso tra le parentesi graffe (es: {"from" : "10/10/2009", "to" : "14/10/2009"})

A lavoro ultimato, cliccando su Save Document, notiamo come il valore dell'attributo _rev sia cambiato, a certificazione dell'avvenuta creazione di una nuova revisione. Possiamo in ogni momento navigare tra le varie revisioni di un documento utilizzando i pulsanti Previous Version e Next Version.

Creare una view

Addentriamoci un po' più all'interno di questo software provando a generare alcuni classici esempi di view attraverso l'interfaccia di Futon; per fare questo selezioniamo il database che abbiamo creato in precedenza (travels) e portiamo il selettore Select view: su Temporary View

code>.

Figura 4. Creare una view
Creare una view

Ci troviamo ora di fronte ad una coppia di aree di testo modificabili (rispettivamente per la funzione di map e quella di reduce). L'area di map contiene già un piccolo esempio che possiamo eseguire premendo il pulsante Run. Il risultato è un hash di tutti i documenti del database, ognuno con chiave null come specificato dall'istruzione emit(null,doc).

Manteniamo questa funzione di map e proviamo, nell'area reduce, a scrivere del codice che ci consenta di contare il numero di risultati ottenuti, per fare questo scriviamo:

function (key, values, rereduce) {
  return values.length;
}

Premendo il tasto Run otterremo una coppia {null: 1}; dove 1 è il numero di documenti del nostro database (possiamo provare ad aggiungere un nuovo documento e verificare che il numero sale a 2).

Ora modifichiamo la funzione di map in modo che le chiavi risultanti siano consistenti con l'attributo destination:

function(doc) {
  emit(doc['destination'], doc);
}

Eseguendo la view noteremo che questa volta avremo un hash formato dalle coppie:

{'destinazione X','numero di documenti con destinazione X'}

questo accade perché Futon aggiunge in automatico un parametro (group=true) alla chiamata HTTP GET della view. Questo fa sì che la funzione di reduce venga invocata per ognuna delle chiavi prodotte dal map (con values valorizzato con l'insieme dei documenti associati a quella chiave). Possiamo disabilitare questo comportamento sopprimendo il passaggio del parametro group; in questo caso la funzione di reduce verrà invocata solamente una volta con tutti l'hash ottenuto dalla parte di map.

Conclusioni

Questo breve articolo assolve il non facile compito di presentare le caratteristiche salienti di un database document-oriented, di introdurre CouchDB e di illustrarne la sua operatività attraverso la console di gestione Futon.

Chiaramente questo DBMS non si presta ad ogni possibile progetto ma, credo, riesce ad essere interessante e preferibile in tutte quelle applicazioni che vivono di dati destrutturati o soggetti a veloci cambiamenti di struttura.

Ti consigliamo anche