J’ai assisté à la présentation d’Hibernate Search par Emmanuel Bernard, après une présentation sur les Portlets 2.0 par Thomas Heute. Tous les deux de JBoss/Red Hat.

L’objectif est de nous présenter le fonctionnement d’Hibernate Search à travers des exemples de codes. Emmanuel assure la présentation avec un ordinateur de secours, son portable n’ayant pas supporté le voyage. Ouch ! Armé du package du bon speaker : un Mac + IDEA IntelliJ, il débute la présentation par une courte introduction afin de se présenter. Membre de l’équipe d’Hibernate, JCP Specification Leader de la JSR-303 « Bean Validation » et membre de l’expert group sur JPA 2.0. Il est l’auteur du livre « Hibernate Search In Action » chez Mannings. Enfin petite information, il revient vivre en France en octobre, ce qui permettra je l’espère de le voir lors des soirées des Java User Group en France.

Son objectif sera de nous expliquer en quoi consiste la recherche « full-text », ce qu’elle peut nous apporter, la magie des analyseurs, les différents algorithmes pour la recherche approximative, bref comment Hibernate Search fonctionne.

La recherche
Que ce soit un formulaire sur un site web ou autre, tôt ou tard une application souhaite offrir une fonction de recherche dans les entités d’une base de données. Hibernate Search vise à offrir une solution efficace et puissante, basée sur Lucene, afin de nous assister lors de l’écriture de cette fonctionnalité clé dans les applications.

Une recherche simple basée sur la syntaxe SQL est trop restrictive. Si vous tapez « car », comment extraire ce mot d’une colonne ? Certes il y a des requêtes comme « LIKE » mais celles-ci coûtent chères en terme de performance. Il explique que la base de données est le premier point de contention, qu’il est donc dommage de ne pas remonter cette fonction afin de la rendre plus puissante.

Par ailleurs, si vous tapez « car », est-ce que le moteur de recherche sera capable de vous montrer les entrées avec « vehicle » ? Et si vous faites une faute de frappe, est-ce que le moteur sera capable de vous monter un résultat ?

La sélection par SQL est donc très limitée.

Emmanuel enchaîne ensuite avec un argument : comment classer par pertinence les résultats ? De ces besoins est né l’idée d’Hibernate Search, offrir un moteur de recherche full-text à Hibernate.

Full Text Search
Pour répondre aux besoins exposés précédemment, la solution s’appelle « Full Text Search ». Elle est relativement simple et puissante. Le principe est de créer un Index des mots clés, en supprimant les mots de liaisons, afin d’identifier les occurences de chaque mot, et donc de créer de la pertinence. Ce principe est très performant puisque l’on ne fait pas d’accès à la base de données.

Il existe déjà différentes solutions, soit embarquées dans la base, soit dans des boîtiers externes sous la forme d’applicance, mais qui manquent de flexibilité, puisqu’en tant que développeur il sera difficile d’affiner la stratégie d’indexation.

Exemples de problèmes
Voici la mission d’Hibernate Search :
– Trouver le meilleur document, la meilleure entité selon son critère de choix.
– Classer par pertinence
– Utiliser un algorithme de similarité pour proposer à l’utilisateur des résultats similaires

Extraction
L’indexation s’effectue tout d’abord en découpant les phrases en mots, ce que l’on appelle l’extraction. A ce propos, la construction de l’index est gérée par Hibernate Search, elle s’effectue automatiquement.
L’extraction consiste tout d’abord à découper les phrases et à filtrer les mots communs pour ne garder que le sens.

Approximation
Ce système permet d’assister l’utilisateur en lui proposant des résultats très proches lorsqu’une recherche exacte ne retourne pas de résultat par exemple. Il est possible de dire à Hibernate Search que l’approximation est moins importante que la recherche exacte, de sorte que la liste des résultats ne soit pas polluée et reste pertinente. Ce poids lors de la recherche est configuré par le développeur, ce qui nous permet de régler finement le comportement du moteur. Les réponses exactes apparaissent en premier, puis les approximations.
La résolution d’approximation se fait avec des algos comme le calcul de distance Levenshtein. Tapez par exemple Hibrenate au lieu d’Hibernate, et vous verrez que le moteur détecte les résultats proches, en calculant la distance entre les lettres.

L’approche n-gram
Emmanuel présente ensuite une fonction plus avancée d’indexation, appelée n-gram. Le principe consiste à découper des mots en paquets de lettre. Par exemplte un tri-gram pour découper en paquet de 3 lettres un mot comme Hibernate donnerait : hib-ern-ate. Cela permet alors de créer des arbres d’indexation et de donner des plans de résultats encore plus pertinent.

Emmanuel montre un exemple où il tappe « poter ». les livres sur Harry Potter s’affichent, mais aussi un
« Capote » car le tri-gram « pot » a matché 🙂

Hibernate Search
je mettrai des photos en ligne un peu plus tard avec des exemples de code

Emmanuel prend ensuite l’exemple d’un Item pour nous présenter l’API.
On voit une annotation @Indexed placée sur un Entity, ce qui permet à Hibernate Search de savoir que cette entité doit être indexée. Par ailleurs il explique qu’Hibernate Search sait que lorsque l’objet est changé, il doit mettre à jour l’index Lucène. Il nous montre enfin comment configurer l’indexation afin de ne pas découper en n-gram par exemple certains champs, afin de conserver des codes ISIN.

La recherche s’effectue comme une requête HQL avec un entitymanager particulier, le FullTextEntityManager qui est une sous-classe de l’EntityManager d’Hibernate.

FullTextQuery q = entityManager.createFullTextQuery(luceneQuery, Item.class)

Pour construire sa requête Lucene, Hibernate Search laisse le soin au développeur de construire celle-ci, ce qui permet de régler finement le type de recherche. Il commence par faire une démonstration d’une recherche exacte, puis ensuite une démonstration d’une recherche avec un n-gram Analyzer.

Il est aussi possible de déclarer par exemple 2 stratégies d’indexation différentes, une indexation exacte et une indexation pour une recherche approximative. Vraiment intéressant de voir la possibilité de réglage d’Hibernate Search. La quantité d’annotations peut faire peur, mais le code est vraiment simple à lire. Quelqu’un demande d’ailleurs s’il serait possible de déclarer dans un fichier XML ce qui est déclaré ici sous forme d’Annotations.

Approximation phonetique
Il existe différents algos pour indexer de manière phonétique des mots:
– Soundex
– Metaphone (JRSKP)
– mostly for latin language
Emmanuel explique cependant que ce n’est pas le cas le plus courant dans la vraie vie.

La recherche par synonyme
L’ídée est de proposer à l’utilisateur des listes de résultats dont le domaine est proche sémantiquement, une voiture, une bagnole, un caisse si vous voulez. Pour cela il faut un dictionnaire, que l’on indexe. Cela crée de gros indexes, mais il est possible de construire des dictionnaires par référence. Dans tous les cas, il nous conseille de construire nous-mêmes nos bases de synonymes.

What’s the catch
Lucene étant relativement bas niveau, l’intégration avec un modèle, la création et la mise à jour de l’index sont donc des fonctions clés d’Hibernate Search. Celui-ci permet d’apporter facilement des fonctions de recherches très puissantes à une application. Et qui n’offrira pas finalement une fonctionnalité de recherche ?

Bonne présentation, détendue, claire, et intéressante. Et en plus avec un Mac et IDEA IntelliJ !