RavenDB VII Consultas e indices estáticos..

 

Map

En la anterior entrada hicimos la primera introducción de lo que considero uno de los puntos fuertes, a mayores del API cliente, de RavenDB y este no es otro que su sistema de consultas. En esta entrada, hicimos una introducción sobre los índices que utiliza por detras RavenDB y que, como ya sabemos, se basan en Lucene.NET. Pero, los índices que vimos, eran dinámicos o bien generados automáticamente por RavenDB, si detecta un numero alto de consultas sobre el temporal. Estos índinces dinámicos no suelen ser una buena solución porque pueden tener una alta latencia, se ejecutan al vuelo y por lo tanto pueden tener un tiempo de ejecución alto, y no son demasiado flexibles en cuanto a las necesidades que podemos tener, trabajo con datos espaciales, búsquedas de texto completo, proyecciones etc.

 

Un índice estático no es más que la definición de  una estructura llamada Raven.Abstractions.Indexing.IndexDefinition cuyo cuerpo podemos ver a continuación:

 


















































 

Si observa la definición anterior, hay una serie de puntos muy interesantes, como son los miembros Map, Reduce, TransformResult y Analyzers. A lo largo de esta entrada, intentaremos, aunque sea de forma somera, ver todos y cada uno de ellos, seguramente juntándolos con algún otro elemento necesario. Aunque es posible crear directamente esta estructura, por lo general, la misma se suele crear a partir de una clase llamada IndexDefinitionBuilder, como podemos ver en el siguiente ejemplo de código.

 




















 

Con esto acabamos de ordenar al servidor de RavenDB crear un nuevo índice, llamado PostByTitle,sobre la colección de posts, utilizando para ellos los métodos de comandos de base de datos que nos ofrece nuestra interface IDocumentStore.  Para el índice anterior podemos ver como hacer la búsqueda de los documentos que nos interesan y a mayores sobre que campos vamos a hacer la búsqueda de nuestros documentos, en este caso solamente la propiedad Title.

 

Ahora, si quisiéramos hacer una consulta sobre este índice podríamos hacer algo tal cual lo que vemos en el siguiente tip de código:

 












 

Analyzers

Bueno, parece que más o menos está claro, sin embargo, seguro que a muchos se os ocurren algunas preguntas. La primera que a mi me vino a la mano, es como es la creación de ese índice en el campo Title, es decir, la tokenización de los valores se puede hacer de muchas maneras, desechando terminos terminales, pudiendo poner sinónimos, etc…. Seguramanete, esta pregunta me la hice porque no conocía demasiado bien el concepto de Analizadores en Lucene.NET. Pues bien, una vez comprendidos los analizadores la siguiente pregunta es ver ¿como podemos decirle a los índices de RavenDB que ciertos campos estén marcados por un analizador? Pues si revisamos el codigo de creación de nuestro índice podemos ver lo sencillo que resulta esta tarea, como se puede ver a continuación.

 




















Observe como el diccionario Analyzers nos permite, de una forma bastante sencilla, incorporar una analizador específico a uno de los campos del Map. Puesto que nos pide el nombre de un tipo de analizador una forma sencilla de hacerlo sin tener que acordarse de todo el fullname es referenciar al tipo y extraer su FullName. Este tipo, está en la librería Lucene.NET que tendrá que referenciar puesto que no forma parte del API cliente de RavenDB.

 

Bien, puesto que seguramente, si ha seguido este post y ha modificado el índice se habrá dado cuenta de que una vez creado no podemos volver a ejecutar PutIndex, puesto que esto falla, al existir uno lógicamente. Para prevenir estos casos y hacer el bootstraping de los índices de una forma más sencilla, RavenDB nos ofrece un mecanismo alternativo que pasaremos a comentar a continuación.  Con el fin de facilitar la definición y reutilización de indices RavenDB nos propone definir los mismos en una clase que extienda AbstractIndexCreationTask<TDocument> como por ejemplo podría ser la siguiente:

 






















 

 

Ahora, para hacer el botstrapper solamente tendríamos que ejecutar la siguiente instrucción, sin preocuparnos si los indices son nuevos, modificaciones de los existentes o ya existen con esa misma definición.

 


 

Reduce

 

Con lo anterior, aunque no sea muy profundamente, ya hemos terminado la parte referida al Map, pero como ya seguramente sabrá, y hemos dicho, esta no es la única parte importante de un índice puesto que también podemos especificar una función de Reduce. En palabras parcas, un Reduce es una paso posterior a la selección o mapeo de documentos, sobre la podemos hacer opero tstaciones ( asociativas ) de agregación. A mayores, la idea es que estas operaciones se puedan hacer sobre subconjuntos de los seleccionados, con el fin de paralelizarlas, por eso la consideración de que sean asociativas las funciones de agregación. Hay una muy buena referencia visual acerca de cual es la teoría de Map/Reduce en este post que seguro le introducirá en este aspecto de buena manera.

 

Una vez que nos hemos preocupado por saber que es un Reduce, concepto que por supuesto tenemos en todas las bases de datos documentales como MongoDB, por ejemplo, pasaremos a ver algún ejemplo de esta característica. Supongamos por ejemplo que deseamos obtener la suma de los comentarios agrupadas por identificador de blog, puesto que la “cuenta” es una operación asociativa, podríamos crear un índice como sigue:

 

 










































 

Y podríamos consultar esto de la siguiente forma en el API de RavenDB.

 
























 

Bueno, creo que son ya bastantes cosas como para ponerse a jugar sin esperar más no?? yo creo que si, o sea que dejaremos para otra entrada algunas cosillas que nos faltan como multi-maps, funciones de transformación etc. etc..

 

Saludos

Unai

Raven DB VI, La hora de las consultas…

Por fin,  seguro que ha sido la frase de muchos al oir que ya llegábamos a las consultas… Bien, pues si, con esta entrada inauguramos el trabajo con las consultas en RavenDB. Después ya de unos cuantos posts sobre el tema creo que ya es hora de tocar una de las cosas más importantes y que mejor hace RavenDB, que es las consultas.

Como siempre, ponemos un pequeño modelo de clase para realizar los distintos ejemplos. En esta occasion reutilizaremos las tipicas Blog, Post y Comment que ya hemos visto otras veces, y daremos por supuesto que tenemos ya una cantidad de documentos incluídos en RavenDB antes de procesar las consultas.










































* Como ya se ha comentado alguna vez, los modelos de las entradas tienen una finalidad lectiva y por lo tanto no se tiene porque ajustar a la realidad

 

Por defecto, y en la práctica, toda consulta que hacemos a RavenDB representa un índice sobre el almacen de RavenDB utilizando el proyecto Lucene .NET. Lucene, es un motor de búsqueda basado en .NET, en realidad es un port de java, basado en un índice invertido que funciona realmente rápido y dispone de muchas características importantes.  Por suerte, RavenDB no nos obliga a conocer la sintaxis de Lucene, aunque aprendiendo Lucene entenderíamos muchos de los comportamientos del motor de consultas, puesto que nos ofrece un completo soporte de Linq, pudiendo escribir todas nuestras consultas en Linq y dejando al API de cliente que se encargue de la traducción de las mismas a la sintaxis de Lucene.

