Aunque SQL está mucho más extendido, proyectos como TrimQuery demuestran que no es imprescindible que el navegador incorpore un parseador para un lenguaje con tantas versiones incompatibles entre sí como fabricantes existen.
BrowserCouch es un intento de implementar la tecnología MapReduce en el navegador. Está escrito enteramente con JavaScript con la intención de que funcione con todos los navegadores, aprovechando de forma transparente otras capacidades disponibles.
No es casualidad que esta librería trate de imitar la funcionalidad de CouchDB en el cliente, e incluso podría soportar su integración en el futuro.
Esta librería es una respuesta al artículo de Vladimir Vukicevic HTML5 Web Storage and SQL. Una API al estilo de CouchDB parece una buena solución para el almacenamiento persistente en la Web, ya que mucha de su semántica es delegada al lenguaje JavaScript, lo que lo convierte en potencialmente sencillo de estandarizar. Además, el paradigma MapReduce también saca ventaja natural de los múltiples núcleos de los microprocesadores- algo cada vez más frecuente hoy día.
Tutorial
Ésta es una breve introducción al uso de la API de BrowserCouch y del mecanismo MapReduce. Hay que remarcar que no se trata de software "maduro" ya que aún faltan muchas características de CouchDB por portar y la API no es estable.
Respecto a los ejemplos de código de este tutorial: se ejecutan en el mismo navegador y los resultados se muestran en algunos casos en esta misma página. Esto permite asegurar que el software funciona como se pretende y permite un aprendizaje interactivamente. También conviene avisar que existe la posibilidad de que alguna de los ejemplos falle.
Primeros pasos
Supongamos que queremos añadir soporte sin conexión a un blog. Para obtener una base de datos llamada
blog-posts
en BrowserCouch se puede usar la siguiente función:BrowserCouch.get('blog-posts', function onRetrieveCb(db) { blogDb = db; /* Guarda la base de datos para luego. */ }, new FakeStorage() );Está claro que el primer parámetro es el nombre de la base de datos; el segundo es la función
callback
que recibirá la base de datos tras obtenerse.El tercer parámetro especifica el motor que será usado para almacenar la base de datos entre sesiones de navegación. En este caso se usa
FakeStorage
, el cual almacena los datos en memoria de forma no persistente, lo que vale como ejemplo. Podríamos igualmente no especificar el tercer parámetro para que BrowserCouch averigüe el mejor motor según las capacidades disponibles.Si la base de datos no existe, se creará una nueva. Poner nuevos artículos en la base de datos se consigue con el método
put()
:blogDb.put( [ {id: 0, author: 'Myk', title: 'Burritos', content: 'Burritos are yum.'}, {id: 1, author: 'Thunder', title: 'Bacon', content: 'I like bacon.'}, {id: 2, author: 'Thunder', title: 'Beer', content: 'Beer is good too.'}], function onDone() { /* Código función... */ } );Cada elemento que guardemos en la base de datos necesita un atributo identificador, pero a parte de eso, el elemento puede contener cualquier dato codificable como JSON.
Vistas
Ahora que tenemos datos, podemos jugar con la generación de vistas sobre los datos usando el mecanismo MapReduce. Por ejemplo, a continuación se define una vista definiendo sólo la fase de mapeo que organiza todos los títulos de artículos por autor:blogDb.view({ map: function(doc, emit) { emit(doc.author, doc.title); }, finished: function(result) { displayInElement(result, 'author-keyed-view'); } });El método
view()
tiene muchos argumentos opcionales, y esa es la razón por la que estamos pasando un único objeto con las claves que se corresponden con los nombres de los argumentos. El argumento map
es la función usada en la fase de mapeo, y el argumento finished
es la función callback que recibirá los resultados cuando termine el proceso.El resultado obtenido en el elemento
author-keyed-view
será:{"rows":[ {"id":0,"key":"Myk","value":"Burritos"}, {"id":1,"key":"Thunder","value":"Bacon"}, {"id":2,"key":"Thunder","value":"Beer"} ]}
Como se puede ver, BrowserCouch esencialmente itera sobre todos los objetos de artículo, pasándole cada uno a la función
map()
junto con una función arbitraria llamada emit()
. La función map()
entonces decide si el par clave-valor debe aceptarse pasándolo a la función emit()
. map()
puede hacer tantas llamadas a emit()
como desee: cada llamada generará una nueva fila en la vista.En este punto quizás resulte interesante saltar a la sección de pruebas para jugar definiendo una función
map()
personalizada. Edita el código y al salir del campo de texto se actualizará el resultado.La fase de reducción de una vista es totalmente opcional y un poco confusa. Intentemos añadir una función
reduce()
a nuestra vista para agrupar juntos los títulos de artículos con los autores:blogDb.view({ map: function(doc, emit) { emit(doc.author, doc.title); }, reduce: function(keys, values) { return values; }, finished: function(result) { authors = result; /* Guarda los resultados para más tarde. */ displayInElement(authors, 'author-titles-view'); } });lo que generará el siguiente resultado:
{"rows":[ {"key":"Myk","value":["Burritos"]}, {"key":"Thunder","value":["Bacon","Beer"]} ]}BrowserCouch tomará todas las filas generadas por
map()
y genera una nueva lista de filas clave-valor, donde el valor de cada fila es la lista de todos los valores que coinciden con la clave de la fila. Esto explica el significado del argumento values
pasado a reduce()
.El argumento
keys
es una lista de tuplas de dos elementos, el primera de los cuales es la clave y el segundo es el identificador del documento que emitió la clave durante la fase de mapeo.La función
reduce()
es invocada por cada clave única, y su valor de retoro es el valor de su clave en la vista final.Una vez que se tiene la vista, se puede usar el método
findRow()
de la vista para encontrar la primera fila cuya clave coincida con (o sea la más parecida a) la provista. Por ejemplo:var rowIndex = authors.findRow('Thunder'); displayInElement(authors.rows[rowIndex], 'author-find-row-view');Y el resultado es:
{"key":"Thunder","value":["Bacon","Beer"]}
Prueba tú
Si tienes los ojos cruzados, no te preocupes - a mucha gente le lleva bastante tiempo comprender cómo funciona MapReduce. Dicho eso, la forma más rápida de comprender su funcionamiento es jugar creando tu propia vista.En la página del proyecto tienes un campo de texto para hacerlo. Pulsa la tecla tabuladora cuando hayas terminado de hacer cambios para lanzar la consulta y ver los resultados.
Excelente el tutorial, es un tema un tanto complejo pero creo que lo has dejado muy claro. Gracias Alfons! :-)
ResponderEliminarHola Nando! Cuánto tiempo.
ResponderEliminarEl artículo no es más que una traducción. Pero me interesa que esta clase de tecnologías lleguen a más gente y creo que una traducción es una buena forma, aunque para estar al día en estas cosas hay que saber mucho inglés. Yo pongo mi granito de arena y ya veremos que sale.
Hola digitta.com, hace poco que descubri este blog y la verdad te estoy muy agradecido por lo que haces, informacion como la que brindas es muy dificil encontrar (bueno al menos para mi)...sigue para adelante...
ResponderEliminarGracias por tus palabras. Intentaré seguir en la misma línea.
ResponderEliminar