4TIC
 
  • Increase font size
  • Default font size
  • Decrease font size

CouchDB: una base de datos diferente

Con este post vamos a empezar una pequeña serie de artículos en los que veremos que es CouchDB, como funciona y para que nos puede servir.

Podemos definir CouchDB como una base de datos documental sin schema, consultable al estilo MapReduce, accesible por REST y con una funcionalidad de replicación integrada. Casi nada... será mejor que veamos cada una de estas características en más detalle.

Base de datos documental sin schema

Estamos acostumbrados a que cuando hablamos de base de datos pensemos en el modelo relacional (columna, filas, tablas, relaciones...)  En cambio CouchDB nos ofrece guardar nuestros datos de otra forma. En pocas palabras, para CouchDB solo hay documentos. Todo lo que almacenamos es un documento sin schema, lo cual nos permite guardar juntos documentos con distintos campos dentro de la misma BD.

Estos documentos se almacenan en JSON, un formato ligero, sencillo y cómodo de usar desde cualquier lenguaje. Vamos a ver un típico documento de CouchDB:

{
"_id" : "234a41170621c326ec63382f846d5764",
"_rev" : "1-480277b989ff06c4fa87dfd0366677b6",
"tipo" : "articulo",
"titulo" : "Esto es una prueba",
"cuerpo" : "Soy el contenido de un artículo de prueba",
"tags" : ["cine", "comedia"]
}

El _id sirve para que CouchDB lo distinga de otros documentos y a nosotros nos vale para poder recuperarlo posteriormente. Es un string que puede contener lo que queramos aunque si no ponemos nada CouchDB generará un UUID. ¿Y por que no genera los ids con un autoincremental? Bueno, el uso del UUID nos permite tener un id único UNIVERSALMENTE, lo cual nos será muy útil cuando entremos en el tema de la replicación, pero no adelantemos acontecimientos...

El campo _rev también es especial y sirve para que CouchDB controle la versión del documento. Cada vez que guardamos un cambio en el documento cambia el número de revisión (se incrementa el 1 de antes del - y el resto del número cambia). Esto es útil porque cada vez que intentamos guardar un documento pasamos el numero de la versión que vamos a modificar, de forma que si CouchDB ve que estamos guardando un cambio sobre una revisión antigua da error y no permite continuar.

Después, el resto de campos  podemos poner lo que queramos, siempre que usemos expresiones JSON válidas, como en el ejemplo donde tenemos el atributo tags que es un array de strings. Podría ser un diccionario ({"clave1": "valor1", "clave2":"valor2"}),un número (2), etc...

La parte buena de estar trabajando sin schema es que este sistema se adapta a los cambios en la estructura de los documentos que es necesario almacenar. De esta forma nos podemos despreocupar de lo que vamos metiendo en la base de datos, ya nos preocuparemos cuando tengamos que recuperarlos.

Consultable al estilo MapReduce

CouchDB no nos ofrece un lenguaje tipo SQL para realizar consultas sino que nos ofrece un sistema basado en MapReduce para poder obtener los datos que queramos. ¿Y como funciona esto?  Pues es mas sencillo de lo que parece, se compone de una parte Map una parte Reduce.

Map: Es una función que se ejecuta para cada documento. Esta función recibe como parámetro el propio documento y puede devolver pares de clave-valor. Un función puede devolver 0, 1 ó varios de estos pares para un único documento de entrada. A primera vista esto puede parece muy ineficiente, pero la función sólo se ejecuta una vez para cada documento y va almacenando los resultados en un índice que relaciona claves y valores de forma que en posteriores consultas se ataque sobre este índice. Por supuesto, si alguno de los documentos de nuestra BD se modifica, se vuelve a rehacer el índice (pero sólo para los documentos modificados)

Un ejemplo rápido:

function(doc) {
  for (var i in doc.tags)
   emit(doc.tags[i], doc);
}

Como podemos ver las funciones Map (y las Reduce) se definen en Javascript. CouchDB ofrece una arquitectura pluggable mediante la cual podemos crear estas en nuestro lenguaje favorito (Python, Ruby...).

Esta función devuelve como clave cada uno de los tags y como valor el propio documento. De esta forma ejecutada sobre nuestro doc de ejemplo daría 2 filas: una para "cine"  y otra para "comedia" ambas teniendo como valor el propio documento.

Después sobre este conjunto de resultados podemos  filtrar por clave o bien por un par de claves inicio y fin. De esta forma si queremos sabe todos los artículos que son de cine filtraríamos aquellos que tienen la clave "cine". Es fácil, ¿no?

Lo bueno es que las claves pueden ser cualquier tipo de datos soportado por JSON como arrays, números, diccionarios... lo cual puede ser útil para realizar consultas más avanzadas.

Reduce: A grandes rasgos esta agrupa los resultados del Map para obtener un número. De esta forma si la parte Map anterior fuera asi:

function(doc) {
  for (var i in doc.tags)
    emit(doc.tags[i], 1);
}

Podemos definir una funcion reduce tal que asi:

function(keys, values)
{
  return sum(values);
}

La función Reduce recibe como entrada todas las claves y todos los valores. Con la funcion sum, proporcionada por CouchDB, vamos acumulando los 1 que devuelve la función map de forma que como resultado de esta obtenemos varias filas con cada uno de los tags como clave y el número de documentos que tienen este tag como valor.

En la nomenclatura de CouchDB un par de funciones MapReduce se llama view (no siendo obligatorio definir la parte reduce).

Accesible por REST

REST nos permite acceder a nuestro datos de una forma muy sencilla a través de URLs. Por ejemplo para recuperar nuestro documento con id  6e1295ed6c29495e54cc05947f18c8af de nuestra BD albums accederíamos a esta URL que nos devuelve el documento JSON correspondiente:

http://localhost:5984/albums/6e1295ed6c29495e54cc05947f18c8af 

De forma similar si queremos acceder a una view como la que comentábamos cuando explicábamos el Map y recuperar algún resultado iremos a la URL:

http://localhost:5984/blog/_design/doc/_view/tag?key="cine" 

Esta URL quiere decir que estamos accediendo a la BD llama blog, para recuperar un design document (donde se guardan las views dentro de la BD) llamado doc y dentro de este a la view llamada tag. Después como comentábamos antes, dentro de la view queremos recuperar el resultado identificado por la clave cine (es interesante ver como hay que pasarla entre " " ya que la clave es un string, uno de los tipos válidos de JSON).

En esta URL obtendríamos un resultado similar a este:

{"total_rows":4,"offset":0,"rows":[
{
"id":"9280b03239ca11af9cfedf66b021ae88",
"key":"cine",
"value":{"_id":"9280b03239ca11af9cfedf66b021ae88",
"_rev":"1-0289d70fe05850345fd4e9118934a99b",
"tags":["cine","comedia"]}
},
{
"id":"a92d03ff82289c259c9012f5bfeb639c",
"key":"cine",
"value":{"_id":"a92d03ff82289c259c9012f5bfeb639c",
"_rev":"2-97377eef95764a4dbf107d8142187f53",
"tags":["cine","drama"]}}
]}

Como podemos ver en key y value tenemos los resultado esperados: el tag y el documento que lo contiene. Aparte CouchDB incluye el id del documento que ha dado lugar a ese resultado (el que entra como parámetro en la función Map). Además se devuelve el numero total de filas devueltas y el offset del resultado.

En vez del parámetro key se le puede pasar a nuestra vista un par de parámetros startkey y endkey para obtener un rango de los resultados que nos interese (p.ej. en una view que devolviera como clave un string representando una fecha). 

Replicación integrada

Una funcionalidad relativamente exótica que nos permite que nuestra BD de datos sincronice sus datos de una forma muy sencilla (una simple llama REST la activa) con otra BD remota o local. De este modo podemos tener de una forma sencillísima una o mas réplicas de nuestra BD para implementar arquitecturas de alta disponibilidad o de balanceo de carga.

Si os acordáis de cuando comentábamos  que CouchDB usaba por defecto UUIDs como identificadores de los documentos veréis que al tener varias BD de datos intercambiándose filas esto es indispensable. Pensad que pasaría si tuviéramos 2 o más bases de datos cada una con su autoincremental como id y empezaran a pasarse datos entre ellas ;)

De forma similar, el atributo _rev anteriormente comentado nos permite que CouchDB detecte casos en los que un mismo documento ha sido modificado en varias bases de datos a la vez (cada documento tendria un _rev diferente) .

Si os ha parecido interesante este post podéis probar a seguir la introducción práctica ofrecida en el libro gratuito de CouchDB aquí

 

Add comment


Security code
Refresh

Documentación

Collaborations


4TIC colabora con decharlas.com para la difusión de la tecnología en Castellón
4TIC apuesta por el conocimiento abierto y se convierte en patrocinador oficial de decharlas.com para la organización de charlas técnicas gratuitas en Castellón.
 
4TIC signs an agreement with UJI for marketing CryptoApplet
4TIC has signed an agreement with the Universitat Jaume I of Castellón, to obtain a commercial license of CryptoApplet, becoming the first enterprise to give commercial support and developing new functionalities over this product

The existence of a commercial license is very important for usign CryptoApplet in non open source projects or in projects that don't meet the restrictions given by GPL license used by CryptoApplet.

 
4State in the "Montserrat Tarradellas i Macià" archive

The Universitat Rovira i Virgili coordinates the project for the digitization and transcription of the historical background from "Montserrat Tarradellas i Macià" archive in the Monasterio de Poblet (Tarragona), using 4State to give support to the transcription and and digital archive of the historical material.

 
SafeNet certification

4TIC has become an official partner of SafeNet and has certified his product of digital signature 4Sign for the use of SafeNet's hardware key storage module: SafeNet Luna HSM

 
VIGIA in the Universitat Rovira i Virgili

The Universitat Rovira i Virgili of Tarragona, has choosenVIGIA for managing their SALTO installation, which now has 1500 doors.