Con el fin de mostrar el primer ejemplo, supongamos el siguiente código.














 

En la consulta anterior podemos ver un ejemplo de consulta dinámica tal cual la podríamos hacer contra otro almacén como una colección de objetos o una tabla con EF. Lógicamente, esta consulta genera una petición http al servidor de RavenDB, en concreto, la siguiente petición:

 

GET http://ravenserver/indexes/dynamic/Posts?query=-Title%253ARavenDB%2520AND%2520Title%253A*&start=0&pageSize=128&aggregation=None HTTP/1.1
Accept-Encoding: deflate,gzip
Content-Type: application/json; charset=utf-8

Si examinamos la trama podemos observar los siguientes elementos:

 

  • Por defecto, las consultas se hacen sobre un indice, en este caso, como no hemos especificado cual se hace sobre un índice dinámico
  • Por defecto las consultas nos protegen contra resultados potencialmente grande, agregando por defecto un parámetro llamado pageSize , cuyo valor por defecto es 128.

Untitled

 

Bien, la primera condición era algo que ya sabíamos, es decir, si RavenDB está basado en Lucene tiene que existir algún tipo de índece. RavenDB, puede generar un índice dinámico para aquellas consultas que no se hacen sobre un índice estático. A mayores, si considera que se ha utilizado una consulta un numero alto de veces, podría materializar el indice dinámico en un índice estático. Para provocarlo, realize esa consulta un número de veces y diríjase a nuestra herramienta de administración de RavenDB, Raven Management Studio y podrá ver algo similar a lo mostrado en la imagen de la derecha. Por supuesto, más adelante veremos como crear los índices de forma estática.

 

El otro punto que hacíamos notar es como RavenDB por defecto nos protegía de las consultas potencialmente grandes restringiendo el número de resultados a 128, puesto que, por defecto el valor de pageSize es este. Si, nosotros queremos traer un conjunto mayor de resultados podríamos hacer uso de este operador en la consulta,  de forma similar a como vemos a continuación:

 

 
















 

Otro nivel de protección que se incluye en RavenDB es que, por defecto, tampoco nos permite obtener más de 1000 resultados por consulta. En este caso, la protección no está en el cliente sino en el servidor y por lo tanto, modificar esto no es tan sencillo como acabamos de ver con el operador Take. Para hacerlo, tenemos que incluir en la configuración del servidor una nueva clave de configuración que se llama MaxPageSize.

 


A mayores, y como elemento de valor, RavenDB incluye algunos métodos de extensión a nuestras consultas que nos permitirán desde customizar las consultas hasta obtener información acerca de las mismas. El primero de los métodos que veremos es Statistics, gracias al cual podremos obtener información acerca del número total de documentos en la base de datos y otra información de valor.

 






















Untitled2

RavenQueryStatistics, nos ofrece, como puede observar, la siguiente información:

 

  • El nombre del índice que se ha utilizado en la consulta
  • Si los datos son state
  • El número total de documentos en la base de datos
  • El número de documentos omitidos ( hablaremos sobre esto)
  • La fecha de ejecución del índice
  • La fecha, a partir de la cual los datos son state

 

 

Un elemento importante de estas estadísticas es el de SkippedResults. Este valor nos ofrece la información de aquellos elementos que no se han tenido en cuenta para calcular el número total de resultados. Por ejemplo porque se ha aplicado una claúsula Distinct. Con esto, queremos decir que por ejemplo, si estamos paginando y hemos obtenido una primera pagina, incluyendo 5 en el valor de SkippedResults la siguiente página tendria que pedirse de la siguiente forma: pageCount + SkippedResults.

Otro de los métodos que podemos incluir en nuestras consultas es el método de Customize. En esta ocasión, este método no nos permite obtener más información sino que, nos permite customizar cierto grado de comportamiento de nuestras consultas. Imáginese por ejemplo que usted en una consulta dinámica no puede permitirse la consulta de datos state,  pues bien, con Customize podemos indicar esto de una forma sencilla.

 






















 

 

Si observa durante un momento los métodos de los que disponemos en IDocumentQueryCustomization , el tipo del parámetro de entrada en la Action que establecemos en Customize, podemos ver que disponemos de diferentes sobrecargas y posibilidades para la espera de resultados no state.

 

  • WaitForNonStaleResultsAsOf: Nos permite establecer una fecha de corte, que lógicamente no tiene porque ser en este momento, para el cual los datos no puedan ser stale. Por supuesto, también nos permite indicar un tiempo máximo de espera.
  • WaitForNonStaleResultsAsOfNow: Nos permite especificar la fecha de corte como ahora.
  • WaitForNonStaleResultsAsOfLastWrite: Nos permite especificar la fecha de corte como la última en la que se ha escrito por parte de la aplicación.
  • WaitForNonStaleResults: Nos permite indicar que el procesado de la query debe de esperar por resultados no stale. Podemos indicar un tiempo máximo de espera si deseamos no estar bloqueando at infinitum. Solamente se suele utilizar dentro de tests unitarios, tiene demasiadas implicaciones como para que exista su uso fuera de este ámbito.

 

La propina

Bueno, la entrada ya se acerca a su final. En la misma, hemos visto los principios de las consultas en RavenDB y los índices dinámicos. Para la siguiente entrada, hablaremos acerca de los índices estáticos, funciones map-reduce y analizadores. No obstante me gustaría dejar una pequeña propina…. fíjese en el siguiente código.

 

 














 

Al igual que el método Include en EF, este método nos permite en RavenDB indicarle que además de la carga de los documentos de la consulte carge adicionalmente la referencia indicada, en nuestro caso el blog asociado, de esta manera, tras el siguiente request:

 

GET http://ravenserver/indexes/dynamic/Posts?query=&start=0&pageSize=128&aggregation=None&include=BlogId HTTP/1.1
Accept-Encoding: deflate,gzip
Content-Type: application/json; charset=utf-8

Podemos ver los siguientes resultados,  dónde además de los post correspondientes se incluyen los Blogs referenciados.

 

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
ETag: c171861e-497b-1bc0-a8c1-78c1da9f6070
Server: Microsoft-IIS/7.5
Raven-Server-Build: 573
X-AspNet-Version: 4.0.30319
Persistent-Auth: true
X-Powered-By: ASP.NET
Date: Mon, 19 Dec 2011 18:08:59 GMT
Content-Length: 1589

