Il y a 15 ans -
Temps de lecture 10 minutes
Hands on Hibernate Search : Recherche full-text
Comme nous l’avions vu dans notre précédent billet (« Introduction à Hibernate Search (Googling your Persistent Domain Model)« ), Hibernate Search vise à réconcilier la recherche full-text et les modèles de persistance relationnels.
Pour ce faire, Hibernate Search se base sur Apache Lucene, un moteur d’indexation et de recherche full-text standalone très puissant et permet ainsi d’utiliser ses capacités dans le cadre d’une couche de mapping Hibernate.
Ce billet présente, au travers d’un exemple simple, les capacités de recherche full-text d’Hibernate Search.
L’exemple proposé permet l’indexation et la recherche de documents auxquels sont attachés des auteurs.
Vous retrouverez l’ensemble des sources présentées dans ce billet dans le repository SVN de Xebia France.
Structuration du projet et démarrage
Le projet utilise maven. Une fois les sources récupérées depuis notre dépot SVN, vous pouvez donc générer une configuration Eclipse à l’aide de la commande :
mvn eclipse:eclipse
Le projet déclare des dépendances vers :
- hibernate-search en version 3.0.0.ga.
- hibernate-annotations en version 3.3.0.ga.
- hibernate-entitymanager en version 3.3.1.ga.
- lucene-analyzers en version 2.3.1.
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search</artifactId> <version>3.0.0.ga</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> <version>3.3.0.ga</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>3.3.1.ga</version> </dependency> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analyzers</artifactId> <version>2.3.1</version> </dependency>
Le projet est structuré sur l’arborescence classique maven :
src\main\java
contient les sources du projet :
– Le packagefr.xebia.demo.hibernate.search.model
contient les entités persistées qui seront mappées dans un index Lucene.
– Le packagefr.xebia.demo.hibernate.search.analysis
contient un analyseur de langue anglaise.src\main\test
contient les sources des tests. Les tests « unitaires » ont ici été détournés pour faire la démonstration des capacités d’indexation et de recherche full-text d’Hibernate Search.
Mapping des entités sur l’index
Le mapping des entités dans un index Lucene se fait par annotation :
- L’annotation
@Indexed
déclare une classe comme indexable - L’annotation
@Analyzer
définit l’implémentation d’analyseur syntaxique à utiliser pour cette indexation :
@Entity @Indexed @Analyzer(impl = SimpleEnglishAnalyzer.class) @Table(name = DOCUMENT_TABLE_NAME) public class Document {
- L’annotation
@DocumentId
indique l’attribut à utiliser en tant qu’identifiant. Cet identifiant est utilisé par Hibernate Search afin d’assurer l’unicité des entités dans l’index :
@Id @DocumentId @Column(name = "id", nullable = false) public Long getId() { return this.id; }
- L’annotation
@Field
marque un attribut comme devant être indexé. La propriétéindex = Index.TOKENIZED
indique que le texte va être « tokenisé » via l’analyseur définit au niveau de la classe. La propriétéstore = Store.NO
indique que le texte de l’attribut ne sera pas stocké dans l’index.
@Column(name = "title") @Field(index = Index.TOKENIZED, store = Store.NO) public String getTitle() { return title; } @Column(name = "summary") @Field(index = Index.TOKENIZED, store = Store.NO) public String getSummary() { return summary; }
- Dans le cadre d’une association, l’annotation
@IndexedEmbedded
indique que l’objet associé doit être indexé dans l’index de l’entité racine. Dans notre exemple, cette méthode permet d’effectuer une recherche full-text sur les entités de typeDocument
basée sur les propriétés de son attributauthor
:
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.EAGER) @JoinColumn(name = "author_id") @Fetch(FetchMode.JOIN) @ForeignKey(name = "fk_document_author_id") @IndexedEmbedded public Author getAuthor() { return author; }
Analyseur et tokeniseur
La « tokenisation » consiste à découper un texte en mots. Ce découpage se fait en suivant les règles définies dans un analyseur. Hibernate Search propose par défaut l’utilisation de org.apache.lucene.analysis.standard.StandardAnalyzer
.
Il est possible de définir son propre analyseur en étendant la classe abstraite Analyzer
dont il faut implémenter la méthode
public abstract TokenStream tokenStream(String fieldName, Reader reader);
C’est ce que propose la classe SimpleEnglishAnalyzer
:
@Override public final TokenStream tokenStream(String fieldName, Reader reader) { if (fieldName == null) throw new IllegalArgumentException("fieldName must not be null"); if (reader == null) throw new IllegalArgumentException("reader must not be null"); TokenStream result = new StandardTokenizer(reader); result = new StandardFilter(result); result = new LowerCaseFilter(result); result = new StopFilter(result, stopTable); result = new PorterStemFilter(result); return result; }
Cette implémentation passe les mots en minuscule, supprime les mots courants, et utilise le PorterStemFilter
pour supprimer les terminaisons morphologiques communes.
Configuration Hibernate Search
La configuration d’Hibernate Search est câblée sur le système d’écoute d’événements. Elle est donc transparente avec Hibernate Annotations.
Dans le cadre de notre exemple, la configuration Hibernate est réalisée dans le set up de la classe HibernateSearchDemoTestCase
. La seule propriété de configuration que nous spécifions pour Hibernate Search est le répertoire de stockage des index :
@Override protected void setUp() throws Exception { AnnotationConfiguration cfg = new AnnotationConfiguration(); [...] cfg.setProperty("hibernate.search.default.indexBase", "target"); [...] }
Création et indexation des données de test
La création des données de test se fait dans la méthode populateDB
de la classe de test (HibernateSearchDemoTestCase
). L’indexation des données de test est faite de manière transparente au moment du commit de la transaction :
Transaction tx = this.testSession.beginTransaction(); Author bTate = new Author( "Bruce", "Tate", "Bruce A. Tate is a [...]"); [...] Document beyondJava = new Document(); beyondJava.setTitle("Beyond Java"); beyondJava .setSummary("In Beyond Java, [...]"); beyondJava.setAuthor(bTate); this.testSession.persist(beyondJava); [...] tx.commit();
Recherches full-text sur les entités
La recherche full-text de documents dans l’index se fait au travers de la méthode searchDocuments
de la classe de test (HibernateSearchDemoTestCase
) :
private List<Document> searchDocuments(String queryString) { FullTextSession searchSession = Search .createFullTextSession(this.testSession); Transaction tx = searchSession.beginTransaction(); MultiFieldQueryParser parser = new MultiFieldQueryParser(new String[] { "title", "summary", "author.lastName", "author.firstName", "author.resume" }, new SimpleEnglishAnalyzer()); FullTextQuery query; try { query = searchSession.createFullTextQuery( parser.parse(queryString), Document.class); } catch (ParseException pe) { logger.error("Error while parsing query.", pe); return new ArrayList<Document>(0); } List<Document> result = query.list(); tx.commit(); return result; }
Pour effectuer une recherche full-text, il faut :
- Créer une
FullTextSession
à partir de la session Hibernate. - Créer un parseur (
MultiFieldQueryParser
) en lui indiquant les champs sur lesquels la recherche doit avoir lieu et l’analyseur à utiliser pour tokeniser les chaînes de recherche (il est bien évidemment préférable d’utiliser le même que pour l’indexation des documents). - Créer une requête de recherche full-text à partir de la chaîne de recherche tokenisée.
- Exécuter la méthode
list
sur la requête.
Hibernate Search retourne alors une liste d’entités « managées ».
Les cas de test
La démonstration peut être lancée via maven avec la commande :
mvn test
Les cas de test proposés sont :
- Recherches simples (
"java"
,"father"
,"JAVA"
):
===================================================== Simple Test (query == "java"): Results list: Document 1: Beyond Java: > Summary: In Beyond Java, Bruce Tate, author of the Jolt Award-winning Better, Faster, Lighter Java, chronicles the rise of the most ... > Author: Bruce Tate (1) > Resume: Bruce A. Tate is a kayaker, mountain biker, and father of two. In his spare time, he is an independent consultant in Austin ... Document 2: From Java to Ruby: > Summary: If you're trying to adopt Ruby in your organization and need some help, this is the book for you. Based on a decision tree ... > Author: Bruce Tate (1) > Resume: Bruce A. Tate is a kayaker, mountain biker, and father of two. In his spare time, he is an independent consultant in Austin ... Document 4: AspectJ Cookbook: > Summary: This hands-on book shows readers why and how common Java development problems can be solved by using new Aspect-oriented pr ... > Author: Russell Miles (3) > Resume: Russell Miles is a software engineer for General Dynamics UK where he works with Java and Distributed Systems, although his ... ===================================================== ===================================================== Simple Test (query == "father"): Results list: Document 1: Beyond Java: > Summary: In Beyond Java, Bruce Tate, author of the Jolt Award-winning Better, Faster, Lighter Java, chronicles the rise of the most ... > Author: Bruce Tate (1) > Resume: Bruce A. Tate is a kayaker, mountain biker, and father of two. In his spare time, he is an independent consultant in Austin ... Document 2: From Java to Ruby: > Summary: If you're trying to adopt Ruby in your organization and need some help, this is the book for you. Based on a decision tree ... > Author: Bruce Tate (1) > Resume: Bruce A. Tate is a kayaker, mountain biker, and father of two. In his spare time, he is an independent consultant in Austin ... ===================================================== ===================================================== Simple Case Test (query == "JAVA"): Results list: Document 1: Beyond Java: > Summary: In Beyond Java, Bruce Tate, author of the Jolt Award-winning Better, Faster, Lighter Java, chronicles the rise of the most ... > Author: Bruce Tate (1) > Resume: Bruce A. Tate is a kayaker, mountain biker, and father of two. In his spare time, he is an independent consultant in Austin ... Document 2: From Java to Ruby: > Summary: If you're trying to adopt Ruby in your organization and need some help, this is the book for you. Based on a decision tree ... > Author: Bruce Tate (1) > Resume: Bruce A. Tate is a kayaker, mountain biker, and father of two. In his spare time, he is an independent consultant in Austin ... Document 4: AspectJ Cookbook: > Summary: This hands-on book shows readers why and how common Java development problems can be solved by using new Aspect-oriented pr ... > Author: Russell Miles (3) > Resume: Russell Miles is a software engineer for General Dynamics UK where he works with Java and Distributed Systems, although his ... =====================================================
- Recherche par approximation (
"jav~"
):
===================================================== Approximation Test (query == "jav~"): Results list: Document 1: Beyond Java: > Summary: In Beyond Java, Bruce Tate, author of the Jolt Award-winning Better, Faster, Lighter Java, chronicles the rise of the most ... > Author: Bruce Tate (1) > Resume: Bruce A. Tate is a kayaker, mountain biker, and father of two. In his spare time, he is an independent consultant in Austin ... Document 2: From Java to Ruby: > Summary: If you're trying to adopt Ruby in your organization and need some help, this is the book for you. Based on a decision tree ... > Author: Bruce Tate (1) > Resume: Bruce A. Tate is a kayaker, mountain biker, and father of two. In his spare time, he is an independent consultant in Austin ... Document 4: AspectJ Cookbook: > Summary: This hands-on book shows readers why and how common Java development problems can be solved by using new Aspect-oriented pr ... > Author: Russell Miles (3) > Resume: Russell Miles is a software engineer for General Dynamics UK where he works with Java and Distributed Systems, although his ... =====================================================
- Recherche par proximité (
""engineer UK"~5"
):
===================================================== Proximity Test (query == ""engineer UK"~5"): Results list: Document 4: AspectJ Cookbook: > Summary: This hands-on book shows readers why and how common Java development problems can be solved by using new Aspect-oriented pr ... > Author: Russell Miles (3) > Resume: Russell Miles is a software engineer for General Dynamics UK where he works with Java and Distributed Systems, although his ... =====================================================
Commentaire
4 réponses pour " Hands on Hibernate Search : Recherche full-text "
Published by Michel CLAVERIE , Il y a 14 ans
Très interessant comme sujet mais il n’y a pas les sources sous :
« Vous retrouverez l’ensemble des sources présentées dans ce billet dans le repository SVN de Xebia France. »
y a t’il un moyen d’obtenir votre exemple ?
Merci.
Published by Christophe Heubès , Il y a 14 ans
Google code a l’air un peu capricieux pour le chargement de l’arborescence, mais les sources sont bien disponibles à cette url : http://code.google.com/p/xebia-france/source/browse/#svn/trunk/search/hibernate-search-demo.
Elles sont « checkoutables » via la commande :
svn checkout http://xebia-france.googlecode.com/svn/trunk/search/hibernate-search-demo/ xebia-france-read-only
Published by David Pilato , Il y a 13 ans
Bonjour,
Merci pour cet exemple très illustratif.
A noter que dans la repository http://repository.jboss.com/maven2/, la version hibernate search est 3.0.0.GA et non 3.0.0.ga
Cela peut poser problème pour ceux qui comme moi utilisent un proxy Archiva sous Linux…
David.