Hibernate Search: la cerise sur le gâteau
Sondage
Vous utilisez Hibernate? oui
Vous utilisez les annotations pour définir vos méta données? oui
Vous ne connaissez pas Hibernate Search? honte à vous!
R.O.I. HB-Search
Il y a des frameworks qui proposent des ROI assez impressionnants, Hibernate Search en fait partie.
Comme vous l’avez probablement deviné Hibernate Search permet d’implémenter un moteur de recherche fulltext efficace. Il s’appuie sur Lucene, Hibernate et les annotations.
Lucene est une technologie java d’indexation et de recherche très mature, aboutie et efficace. L’intérêt d’HB Search réside en son intégration avec Hibernate, il en résulte une facilité de mise en œuvre impressionnante.
Nous sommes régulièrement confronté au problème d’implémentation de moteur recherche dans nos applications d’entreprise. Plusieurs soucis:
- niveau conception : nous sommes très forts pour proposer des formulaires de recherche ciblant toutes les données imaginables de nos modèles, allant parfois implémenter des formulaires de recherche comportant 36 champs. Le problème de ces formulaires étant leur inaccessibilité pour la ménagère de moins de 50 ans –> pour le grand public
- niveau pertinence: nous savons être pertinents et précis sur des numériques, des dates, des booléens mais lorsque l’on nous demandes de prendre en compte les fautes d’orthographes ou les synonymes sur les chaînes de caractères, on se retrouve généralement démunie
Avec HB Search vous pouvez proposer à vos clients, pour un coût moindre, une ouverture vers un moteur fulltext user-friendly (typiquement champ de formulaire unique « à la google »). Ils seront agréablement surpris et n’auront aucun mal à élargir le spectre des spécifications pour consolider ce moteur.
L’exemple
Imaginez une classe Produit avec diverses propriétés de type String comme le libellé et la marque ou libellePrincipal et libelleSecondaire.
Vous souhaitez que la recherche cible ces deux propriétés.
Ci-dessous l’entité annotée comme vous en avez l’habitude:
@Entity public class Produit { @Id private int codeProduit; private String libellePrincipal; private String libelleSecondaire; ... }
Et effectuer une recherche, par exemple, via HQL:
javax.persistence.Query q = em.createQuery( "select produit " + "from Produit produit " + "where produit.libellePrincipal = :param"); q.setParameter("param", "café"); List results = q.getResultList();
Méta données
Que faut-il ajouter pour que l’entité et ses 2 champs soient puissent être ciblées par le moteur fulltext?
@Entity @Indexed public class Produit { @Id @DocumentId private int codeProduit; @Field private String libellePrincipal; @Field private String libelleSecondaire; ... }
@org.hibernate.search.annotations.Indexed stipule que l’entité annotée peut être indexée. Grâce à cette annotation, l’intégration Lucene/Hibernate est activée.
Parmi tant d’autres fonctionnalités gérées, l’indexation automatique vous simplifie la vie: lorsque vous agissez sur une entité de ce type, l’index lucene est automatiquement géré.
org.hibernate.search.annotations.Field déclare qu’une propriété est indexée. L’annotation propose divers leviers pour définir comment la propriété est indexée. Pour le moment appliquons le paramétrage par défaut.
Plutôt facile non? Attardons nous maintenant à l’aspect API
API de recherche
Avant de commencer, notez que des APIs équivalentes existent pour la session Hibernate.
Ici, plusieurs étapes sont nécessaires. Il faut d’abord obtenir un EntityManager fulltext, puis créer une requête Lucene. Enfin, la création d’une requête de recherche JPA depuis la requête Lucene nous permettra de retomber sur une API familière et pratique pour manipuler les entités retournées par la recherche.
Voici ce que ça donne:
// expression littérale de la requête Lucene</pre> String searchQuery = "cafe~"; org.hibernate.search.jpa.FullTextEntityManager fullTextEm = Search.getFullTextEntityManager(entityManager); SearchFactory sf = fullTextEm.getSearchFactory(); // Construction d'un QueryParser, définition du champ par défaut // récupération de l'analyseur lié à l'entité org.apache.lucene.queryParser.QueryParser parser = new QueryParser( "libellePrincipal", sf.getAnalyzer( Produit.class ) ); // construction de la requête lucene org.apache.lucene.search.Query luceneQuery = parser.parse(searchQuery); // création de la requête JPA fulltext org.hibernate.search.jpa.FullTextQuery ftq = fullTextEm.createFullTextQuery(luceneQuery, Produit.class); // exécution de la requête List results = ftq.getResultList();
La subtilité ici réside en la recherche Lucene «~cafe ». Le tilde active une recherche par approximation. Ce type de recherche permet d’éviter les problèmes d’accent et de typo que l’on rencontre très souvent. De même si les utilisateurs saisissent des fautes d’orthographes, cette recherche s’en sortira facilement.
Bien plus de fonctionnalités
Cette article n’a pas l’ambition de couvrir toute la puissance d’Hibernate Search, simplement de démontrer la facilité et rapidité de mise en œuvre. L’exploitation d’un graph d’objet (et de ses associations) pour la recherche, la pondération de certains champs, la pertinence de la recherche sont possibles et faciles à utiliser.
Bien entendu, d’autres aspects doivent être pris en compte, notamment l’utilisation des analyseurs (approximation, phonétique, synonymes,…) et la gestion / maintenance des index.
Je vous recommande donc la lecture du guide de référence mais surtout du livre d’Emmanuel Bernard et John Griffin.
leave a comment