{«Results»:[{«BlogId»:»blogs/1″,»Title»:»The curegame, making off!»,»Body»:»This is the first post about thecuregame..»,»Comments»:[],»@metadata»:{«Raven-Entity-Name»:»Posts»,»Raven-Clr-Type»:»RavenQueryI.Post, RavenQueryI»,»@id»:»posts/1″,»Temp-Index-Score»:1.0,»Last-Modified»:»2011-12-19T17:06:05.6520000″,»@etag»:»00000000-0000-0500-0000-000000000005″,»Non-Authoritive-Information»:false}},{«BlogId»:»blogs/2″,»Title»:»The curegame, making off!»,»Body»:»For me, this is the first entry about…»,»Comments»:[],»@metadata»:{«Raven-Entity-Name»:»Posts»,»Raven-Clr-Type»:»RavenQueryI.Post, RavenQueryI»,»@id»:»posts/2″,»Temp-Index-Score»:1.0,»Last-Modified»:»2011-12-19T17:06:05.6520000″,»@etag»:»00000000-0000-0500-0000-000000000006″,»Non-Authoritive-Information»:false}}],»Includes»:[{«Title»:»O Bruxo mobile!»,»About»:»About this blog post…»,»@metadata»:{«Raven-Entity-Name»:»Blogs»,»Raven-Clr-Type»:»RavenQueryI.Blog, RavenQueryI»,»@id»:»blogs/1″,»Last-Modified»:»2011-12-19T17:03:56.0000000″,»@etag»:»00000000-0000-0400-0000-000000000002″,»Non-Authoritive-Information»:false}},{«Title»:»La masa, el ladrillo, la bota y el bocadillo!»,»About»:»About this blog post…»,»@metadata»:{«Raven-Entity-Name»:»Blogs»,»Raven-Clr-Type»:»RavenQueryI.Blog, RavenQueryI»,»@id»:»blogs/2″,»Last-Modified»:»2011-12-19T17:03:56.0000000″,»@etag»:»00000000-0000-0400-0000-000000000003″,»Non-Authoritive-Information»:false}}],»IsStale»:false,»IndexTimestamp»:»2011-12-19T17:06:05.6520000″,»TotalResults»:2,»SkippedResults»:0,»IndexName»:»Temp/Posts»,»IndexEtag»:»00000000-0000-0500-0000-000000000006″}

 

Saludos y hasta la siguiente entrada,

 

Unai

RavenDB(V) Actualizaciones, concurrencia, patch y otras hierbas

En esta quinta entrega de la serie, puede ver las anteriores entradas aquí, I,-IIIII y IV, vamos a hablar sobre actualizaciones y los mecanismos de gestión de la concurrencia que tenemos, por supuesto intentando bajar lo más posible, para mi estas entradas son también una fuente de aprendizaje. Empezaremos la casa por dónde debemos, o sea que lo primero que haremos será hacer una pequeña inserción de un documento más o menos complejo, ya que esto nos dará juego para alguna cosa interesante, basado en las siguientes clases que podemos ver a continuación:

 

Actualizaciones

 

 

En concreto, la inserción la haremos con los siguientes datos:

 


Bien, llega la hora de empezar a hablar de actualizaciones. Como ya dijimos en la primera  introducción a RavenDB, este ha sido creado apoyándose en patrones y experiencias provenientes de ORMs, muy especialmente de NHibernate, aunque algo también hay de otros….Uno de estos patrones recogidos de frameworks como NH o EF es UnitOfWork, el cual a estas alturas seguro que todos conocemos… Por lo tanto si un IDocumentSession es un UnitOfWork entonces la actualización de un documento debería de ser tan facil como lo siguiente:

 


 

Aunque por ahora seguimos sin hablar de la parte de consulta, fíjese en le método Load, el cual, como seguramente habrá deducido, nos permite obtener un documento en función de su clave, en este caso, estamos utilizando la convención por defecto ( HiLo ). Si miráramos con Fiddler este proceso la primera entrada sería precisamente esta carga:

 

GET http://ravendbserver/docs/posts/1 HTTP/1.1

Accept-Encoding: deflate,gzip

Content-Type: application/json; charset=utf-8

Host: portblackcode

Connection: Keep-Alive

 

Ahora, puesto que cualquier IDocumentSession es un UnitOfWork, cualquier cambio en cualquier objeto consultado, y por lo tanto trackeado por la infraestructura de RavenDB, es llevado al servidor una vez realizada la llamada a SaveChanges, todos los cambios,inserciones,etc de una sesión se hacen en batch para reducir roundtrips.

 

POST http://ravendbserver/bulk_docs HTTP/1.1

Accept-Encoding: deflate,gzip

Content-Type: application/json; charset=utf-8

Host: portblackcode

Content-Length: 513

Expect: 100-continue

[{"Key":"posts/1","Method":"PUT","Document":{"Title":"RavenDB(V): actualizaciones, concurrencia y otras hierbas!","Body":"En esta quinta entrega de la serie, puede ver las ….","Comments":[{"UserName":"preguntoncojonero","Text":"cualquier pregunta…."},{"UserName":"Unai","Text":"Pues efectivamente preguntoncojonero…"}]},"Metadata":{"Raven-Entity-Name":"Posts","Raven-Clr-Type":"sampleupdate.Post, sampleupdate","@etag":"00000000-0000-0200-0000-000000000001","Last-Modified":"/Date(1323811962000+0100)/"}}]

 

 

Fijese en la traza anterior como el cuerpo de nuestro POST contiene la información de la operación a realizar y el nuevo documento a establecer en RavenDB. Esto es una nota muy importante, es decir, es todo el documento que vamos a modificar no es la modificación en si. Esto, por ejemplo, tiene mucho impacto cuando estamos hablando de concurrencia, en concreto de gestión de concurrencia optimista, porque si dos personas incluyen un comentario en un post de forma concurrente podríamos tener problemas. Esto es una de las cosas que quizás mas impacta con respecto a lo que sabemos de modelos relaciones, ¿un problema de concurrencia en una inserción, porque en realidad es lo que estamos haciendo??…  Pues si, ahora no hay tablas y filas, hay un documento, una estructura compleja, que cambia, y cambia, en este caso, completamente, por lo tanto, si tenemos dos personas insertando un comentario sino manejamos el problema de concurrencia podríamos perder comentarios….

Concurrencia y ETag

Como podemos solventar el caso anterior, o más bien, como podemos enterarnos de un posible problema de concurrencia??? Bueno, a estas alturas ya todos sabemos que RavenDB es Restfull ¿verdad? y los que sabéis de http sabéis que existe el concepto de ETag para identificar un estado de un recurso en un momento dado. Pues bién, RavenDB utiliza este mismo concepto para gestionar la concurrencia de los documentos. Si volvemos a fijarnos en la trama de actulización veremos como junto con la metadata del documento tenemos una propiedad llamada etag, que en nuestro caso es la siguiente:

 

"@etag":"00000000-0000-0200-0000-000000000001"

 

Este valor es automaticamente modificado en el servidor cuando un documento se actualiza, por lo tanto, si tenemos un problema de concurrencia, lo que pasaría es que  el etag del documento en el servidor no se correspondería con el documento que nosotros enviamos, seria anterior, y por lo tanto podríamos suponer que estamos intentando actualizar algo que ya ha sido cambiado por otro proceso. Ok, ¿como se activa en RavenDB la comparación de los etag en los procesos de modificación?, pues muy sencillo, cualquier sesión, IDocumentSession, tiene un mecanismo para activar el chequeo de concurrencia de la siguiente forma:

 


 

Con este simple flag, podemos hacer la indicación de comprobación, en realidad lo que hace es que el documento que se envia incluye otra propiedad ETAG, a mayores de la que hay en metadata, para que el servidor sepa que tiene que hacer esta comprobación. Ahora, si ocurriera un problema de concurrencia, recibiríamos la siguiente respuesta del servidor:

 

HTTP/1.1 409 Conflict

Cache-Control: private

Content-Type: text/html

Server: Microsoft-IIS/7.5

Raven-Server-Build: 531

X-AspNet-Version: 4.0.30319

X-Powered-By: ASP.NET

Date: Tue, 13 Dec 2011 22:49:33 GMT

Content-Length: 219

{

  "Url": "/bulk_docs",

  "ActualETag": "00000000-0000-0300-0000-00000000000b",

  "ExpectedETag": "00000000-0000-0300-0000-00000000000a",

  "Error": "PUT attempted on document ‘posts/1’ using a non current etag"

}

 

Esta respuesta, provoca que se lance una excepción dentro del API de RavenDB, llamada ConcurrencyException que nos dará la información del problema que tenemos, y por lo tanto, refrescar, si así lo queremos nuestra operación.

 

Patching….

Bien, seguro que muchos estais pensando que, efectivamente, aunque la gestión de la concurrencia nos permite resolver nuestro problema, lo habitual ante la entrada de un post, es la coincidencia de muchos comentarios y por lo tanto tendremos muchos conflictos y demasiado trabajo para que estamos buscando hacer. Una alternativa para hacer esto es utilizar el concepto del verbo PATH en http. Hacer esto en RavenDB es realmente sencillo, como veremos a continuación, y nos permitirá, en el caso que estamos, crear nuevos comentarios a un post evitando problemas de concurrencia y mejorando el rendimiento ya que nos evitaremos tener que hacer un load de un documento.

 

Un patch request es, desde un punto de vista de código, tan sencillo como lo siguiente:

 


 

Si nos fijamos en el código anterior, un patch está representado por la clase PatchRequest, en la cual, podemos indicar el tipo de cambio a realizar, PatchCommandType nos ofrece comandos como ( Set, UnSet, Add, Modify,Rename …) la propiedad que modificaremos, en nuestro caso Comments y el cambio. En nuestro caso, el cambio es agregar un nuevo comentario, por lo tanto hacemos una transformación del objeto new Comment a Json por medio de la clase RavenJToken.

 

Una vez creado el request es suficiente con llamar a Patch definido en DatabaseCommands como podemos er a continuación:

 


 

Si prestamos un poco de atención vemos como el método Patch nos permite especificar la clave del documento sobre la que vamos a aplicar el cambio, en este caso posts/1. Si ejecutamos esto, y observamos fiddler veríamos algo similar a lo siguiente:

Untitled

POST http://ravendbserver/bulk_docs HTTP/1.1

Accept-Encoding: deflate,gzip

Content-Type: application/json; charset=utf-8

Host: portblackcode

Content-Length: 139

Expect: 100-continue

[{"Key":"posts/1","Method":"PATCH","Patches":[{"Type":"Add","Value":{"User":"Unai","Text":"mira señor pregunton…"},"Name":"Comments"}]}]

 

y una respuesta de confirmación de cambio:

HTTP/1.1 200 OK

Cache-Control: private

Content-Type: application/json; charset=utf-8

Server: Microsoft-IIS/7.5

Raven-Server-Build: 573

X-AspNet-Version: 4.0.30319

Persistent-Auth: true

X-Powered-By: ASP.NET

Date: Thu, 15 Dec 2011 19:40:37 GMT

Content-Length: 271

[{"Etag":"00000000-0000-0b00-0000-000000000001","Method":"PATCH","Key":"posts/1","Metadata":{"Raven-Entity-Name":"Posts","Raven-Clr-Type":"RavenUpdate.Post, RavenUpdate","Last-Modified":"2011-12-15T18:52:37.8760000Z","Non-Authoritive-Information":false,"@id":"posts/1"}}]

 

Otras hierbas

Este mismo mecanismo nos puede servir también para realización de un cambio en multiples documentos, para ello tenemos el método Batch en vez de Path, desde aquí os dejo a vosotros la tarea de investigación…

 

 

Saludos

Unai

RavenDB (IV) La identidad de los documentos

En las anteriores entradas hemos trabajado bastante con los documentos, desde una pequeña introducción hasta elementos más avanzados como el trabajo con la metadata y la serialización de los mismos. Sin embargo, hay una cosa sobre la que hemos pasado de puntillas, intencionalmente, pero que trataremos en detalle en esta entrada, y es el tratamiento de la identidad de los documentos. Los documentos, como cualqiuer otro elemento en una colección, tienen que tener un mecanismo que nos permita disitnguirlos de forma unívoca. RavenDB hace un gran aporte en este tema, al dejarnos muchas posiblidades para gestionar las identidades adhoc o delegar en RavenDB, y sus distintos mecanismos, la gestión de la misma.

 

La propiedad Id

La propiedad Id, sin especificar por ahora el tipo de la misma, juega un papel muy importante dentro de RavenDB ya que por defecto, disponemos de una convención que asocia a esta como la propiedad que marca la identidad de un documento. Por supuesto, estas convenciones pueden modificarse, para ello, solamente tendríamos que especificar los valores de los delegados FindIdentityPropertyNameFromEntityName y FindIdentityProperty.

 


Con la finalidad de ir mostrando las distintas alternativas que tenemos nos basaremos en una estructura de documento similar a la siguiente:

 


 

Fijese en una cuestión importante que suele escapar a aquellas personas que empiezan a trabajar con documentos y es que la entidad OrderDetail NO tiene identidad. Este forma parte del agregado y por lo tanto no puede vivir fuera de el, llevado a documentos podríamos decir que OrderDetail siempre es parte de un documento Order y por lo tanto la identidad no tiene sentido en el. Note que hablamos de identidad, nada impediría tener  algún tipo de secuencia para los detalles de los pedidos.

 

Sobre una instancia del documento anterior, si procedieramos a su inserción veríamos varios aspectos interesantes que comentaremos a continuación. Supongamos para ello el siguiente código de inserción.

 


Revisando la secuencia de peticiones con fiddler podemos observar las siguientes trazas, imagen de la derecha, que se corresponden con todo el trabajo necesario para realizar este almacenamiento del documento. La primera de las peticiones, la cual devuelve un 404 es una petición para obtener el valor del HiLo para la colección de Orders. Esto en realidad ya nos ha dicho mucho acerca de como se va a tratar la identidad. RavenDB dispone por defecto de una implementación del algorítmo HiLo que utiliza por defecto cuando disponemos de una propiedad Id. La tercera entrada de la traza se corresponde a la creción del primer valor de este HiLo, vemos el contenido de la traza en la siguientes lineas:

Untitled

En la cuarta traza de fiddler ya podemos ver la inserción del documento con una identidad formada por el nombre de la colección y el Lo calculado, en nuestro caso orders/1:

 

Esto está hecho así precisamente para garantizar que las claves sean human readables, como tenemos un API rest el documento anterior podría ser consultado de la siguiente manera:

 

http://ravendbserver/docs/orders/1

 

Seguro que no se sorprende si le digo que el algoritmo de HiLo puede ser customizado y/o sustituído por otro si así lo deseamos.  La convención DocumentKeyGenerator nos permite realizar esta tarea, como ejemplo demostrativo intentaremos establecer que el mecanismo de generación es el HiLo implementado en RavenDB pero cambiaremos la capacidad para ir de 100 en 100 en vez de 1024 en 1024.  Esto, es tan sencillo como se puede ver a continuación:

 

 


Bien, hasta ahora entonces podríamos resumir que si una entidad tiene una propiedad Id de tipo string entonces RavenDB se basa en la conbinación del nombre de la colección ( Raven-Entity-Name) y un algoritmo de HiLo para generarnos la identidad de los documentos de una forma sencilla y human readable. Vamos a ver un caso diferente,¿que pasa si la propiedad en vez de ser de tipo string  es de tipo int?. Nada, no pasa absolutamente nada, todo sigue igual, se sigue utilizando el mismo mecanismo de Hilo y la identidad es igual que para el caso anterior, aunque nosotros vemos en la entidad este valor como un entero sin el nombre de la colección en vez de como un string.

¿ Y si no quiero un HiLo y quiero un auto incremental?¿ Cómo hago entonces?? Bueno, en realidad esto es también muy sencillo de implementar en RavenDB solamente tendríamos que poner nuestra convención DocumentKeyGenerator como sigue:

 


Untitled2

 

¿Y si ponemos un Guid? En este caso RavenDB nos sigue ayudando y nos ofrece un generador automático para este tipo de propiedades, lógicamente la pega es que ahora este tipo de consultas por rest ya no son tan amigables como teníamos para los casos anteriores.

 

 

Untitled3

 

 

 

 

 

Por supuesto, otra posibilidad que tenemos es  nos especificar ninguna propiedad Id, y delegar por completo en la infraesetructura de RavenDB su generación, por supuesto, esta identidad no será visible en los documentos, por lo menos no directamente tendríamos que usar session.Advanced.GetDocumentId.

 

Las referencias

Durante todas las entradas hemos estado hablando acerca de como emparejábamos la definición de los documentos con la de agregados. Pues bien, los documentos, al igual que los agregados, pueden contener referencias a otros documentos. En nuestro caso, para ilustrarlo vamos a modificar la definición de nuestra entidad OrderDetail para agregar una referencia a un documento llamada Producto, para ello, incluiremos una propiedad llamada ProductId, fíjese en la convención por defecto.


 

Untitled4

 

Una vez modificado esta entidad, si hacemos el ejercicio de incluir un documento de tipo Producto y referenciamos su identidad dentro de un nuevo pedido podríamos ver como el Raven Management Studio es capaz de reconocer estas referencias y dejarnos navegar por las mismas. Por no adelantarnos no lo haremos, pero en realidad, esto también es relativamente importante para nuestro proceso de consulta puesto que, nos permitirá hacer un eager loading de aquellas referencias que tengamos en nuestros documentos, de tal forma que nos podamos ahorrar round trips con la base de datos y por lo tanto mejorar el rendimiento de nuestro sistema. Curiósamente el método de carga de referencias se llama Include, igual que en Entity Framework para las propiedades de navegación.

 

Bueno, creo que no nos hemos dejado nada con respecto al tratamiento de las entidades ¿verdad?.  En la siguiente entrada sobre RavenDB, veremos un poco, solamente un poco de consultas para tratar los temas de actualización, concurrencia optimista y patches…..

 

 

 

 

 

 

 

 

Saludos

Unai

Libro gratuíto y en español de Windows Azure

Por si a alguno se les ha escapado la noticia permitirme recordaros en mi blog que por fin tenemos el primer libro gratuito y en Castellano sobre Windows Azure y Cloud Clomputing. Aunque tengo el honor de aparecer como autor, por ser fiel a la realidad, os puedo asegurar que mi trabajo, por desgracia, ha sido mínimo y que todo el esfuerzo y los grandes resultados han sido obra de Ibon Landa, Microsoft MVP de Windows Azure.

 

El libro, accesible en formato digita len Scribid lo tenéis disponible en tres entregas que paso a relatar:

 

Parte I – Introducción a Windows Azure

Parte II – Sistemas de almacenamiento, SQL Azure y Windows Azure Storage

Parte III – Windows Azure AppFabric

 

Espero que os guste, por supuesto, los comentarios siempre son bienvenidos…

 

Saludos

 

Unai

RavenDB(III) Sobre la metadata y la serialización de los documentos

En esta tercera de las entradas sobre RavenDB, si lo deseas puedes consultar las entariores aquí y aquí, intentaremos continuar el trabajo dónde lo dejamos, es decir, profundizando acerca del concepto y el trabajo con lo documentos. Como ya hemos podido ver, RavenDB utiliza una serie de convenciones para trasladar nuestros tipos .NET ( incluídos dinámicos y anónimos ) a un payload que enviar por HTTP. Este proceso tiene dos partes importantes, la primera es la de obtención y creación de la metadata y la segunda la obtención del contenido de la entidad que será almacenada como un documento en la base de datos. A lo largo de esta entrada veremos como se realizan ambas tareas y sobre todo como podemos cambiar o ampliar su funcionamiento.

 

La metadata

En la anterior entrega ya vimos como RavenDB dispone de una serie de convenciones que nos permitían decidir como extrar información de metadata, en concreto, vimos como disponemos de una convención llamada FindTypeTagName gracias a la cual podríamos modificar como se obtenía el valor de Raven-Entity-Name, propiedad de la metadata de un documento por defecto. A mayor de esta propiedad, RavenDB incluye en la metadata la información del tipo .NET que se ha utilizado para serializar el documento en la propiedad Raven-Clr-Type, el valor de esta propiedad en realidad es meramente informativo, es decir, se podría modificar el tipo ( nombre ,namespace etc ) y cargarse perfectamente después aunque el valor de Raven-Clr-Type ya no sea coincidente. Al igual que con Raven-Entity-Name la forma de obtener el tipo de un documento también se puede modificar facilmente, como para el caso anterior, en nuestro DocumentStore.Conventions tendremos el método FindClrTypeName al cual le podremos asignar el delegado que se ejecutará para obtener este valor. Un ejemplo de implementación podría ser el siguiente:

 


 

Por supuesto, ver o cambiar las propiedades actuales de la metadata no es lo único que nos puede interesar. Otra tarea importante es la de poder agregar nuevos elementos a esta metadata, bien para realizar alguna extensión de RavenDb, muchos bundles usan esto, o para poder posteriormente hacer algún índice de consulta que use esta información. Agregar información a la metadata de un documento se puede hacer de diferentes formas, iremos viendo estas en los siguientes puntos:

 

Metadata de forma explícita

 

Cualquier session de RavenDB pone a nuestra disposición un método para obtener y manipular la metadata de un documento. En concreto, el método GetMetadataFor que tenemos disponible en las operaciones de una sesión nos permite obtener un RavenJObject con esta información. En el siguiente tip de código podemos ver esto:

 


Estos objetos RavenJObject no son más que, en un principio, pares de clave/valor  sobre los que podemos interactuar de una forma sencilla como podemos ver en el siguiente tip de código, en el que incluímos una nueva propiedad de metadata.

Untitled


Por supuesto, si nos vamos a nuestro Raven Management Studio podremos ver como esta nueva propiedad de metadata forma parte de nuestro documento, tal y como nos muestra la imagen anterior.

 

Metadata con Listeners

 

RavenDB nos proporciona un mecanismo de listerners similar, en concepto no tanto en propósito, al que tenemos en NHibernate con los event listeners.  Para registrar un listener con RavenDB solamente tenemos que hacer uso del método RegisterListener que nos proporciona la clase DocumentStore, ojo que es explícitamente la implementación y no la interface IDocumentStore. Me imagino que esto es intencional para promocionar el registro y la inicialización en un punto, aunque lógicamente esto habría que preguntarselo al creador. Un listener, no es más que cualquier clase que implemente el contrato IDocumentConversionListener. En las siguientes lineas se puede ver la inicialización de un DocumentStore con un Listener y el esqueleto del mismo.

 


 

Visto esto, si quisiéramos hacer lo mismo que en el caso anterior sería tan simple como lo siguiente:

 


 

La serialización

 

Bien, llegados hasta aquí ya sabemos como se obtiene la metadata de cada uno de los documentos, a mayores, también sabemos incluso los mecanismos que tenemos para modificar esa metadata si queremos agregar información personalizada y, también, cuales son las convenciones que tenemos por defecto. Ahora llega la hora de entender cuales son los miembros de un tipo que se utilizan para formar los documentos de las entidades y, si es posible, como cambiar este comportamiento.  Para hacer este ejercicio vamos a empezar por jugar con una clase que contenga los elementos más habituales en una entidad, propiedades, fields, propiedades de solo lectura, etc etc… la entidad en cuestión es la siguiente:

 


Si hacemos el ejercicio de guardar una instancia de esta clase en realidad el documento contendría la siguiente información:

 

Es decir, RavenDB por defecto solamente incluye los elementos públicos de nuestras entidades, sean propiedades o fields y sean de solo lectura o no las propiedades. Aunque esto está bien, la verdad, es que en muchísimos escenarios no será correcto, necesario o incluso válido. En muchas ocasiones no queremos que algunas propiedades se serializen, como por ejemplo Values, o también que los fields privados sean parte del documento. Por suerte, RavenDB nos ofrece bastantes facilidades para lograr introducir todas nuestras necesidades en cuanto al diseño de los documentos.

 

La primera parte, la de ocultar alguna propiedad o, incluso, hacer que el nombre de la propiedad en el documento no se corresponda con la propiedad de la entidad puede hacerse fácilmente utilizando una serie de atributos, al igual que, por poner un simil, podemos hacer en MongoDB con BsonDocument, BsonElement etc. En realidad, estos atributos son los definidos por JSON.NET, dependencia de RavenDB para el tema de serialización/deserializacion JSON/.NET. En el siguiente tip de código podemos ver como ignorar la propiedad Values y modificar el nombre de nuestro field publico.

 


 

Con lo que el documento quedaría de la siguiente forma:

 

Bien, la primera parte ya la hemos tratado, ahora quizás queda en principio la más complicada y es, decirle a RavenDB como tiene que resolver estos contratos de entidad,  usar fields privados etc etc…. Por suerto, como siempre hasta ahora, tenemos un punto de trabajo. En RavenDB, la resolución de las entidades, que elementos formarán parte de los documentos se hace por medio de un contrato llamado IContractResolver.  La implementación por defecto que RavenDB nos ofrece es DefaultRavenContractResolver y la misma puede agregarse facilmente desde nuestro DocumentStore.

 

Con el fin de lograr nuestro objetivo vamos a hacer una nueva implementación de IContractResolver y, para ahorrarnos tiempo y trabajo la vamos a hacer partiendo de la implementación por defecto,DefaultContractResolver, ojo no es DefaultRavenContractResolver.. Asi, si queremos serializar miembros privados solamente tendríamos que hacer lo siguiente:

 


 

Bueno, creo que por ahora es bastante, hay que dejar para las siguientes entradas… ¿Preguntas?

 

Saludos

Unai

RavenDB (II)- Los documentos

Tal y como ya dijimos en la anterior entrada, RavenDB es una base de datos documental,  y esto, está llevado a su extremo. Una de las primeras cosas que me llamaron la atención de RavenDB es que en realidad, físicamente, en RavenDB, no hay otra cosa que documentos, es decir, al contrario que en otros sistemas del mismo tipo como MongoDB, no existen, físicamente por lo menos, el concepto de colecciones, más adelante veremos como en realidad el concepto de colección es lógico es decir, documentos que comparten un atributo de metadata.

Una  de las características principales que también nombramos es que Raven es RESTfull y por lo tanto podremos mediante la identificación de recursos y verbos HTTP acceder a los documentos almacenados. Según vayamos avanzando en nuestras entradas iremos viendo los verbos y el contenido de cada tipo de petición. Por ahora, con el fin de ir mostrando distintos ejemplos iremos a nuestro Raven Management Studio y utilizaremos la opción de crear un juego de documentos de ejemplo, tal y como podemos ver en la siguientes figura:

sampledata

Una vez creados los documentos de ejemplo, podremos observar como RavenDB pone a nuestra disposición un conjunto de recursos principales para almacenar los documentos, estos recursos, o database layout como se conoce en la documentación, son los siguientes:

http://[servidor-raven]/docs : Nos permite acceder a todos los documentos de la base de datos por defecto. Si, raven permite tener múltiples bases de datos, pero, sino hacemos el ejercicio de crearlas siempre tenemos la “Default Database” como punto de trabajo. Si haces esta petición utilizando IE recuerda que este no entiende el application/json y por lo tanto seguramente te pida descargar un archivo (para evitarlo se puede tocar una entrada de registro). Si miramos el resultado de esta petición con Fiddler veríamos algo como lo siguiente:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
Raven-Server-Build: 531
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 01 Dec 2011 18:47:28 GMT
Content-Length: 12447

[{"AlbumArtUrl":"/Content/Images/placeholder.gif","Artist":{"Id":"artists/113","Name":"Os Paralamas Do Sucesso"},"Genre":{"Id":"genres/7","Name":"Latin"},"Price":8.99,"Title":"Arquivo II","CountSold":1,"@metadata":{"Raven-Entity-Name":"Albums","Raven-Clr-Type":"MvcMusicStore.Models.Album, Mvc Music Store","Content-Type":"application/json; charset=utf-8","Last-Modified":"2011-12-01T18:38:30.4410000+01:00","@id":"albums/626","@etag":"00000000-0000-0100-0000-000000000100","Non-Authoritive-Information":false}},{….}]

 

