Etiquetas

, ,

Uno de los principales obstáculos que tienen los desarrolladores a la hora de utilizar Windows Azure Storage está en la limitación para realizar filtros de búsqueda.

Las tablas de Windows Azure Storage nos permiten realizar búsquedas de registros por PartitionKey y RowKey; y en función de las búsquedas, entre otros aspectos como la escalabilidad, diseñamos dichos índices. La problemática surge cuando requerimos buscar registros filtrando por alguna propiedad que contiene cierta información que no es parte de la información utilizada para construir el índice del PartitionKey o RowKey.

Para solucionar este problema tenemos varias alternativas, una de ellas es construir nosotros un índice que mantenga la información de las propiedades que queremos utilizar para búsquedas, el cual lo podemos almacenar tanto en SQL como en Storage. Esta alternativa tiene un costo de mantenimiento alto y también de construcción. Para evitar este camino podemos recurrir a la utilización de Lucene.NET y utilizarlo para indexar los registros de Storage.

Lucene.NET también tiene varias consideraciones y un determinado costo de mantenimiento. Este costo de mantenimiento se ve reflejado en las acciones que son necesarias para la actualización de los índices. En este caso no vamos a entrar en todos los detalles que son necesarios tener en cuenta para terminar teniendo algo 100% productivo, que por otro lado, cada proyecto tiene sus propias consideraciones.

Aquí veremos un simple ejemplo de como podemos utilizarlo para este fin. Podemos descargar el código fuente del ejemplo aquí.

En este ejemplo estamos utilizando al mismo tiempo AzureDirectory para implementar un cache de los índices de Lucene localmente en el disco.

Cabe aclarar que el siguiente código es solo a modo de ejemplo para que comencemos a utilizar Lucene.NET, el mismo no es una buena implementación para producción dado que debemos tener por lo menos las siguientes consideraciones:

  1. Existen dos índices, uno para escritura y otro para lectura. El índice de escritura es el que vamos a utilizar para insertar los nuevos registros y el de lectura es el que utilizaremos para realizar las búsquedas. Para tener un correcto funcionamiento es necesario actualizar frecuentemente el índice de lectura, dado que de otro modo no encontraremos los nuevos registros que se hayan insertado. Al mismo tiempo necesitamos realizar esta operación sin dar de baja al servicio de búsqueda, como esta actividad puede demorar debemos mantener un índice activo mientras preparamos la actualización en un nuevo índice de lectura para posteriormente hacer el cambio sin tener un impacto en el servicio de búsquedas de nuestra aplicación.
  2. Cada vez que se inserte un nuevo registro que requiera ser dado de alta en el índice debemos tener en cuenta que esta operación tiene un determinado costo. Por lo expuesto en el punto anterior, dicho registro no será visible automáticamente y por otro lado no queremos que el costo de indexación impacte en la performance de nuestra aplicación. Esta actividad debería ser procesada en otro proceso. Para solucionar esto podemos implementar la indexación en un Worker Role y mediante una cola notificar los nuevos registros que se han dado de alta. De esta forma minimizamos el costo de indexación, o bien podemos realizarlo en forma asíncrona, dentro del mismo rol pero en otro thread.
  3. Tener en cuenta que cada vez que eliminamos un registro debemos quitarlo del índice también para que no devuelva registros inexistentes.
  4. Al actualizar un campo de un registro que se encuentre dentro del índice debemos actualizar el mismo. Si se está actualizando alguna propiedad que no forma parte del índice no se debe realizar ninguna operación.

Al margen de estas aclaraciones de una implementación más seria, veremos un ejemplo muy simple de como podemos comenzar a utilizar Lucene.NET

 

image

El primer paso que realizaremos es la definición del directorio donde se almacenarán los índices. Utilizando Azuredirectory estaremos utilizando un cache local para mejorar el tiempo de respuesta de los mismos.

Posteriormente creamos el índice de escritura, donde daremos de alta los registros indicando los campos que serán utilizados para la búsqueda.

El método InsertSampleData genera una serie de registros con información de prueba y al mismo tiempo inserta dicha información en el índice. En este ejemplo estamos evitando la inserción completa de dicho registro en storage dado que este es un paso que ya debemos dominar para este ejercicio.

En este ejemplo cerramos el índice de escritura dado que no se insertarán más registros sobre Lucene.NET. En una implementación de producción, generalmente este índice debe permanecer abierto para dar de alta los registros nuevos que sean producto de nuestros casos de uso.

Posteriormente inicializamos el índice para búsquedas y por último ejecutamos una búsqueda utilizando como parámetro del texto a buscar “Lucene.NET”.

Código para la creación del índice de escritura:

image

Código que inserta los registros de prueba:

image

image 

Aquí debemos observar que dentro de un registro de Lucene no estamos insertando toda la información de nuestra entidad. Solo debemos insertar los campos que queremos que participen como filtro dentro de la búsqueda, en nuestro caso es solamente “Propiedad2” y por supuesto la información necesaria para recuperar posteriormente el registro completo. Como estamos utilizando Lucene.NET para indexar registros de storage debemos indicar el PartitionKey y el RowKey.

Y por último el código que utilizamos para la implementación de la búsqueda:

 

image

 

Podemos descargar el código fuente del ejemplo aquí.