Des résumés ont déjà été fait sur le web, mais voici les quelques détails que j'ai retenu.

JDuchess

Avant la présentation d'Emmanuel, la communauté Duchess s'est présentée. Il s'agit de regrouper et de représenter les femmes travaillant dans le monde Java. L'initiative provient de Hollande et se développe petit à petit en France, au Brésil et dans le monde entier. Donc mesdemoiselles, si vous codez en Java, ce site vous est dédié.

Hibernate Search

La première partie de la présentation d'Emmanuel est dédié à Hibernate Search (HS). HS fournit une infrastructure pour gérer les objets Hibernate autour du moteur de recherche Lucene. Dans 80% des cas, il ne restera pour le développeur qu'à configurer la recherche full-text sur ses objets.

Legacy solutions

Emmanuel a abordé les techniques "legacy" de recherche sur base de données :

  • recherche SQL "%mot%" : perfs pourries (tablescan), pire si plusieurs mots à chercher ou plusieurs colonnes
  • solutions intégrées DB : structure rigide à redéfinir à chaque changement de schéma, propriétaire
  • à ceci je rajouterai une solution vue au taf : créer une "table des mots" associant à chaque mot trouvé dans une table la clé de l'enregistrement contenant ce mot...

Ces solutions ne sont pas idéales, et manque des fonctionnalités suivantes :

  • récupération du nombre de résultats total et des n premiers résultats en une seule requête (au lieu du "select count(*)" et "select *" habituel
  • tri par pertinence
  • recherche approximative
  • filtre des résultats (par utilisateur, groupe, ...)

Méthodes de tri par pertinence

On peut trier par pertinence avec les critères suivants :

  • document contenant tous les mots de la recherche placé en premier
  • document contenant le plus de fois le mot cherché (rapporté au nombre de mots total du document) en premier
  • attributs du documents pondérés (si on trouve le mot dans le titre, c'est plus pertinent que si on ne le trouve que dans la description)

Lucene

Pour répondre aux besoins précédents, le moteur de recherche Lucene s'appuie sur un index stocké sur un système de fichier (autres types de stockages, voir "Évolutions"). Une indexation initiale est faite en s'appuyant sur la base, puis la recherche se fait sur l'index.

Recherche approximative

Lucene pourrait utiliser un index simple, composé des mots trouvés dans la base. Mais cette stratégie ne permet pas de faire de recherche approximative. Plusieurs techniques sont utilisées pour améliorer la recherche.

L'analyse effectuée lors de l'indexation est réalisée par deux composants : un tokenizer qui découpe les mots et un ou plusieurs filtres pour corriger le "flux de mots" à destination de l'index.

Filtres dispo :

  • la distance de Levenshtein (mesure la similarité entre deux mots)
  • l'algorithme des n-gram
  • phonétiques : phonétise les mots, et de ce fait ignore l'orthographe
  • synonymes : associations de mots pour catégoriser la recherche (ex : véhicule pour automobile, voiture)
  • famille de mots : amour pour amoureux, amoureuse, aimer

Parmi ces filtres, Emmanuel nous a présenté l'algorithme n-gram qui découpe les chaines de caractère en sous-chaines, dans le but de permettre les recherches approximatives.

Petit exemple des n-gram :

Lucene indexe "hippopotame". Il le découpe en n-gram de 3 lettres :

   hip, ipp, ppo, pop, opo, pot, tam, ame

Régis cherche maintenant le mot "ipopotame", même découpage, on retrouve les 3-gram correspondants dans l'index :

hippopotame = hip, ipp, ppo, pop, opo, pot, tam, ame
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
ipopotame =   ipo,             pop, opo, pot, tam, ame

Lucene trouve 5 sous-chaines, la similarité est donc élevée et on pourra renvoyer des résultats, même avec une mauvaise orthographe.

Mise en place d'HS

La configuration de l'indexation est mise en place sur les entités Hibernate via les annotations. Petit exemple de base (en vert JPA, en rouge HS):

@Indexed @Entity
public class Livre {

    @Field @Basic
    private String titre;

   
    @Field @Basic
    private String description;
    ...
}

Avec @Indexed, on indique à HS d'indexer l'entité sur les champs marqués par @Field.

On peut ensuite ajouter un @Analyzer à la classe pour spécifier l'analyseur à utiliser, défini par un tokenizer et des filtres :

@AnalyzerDef(
  name = "customanalyzer",
  tokenizer = @TokenizerDef(
    factory = StandardTokenizerFactory.class),
    filters = {
      @TokenFilterDef(factory = LowerCaseFilterFactory.class),
      @TokenFilterDef(
        factory = SnowballPorterFilterFactory.class,
        params = { @Parameter(name = "language", value = "English") }
      )
    }
  )
)

Il n'y a pas grand chose d'autre à faire. Quelques configurations en plus pour définir les répertoires des indexes.

HS fournit d'autre fonctionnalités comme les filtres de résultat, ou la répartition de l'index sur plusieurs répertoires, qui n'ont pas été abordés par Emmanuel.

Pour une plus grande scalabilité, on peut avoir un système d'index maître/esclave, où l'index maître est mis à jour via une file d'attente JMS, puis répliqué sur les esclaves. Cette méthode évite de "locker" l'index trop longtemps pour les mises à jours.

Évolutions d'HS

L'équipe d'HS travaille actuellement sur des mécanismes d'indexation de masse, pour paralléliser l'indexation (attention, à n'utiliser que lorsque la base est libre, car ce système ouvre de nombreuses connections).

Concernant d'autres implémentations sont à l'étude, notamment des solutions de type "grille mémoire" (entre autre Infinispan de JBoss).

Les autres résumés :

Paris JUG "Emmanuel Bernard" (Coder's Breakfast)

Soiree Emmanuel Bernard au Paris Jug (Le Touilleur Express)

Paris JUG : La soirée Emmanuel Bernard (Duchess)

Liens :

Hibernate Search : https://www.hibernate.org/410.html

Tuto Xebia architecture HS : http://blog.xebia.fr/2008/03/06/introduction-a-hibernate-search-googling-your-persistent-domain-model/

Tuto Xebia utilisation HS : http://blog.xebia.fr/2008/03/28/hands-on-hibernate-search-recherche-full-text/