Fíjese la estructura para el primer documento devuelto, se ha omitido toda la salida por simplicidad, por el servidor, la cual está formada por una serie de propiedades principales, el Id, la metadata, detalles del documento, ultima modificación, un etag etc  y la propia información del mismo, el cuerpo o los datos. Más grafícamente, el documento en cuestión seria tal y como se puede ver en las siguientes imagenes extraidas de RavenDB Management Studio.

 

23

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Si se fija en la imagen de la derecha, dentro de la metadata del documento, tenemos una propiedad llamada Raven-Entity-Name.  Es esta propiedad la que nos permite indicar que un documento se categoriza en una determinada colección, en nuestro caso Albums, recuerde que unos párrafos atrás deciamos que físicamente no existe el concepto de colección, aquí tiene la respuesta de cómo hace el Management Studio para categorizarnos esta información.

 

http://[servidor-raven]/indexes: En este recurso se alamacenarán los documentos referidos a los indices, estáticos o dinámicos, que tengamos en RavenDb. Por ahora no vamos a hablar sobre esto, intentaremoms ir paso a paso y lo tocaremos en su profundidad cuando hablemos sobre consultas.

 

http://[servidor-raven]/static : Recurso dedicado a los attachements, al igual que los índices lo veremos en cuanto toque.image_thumb_1

 

¿Como se diseña un documento?

Si es la primera vez que usted trabaja con una base de datos documental seguramente empezará pagando el peaje del mundo relacional, una tendencia en la que todos hemos caido la primera vez que nos enfrentamos a esta otra forma de trabajar. Generalmente, el primer error que cometemos es la tendencia a crear documentos como filas de una tabla, es decir elementos planos de datos. Poco a poco, nos damos cuenta que esos documentos pueden contener otros documentos, de hecho deberían.  Una buena forma de pensar en estos documentos es igualando los limites del mismo al concepto que tenemos en DDD para definir los agregados, Aggreagte Root, de un modelo de dominio:

“Definition: A cluster of associated objects that are treated as a unit for the purpose of data changes. External references are restricted to one member of the Aggregate, designated as the root. A set of consistency rules applies within the Aggregate’s boundaries.”

De hecho, hay alguna característica importante de RavenDB que hacen que este patrón, Aggregate, sea mucho más sencillo de implementar, mucho más que utilizando ORM’s como EF o NH. Hablaremos un poco más adelante sobre esto.

Por supuesto, un documento puede referenciar a otros documentos, de igual forma que Eric Evans en la anotación sobre Agregados comenta:

“Choose one Entity to be the root of each Aggregate, and control all access to the objects inside the boundary through the root. Allow external objects to hold references to root only”

Cuando veamos el trabajo en la parte cliente nos pararemos, y veremos con detalle, estos puntos que acabamos de comentar, como mantener los invariantes de nuestros agregados y, como se realizan las referencias entre documento y como las mismas son manejadas en RavenDB.

 

 

Vale… pero empezamos ya a trabajar!!

 

Seguro que llegados hasta aquí ya todos tenemos ganas de ponernos con lo nuestro, que es .NET, por lo tanto, empezaremos a ver como que son los documenetos en el mundo .NET y como podemos trabajar con ellos. Lógicamente utilizaremos el API cliente y por lo tanto algunos elementos sobre los que no hemos hablado, aunque mencionaremos su propósito, no se preocupe si no se explican en detalle porque en futuras entradas seguro que se tratan de la forma debida. Por ahora, trabajaremos, por simplificar, con una aplicación de consola a la que le agregaremos las referencias de RavenDB de cliente, recuerde que en los directorios de instalación teníamos una carpeta llamada Client, si trabaja en .NET 3.5 lógicamente debe de usar la carpeta Client3.5.

 

Ahora que ya tenemos un proyecto con las referencias correspondientes vamos a empezar a crear un ejemplo sencillo. Para RavenDB, un documento no es más que una clase .NET o un tipo dinámico cualquiera, por ejemplo, los siguientes elementos son documentos correctos para RavenDB.

2

1

 

 

 

 

 

 

 

 

 

 

Para empezar a investigar que es lo que se extrae de estos documentos, vamos a escribir la siguientes lineas de código, las cuales, nos permitirán realizar un guardado en la base de datos de estos documentos. Si tiene experiencia con NHibernate, EF seguramente le resulte bastante similar este código al de cualquier guardado con NH/EF. Permitame recordarle que por ahora no nos pararemos demasiado en el código, lo haremos más adelante, permítame ir centrándonos primero en los documentos.

 


Si revisáramos con una traza de fiddler el contenido de la petición anterior veríamos algo similar a lo siguiente (en realidad también hay otra serie de operaciones referidas al calculo de la idéntidad de este documento, que serán exáminadas en futuras entradas):

 

POST http://ravendbserver/bulk_docs HTTP/1.1

Accept-Encoding: deflate,gzip

Content-Type: application/json; charset=utf-8

Host: portblackcode

Content-Length: 270

Expect: 100-continue

[{"Key":"orders/2049","Method":"PUT","Document":{"OrderDate":"2011-12-05T10:55:03.7964347+01:00","Address":{"City":"the city","ZipCode":"the zip code","AddressLine1":"the address line"}},"Metadata":{"Raven-Entity-Name":"Orders","Raven-Clr-Type":"RavenI.Order, RavenI"}}]

 

De esto pueden estraerse algunas conclusiones o elementos que deberemos de examinar en más detalle.

  • Las operaciones de inserción de documentos se hacen sobre un RequestReponder que trabaja en la URI http://ravendbserver/bulk_docs con el verbo POST.
  • La información de metadata se extrae de la información del tipo que estamos incluyendo, así por ejemplo el nombre de la propiedad Raven-Entity-Name que nos permite calificar a nuestra colección es una pluralización de la entidad. Esta pluralización está marcada por el tipo Raven.Client.Util.Inflector, aunque como todo el RavenDB, podemos modificarla escribiendo nuestra propia convención de pluralización y asignándola como podemos ver a continuación:

  • Al documentose le ha asignado una clave de forma automática  “orders/2049” que tendremos que investigar como y que mecanismo se ha utilizado para generarla. Por lo tanto, para consultar este documento podría hacer lo siguiente http://ravendbserver/docs/orders/2049

 

Si ahora, en vez de utilizar order, utilizamos un tipo anónimo ( igual con lo dinámicos) como el que vimos en las imagenes anteriores tendríamos una payload hacia el servidor como el siguiente:

 

POST http://ravendbserver/bulk_docs HTTP/1.1

Accept-Encoding: deflate,gzip

Content-Type: application/json; charset=utf-8

Host: portblackcode

Content-Length: 239

Expect: 100-continue

[{"Key":null,"Method":"PUT","Document":{"OrderDate":"2011-12-05T11:17:02.5348622+01:00","Address":"the shipping address"},"Metadata":{"Raven-Clr-Type":"<>f__AnonymousType0`2[[System.DateTime, mscorlib][System.String, mscorlib]], RavenI"}}]

 

Y de esta información podemos extraer las siguientes conclusiones:

 

  • Los documentos representados como un tipo anónimo no son categorizados en ninguna colección.
  • Por defecto su clave es null y por lo tanto se delega en el servidor la creación de una, RavenDB utiliza un Guid para representar la clave de este documento, así por ejemplo la consulta del mismo se podría hacer de la siguiente forma http://ravendbserver/docs/69851d86-58bf-4915-a6c8-2c9b5ae991f7

 

 

Bueno, hasta aquí llegamos con la segunda entrada, en la siguiente, veremos como hace RavenDB para decidir que elementos de una entidad son utilizados como parte de la información de un documento, propiedades publicas o no, fields publicos o no etc etc etc.. a mayores también hablaremos acerca de la identidad y del trabajo con la metadata.

 

Saludos

Unai

RavenDB (I)–Empezamos….

 logo-ravendb

 

Los que tenéis la suerte o desgracia de sufrir  mi blog, sabréis lo que me gusta Mongo DB, de hecho, repasando las entradas de mi blog me he dado cuenta de que  ya son unas cuantas, sumado esto a los diferentes eventos en los que he tenido la suerte de hablar de esta base documental y, a los proyectos en producción en los que he participado haciendo uso de esta base de datos, os podéis dar una buna idea del aprecio  y cariño que le tengo a esta NoSQL.

No obstante, hace ya bastante tiempo que tenía ganas de experimentar, de forma seria, RavenDB, cada vez que podía, restando a las ínfimas horas no ocupadas de las que dispongo, me pasaba por el sitio de esta base de datos y revisaba la documentación, buscando como avanzaba el proyecto y las multiples y diferentes características que se iban añadiendo poco a poco, así como el  impacto que el producto estaba teniendo en la comunidad. No voy a negar que en un principio siempre tenía la tentación de buscar comparaciones, sería mentir, puesto que en cierto grado es normal, cuando vas probar un producto, y lo que no es un producto :-), siempre lo comparas con lo que ya conoces. MongoDB hace esto así, entonces, ¿como lo hace Raven DB? … eran de las primeras cosas que venían a la cabeza. Pero, poco a poco, eso se ha ido perdiendo,diluyendo más bien….esto es debido a que, básicamente, creo que sinceramente no hay comparación posible, por lo menos no en el mundo .NET y menos aún en ciertos escenarios, por lo tanto, en esta entrada y en las siguientes de la serie no vais a encontrar comparaciones, si acaso ligeras referencias de términos o elementos que hacen a RavenDB diferente. 

 Lógicamente lo primero es lo primero, en esta entrada empezaremos por los conceptos más básicos, sitio del producto, descarga y puesta en marcha etc.., según avancemos en las diferentes entradas iremos  ampliando la información y nos meteremos en harina, que es dónde nos gusta estar:

RavenDB .NET

El sitio principal del proyecto es www.ravendb.net , por ahora, es un sitio aún no demasiado bien cuidado, pero están preparando un nuevo portal mucho mejor organizado, con mejor presencia y sobre todo mucho más contenido que el actual, este nuevo portal se puede consultar en http://beta.ravendb.net . Nada más entrar al sitio podemos ver las principales características de RavenDB:

  • Base de datos documentalimage_4
  • Transaccional
  • RestFull
  • Scalable
  • Schema Free
  • Multi-Level caching
  • Extensible

 

 

 

Para serle sincero, desde el principio fue una sorpresa ver como en este sitio, y más aún en el beta, hay una gran cantidad de documentación, tutoriales, FAQ,etc etc que podemos consultar,y, si aún tenemos preguntas no respondidas podriamos ir al  propio grupo de soporte para RavenDb.

  

Despliegue

Untitled

Una vez que hemos descargado una build de RavenDB, podéis acceder a las fuentes y últimos binarios aquí, simplemente tenemos que descomprimir el contenido de la descarga y nos encontraremos con una estructura de directorios similar a la que podéis ver en la imagen presente a la izquierda. En cada uno de los directorios podemos encontrar diferentes elementos, la información completa de su contenido la teneis disponible en el correspondiente fichero readme.txt. Por ahora solamente nos vamos a fijar en las carpetas Server y Web que son las que nos permitirán desplegar un servidor de Raven DB, de varias formas diferentes.

Para el primer ejemplo de despliegue nos iremos a la carpeta Server, en esta carpeta encontraremos, a mayores del conjunto de librerías necesarias, un ejecutable llamado Raven.Server.exe . Este ejecutable representa un servidor de Raven, el cual podría ser levantado simplemente con la ejecución de la siguiente linea de comandos:

Raven.Server.exe –debug –browser

 Es recomandable ver la ayuda de la linea de comandos para ver las diferentes opciones que tenemos disponibles. En este sentido me ha recordado mucho a MongoDB, el proceso es similar a levantar un mongod en MongoDB y, además, comparte tambien con esta bd la opción –install para poner Raven DB como un Servicio Windows.

El fichero de configuración del proceso Raven.Server.exe.config contiene algunos parámetros de configuración importantes, aunque no todos como veremos durante las siguientes entradas.

     <add key=«Raven/Port» value=«*»/>
    <add key=«Raven/DataDir» value=«~Data»/>
    <add key=«Raven/AnonymousAccess» value=«Get»/>

Como se habrá dado cuenta con estos tres parámetros podemos configurar el puerto en el que escuchará el HttpListener asociado a RavenDB, la carpeta que se utilizará para almacenar toda la información referida al servidor de Raven ( datos, indices etc…) y los permisos que por defecto tendrá un usuario anónimo, estando este por defecto a Get.

 

Si no queremos hacer el despliegue de Raven como un Servicio Windows, podemos optar por un despliegue de un sitio en Internet Information Server. Para ello tenemos el directorio Web, el cual, si se fija lo único que contiene es, además del directorio bin con las dependencias necesarias, el archivo de configuración del sitio con un handler para especificar el manejo de los request, en concreo la factoria de los RequestResponders, algo sobre lo que hablaremos cuando veamos la parte de extensibilidad, simple ¿verdad?

 

Raven Db Manastudiogement Studio

Tanto si levantamos el servidor como una consola, un servicio windows o un sitio de IIS en todos tendremos disponible una aplicación Silverlight, si, has oído bien, llamada Raven Db Management Studio que, como su propio nombre indica, es una pequeña herramienta de administración de las diferentes bases de datos que tengamos en el servidor.

Por ahora no vamos a entrar en más consideraciones, aún hay muchas cosas que explicar, según avancemos en las diferentes entradas iremos viendo esta herramienta y el porque de algunas cosas. Por ahora, con tener el servidor levantado y preparado para funcionar es suficiente.

Un detalle importante, que a mi no me ha resultado demasiado sencillo encontrar en la documentación es que el sitio de IIS tiene que tener habilitada la integración con Windows. Recurede que en la settings de configuración se podía restringir el acceso anónimo por lo tanto,  sino queremos trabajar en modo anónimo tenemos que asegurarnos que cumplimos esto.

Bueno, por ahora lo dejaremos aquí, seguiremos en las siguientes entradas, que no se retrasarán mucho, lo prometo.

 

Saludos

Unai