Anthony Patricio’s Blog

Hibernate Search: la cerise sur le gâteau

Posted in Persistance / Données by apatricio on juillet 15, 2009

imagesSondage

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.

search

Publicités

2 Réponses

Subscribe to comments with RSS.

  1. Louis said, on janvier 24, 2011 at 9:49

    Bonjour,
    Merci pour cet article.J’aurai une question concerant la recherche FullText avec JPA.
    Dans une requete standard JP-QL on peut utiliser les fonctions setParameter pour restreindre le scope de la requête or dans le cas d’une recherche fulltext (org.hibernate.search.jpa.impl.FullTextQueryImpl) il n’est pas possible d’utiliser ce genre de méthode car non supporté.
    Dans mon cas de figure j’ai un moteur de recherche multi-critère (dont un champ doit être fait par approximation => hibernate-search), quel serait votre suggestion pour gérer ce cas de figure. Si je trouve de mon côté une solution, je vous en ferai part.
    Cordialement,
    Louis

    • apatricio said, on juillet 22, 2011 at 2:16

      Désolé Louis, j’ai abandonné mon blog pendant de très longs mois, je n’avais pas vu ton commentaire.
      Je vais refaire vivre ce blog un peu, j’espère que tu as trouvé une solution à ton problème.


Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :