Categorías del Tutorial

icono_twiter
Alejandro Pérez García

Alejandro es socio fundador de Autentia y nuestro experto en J2EE, Linux y optimización de aplicaciones empresariales.

Ingeniero en Informática y Certified ScrumMaster

Si te gusta lo que ves, puedes contratarle para darte ayuda con soporte experto, impartir cursos presenciales en tu empresa o para que realicemos tus proyectos como factoría (Madrid).
Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación.

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2008-06-13

Tutorial visitado 9.558 veces Descargar en PDF
Toda la potencia de un buscador como Google en tu base de datos, gracias a Hibernate Search

Toda la potencia de un buscador como Google en tu base de datos, gracias a Hibernate Search

Creación: 13-06-2008



Índice de contenidos

1. Introducción
2. Entorno
3. Configuración del entorno de desarrollo
4. Configuración de Hibernate Search
5. Construyendo un POJO que sea "indexable"
6. Como hacer consultas en nuestros índices
7. Recursos
8. Conclusiones
9. Sobre el autor


1. Introducción

Sí, has oído bien, en este tutorial vamos a ver una introducción de como montar un sistema para poder hacer búsquedas textuales tipo Google sobre la información que tenemos almacenada en nuestra base de datos.

Para ello vamos a utilizar Hibernate Search (http://www.hibernate.org/410.html). Este proyecto hace algún tiempo que vio luz, y lo que hace es combinar la potencia de Hibernate (http://www.hibernate.org) para la gestión de bases de datos, con la potencia de Apache Lucene (http://lucene.apache.org/java/docs/index.html) como motor de búsquedas textuales basado en índices inversos.

Gracias a Hiberante Search conseguimos unificar estas dos grandes herramientas unificando manejo e interfaces. Y resulta trivial dotar de esta funcionalidad a nuestras aplicaciones.

Tal como nos cuenta la gente de Hibernate Search en su página principal, algunas de las principales ventajas son:

  • Solventa el problema del desajuste de estructuras entre Hibernate y Lucene. Es decir, se encarga de hacer la traducción entre nuestros objetos y los índices de Lucene.

  • Acaba con los problemas de duplicidad, manejando los índices, manteniendo sincronizados los cambios con la base de datos, y optimizando el acceso al índice, de forma transparente para el desarrollador.

  • Unifica los APIs, de manera que se pueden hacer búsquedas en el índice y recuperar los objetos pertinentes como si se tratara de una consulta normal de Hiberante (además siempre tenemos la opción de acceder directamente al API Lucene, si fuera necesario).

Pero bueno, empecemos ya que me estoy poniendo nervioso ;)



2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Asus G1 (Core 2 Duo a 2.1 GHz, 2048 MB RAM, 120 GB HD).

  • Nvidia GEFORCE GO 7700

  • Sistema Operativo: GNU / Linux, Debian (unstable), Kernel 2.6.24, KDE 3.5

  • Java Sun 1.6.0_06

  • Hibernate 3.2.6.ga

  • Hibernate Search 3.0.1 GA

  • JUnit 4.4



3. Configuración del entorno de desarrollo

La configuración del entorno, como siempre, la vamos a hacer con Maven. De esta forma resulta tan sencillo como añadir a nuestro pom.xml las siguientes líneas:

	...
	<repositories>
		...
		<repository>
			<id>repository.jboss.org</id>
			<name>JBoss Maven Repository</name>
			<url>http://repository.jboss.org/maven2</url>
			<layout>default</layout>
		</repository>
		...
	</repositories>

	<dependencies>
		...
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate</artifactId>
			<version>3.2.6.ga</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-search</artifactId>
			<version>3.0.1.GA</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-annotations</artifactId>
			<version>3.3.1.GA</version>
		</dependency>
		...
	<dependencies>
	...



4. Configuración de Hibernate Search

Hibernate Search ya viene configurado para poder trabajar en la mayoría de los casos. Por esto para empezar a trabajar tan sólo tendremos que añadir a nuestro hibernate.cfg.xml las siguientes líneas:

<property name="hibernate.search.default.directory_provider">org.hibernate.search.store.RAMDirectoryProvider</property>
<!-- <property name="hibernate.search.default.directory_provider">org.hibernate.search.store.FSDirectoryProvider</property>-->
<!-- <property name="hibernate.search.default.indexBase">/tmp/autentia-hibernate-search/indexes</property>-->

En la línea 1 le estoy diciendo que quiero que me guarde los índices en memoria. Es decir, al terminar la aplicación se perderán los índices. Esto no suele ser el comportamiento deseado, pero para hacer test automáticos nos va a venir muy bien :)

En la línea 2 y 3 os pongo comentado como configuramos Hibernate Search para que mantenga los índices en el disco duro. En concreto en la línea 3 es donde le decimos la ruta en el disco donde se guardarán los índices. Os recomiendo que sea una ruta fuera de vuestro servidor de aplicaciones, y deberíais hacer copias de seguridad de ella.

Si guardáis los índices en disco siempre podéis explorarlos y jugar con ellos, gracias a la herramienta Luke (http://www.getopt.org/luke/). Os dejo un pantallazo para que os hagáis una idea:

Por supuesto que hay un montón de cosas más que podemos configurar, tanto de Hibernate Search como de Lucene. Pero entrar en esto escapa de la intención de este tutorial.

Para saber más al respecto os recomiendo especialmente la documentación de Hibernate Search (http://www.hibernate.org/hib_docs/search/reference/en/html_single/) que es bastante clarificadora y es bastante corta (te la lees en un ratito). Y por supuesto la documentación de Luece para entender bien todos los conceptos, y exprimirlo al máximo (http://lucene.apache.org/java/2_3_2/).



5. Construyendo un POJO que sea "indexable"

Ahora vamos a crear un POJO que el sistema sea capaz de indexar. Usaremos anotaciones y nos queda algo tan sencillo como:

@Entity
@Indexed
public class Book {

	@Id
	@GeneratedValue
	@DocumentId
	private Integer id;

	@Field
	private String title;

	@Field
	private String summary;

	@Field
	@DateBridge(resolution = Resolution.DAY)
	@Temporal(TemporalType.TIMESTAMP)
	private Date publicationDate;

	...
	// Resto de constructores, métodos, getter y setters, etc
	...

Vamos a comentar exclusivamente las anotaciones de Hiberante Search:

  • Línea 2, @Indexed: Esta anotación indica que este POJO debe ser indexado por Lucene cada vez que hagamos una operación con Hibernate. Es decir, al persistir, actualizar o borrar el POJO, Hibernate Search se encargará de mantener actualizado y sincronizado el índice de Lucene.

  • Línea 7, @DocumentId: Indicamos que atributo será el identificador para la indexación. Esto es un tema de diseño de Hibernate Search, y es obligatorio.

  • Línea 9, @Field: Hibernate Searcho sólo guardará en el índice los atributos que tengamos marcados con esta anotación. Por defecto esta anotación indexa el atributo "tokenizado" (es decir, separado en palabras), y no guarda el contenido del atributo en el índice. Esto último significa que para ver el contenido del atributo Hibernate Search tendrá que hacer una búsqueda en la base de datos (esto suele ser lo habitual y solo guardaremos el contenido en el índice si queremos hacer proyecciones).

  • Línea 16, @DateBridge: Los índices de Lucene sólo guardan texto (por eso son búsquedas textuales ;), lo que hace Lucene es guardar la representación como texto de los atributos de nuestra clase. Todo esto es configurable, por ejemplo en el caso de las fechas, gracias a esta anotación, podemos decirle que parte de la fecha queremos indexar en el índice, por defecto indexará toda la precisión del Date (hasta segundo y milisegundos), pero esto no es demasiado útil, así que le podemos marcar cuanta precisión queremos indexar. En el índice le estamos indicando que sólo guarde hasta el día, este incluido. Hay que tener en cuenta que para buscar estas fechas el texto de la consulta lo tendremos que expresar en el formato yyyyMMddHHmmssSSS, en tiempo GMT.

Os recuerdo que para saber más sobre estas anotaciones, sus parámetros, posibles configuraciones, ... lo mejor es acudir a la configuración de Hibernate Search.

Pues ya está señores, ya hemos terminado. Fácil ¿verdad? Ahora vamos a ver como podemos hacer algunas consultas.



6. Como hacer consultas en nuestros índices

Imaginemos que tenemos un Dao implementado con Spring (en los recursos podéis acceder a todo el código). Podríamos tener el siguiente método para hacer búsquedas textuales sobre una entidad concreta:

@Transactional(readOnly = true)
public <T> List<T> findByFullText(Class<T> entityClass, String[] entityFields, String textToFind) {
	final FullTextSession fullTextSession = Search.createFullTextSession(getSession());
	final MultiFieldQueryParser parser = new MultiFieldQueryParser(entityFields, new StandardAnalyzer());
	final org.apache.lucene.search.Query luceneQuery;
	try {
		luceneQuery = parser.parse(textToFind);
	} catch (ParseException e) {
		log.error("Cannot parse [" + textToFind + "] to a full text query", e);
		return new ArrayList<T>(0);
	}
	final Query query = fullTextSession.createFullTextQuery(luceneQuery, entityClass);
	return query.list();
}

  • Línea 3: Creamos una sesión para hacer búsquedas textuales. Esta sesión se hace en base a la sesión de Hibernate.

  • Línea 4 a 11: Preparamos la consulta de Lucene, en función al texto que nos han pasado y los campos que queremos inspeccionar.

  • Línea 12: Lanzamos la consulta de Hibernate sobre la entidad que nos han dicho. No es obligatorio hacer consultas sobre entidades determinadas, podríamos lanzar consultar sobre cualquier entidad; pero es interesante, de cara al rendimiento, reducir las búsquedas siempre que sea posible, limitando las entidades sobre las que se tiene que lanzar la consulta.

Sobre las búsquedas, y en general aplicable para cualquier operación que hagamos con Hibernate, es muy recomendable hacerlas siempre en el marco de una transacción (el ejemplo lo conseguimos gracias a Spring con la anotación de la línea 1). De esta forma se optimiza el rendimiento.

Por ejemplo, si hacemos varias inserciones en la base de datos dentro de la misma transacción, el índice de Lucene sólo se actualizará con el commit de la transacción. Además, si algo sale mal, y se hacer rollback de la transacción, el índice no se llegará a tocar.



7. Recursos

Aquí os dejo todos los fuentes del ejemplo, donde podréis encontrar toda la configuración y los test completos donde podréis jugar haciendo diferentes búsquedas.



8. Conclusiones

Evidentemente hay mucho más de lo que hemos visto aquí. Pero sólo con lo que hemos tratado en este tutorial ya tenemos para hacer grandes cosas.

Además gracias a las anotaciones conseguimos desarrollar muy rápido, y el código es sencillo, limpio, y mantenible.

Espero que os haya gustado, a mi desde luego me parece una opción muy interesante.



9. Sobre el autor

Alejandro Pérez García, Ingeniero en Informática (especialidad de Ingeniería del Software)

Socio fundador de Autentia (Formación, Consultoría, Desarrollo de sistemas transaccionales)

mailto:alejandropg@autentia.com

Autentia Real Business Solutions S.L. - "Soporte a Desarrollo"

http://www.autentia.com



A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL:

Fecha publicación: 2008-06-14-09:10:13

Autor:

[Jose] Deseando tener un proyecto en el que implantarlo... :-D Muy interesante. Si señor. Salu2.