Chapitre 13. Hibernate Search
13.1. Introduction à Hibernate Search
13.1.1. Hibernate Search
13.1.2. Premières étapes de Recherche Hibernate
- Voir Configuration dans le Guide de configuration et d'administration pour configurer la Hibernate Search.
13.1.3. Activer Hibernate Search dans Maven
hibernate-search-orm
:
<dependencyManagement> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <version>4.6.0.Final-redhat-2</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <scope>provided</scope> </dependency> </dependencies>
13.1.4. Ajouter les annotations
example.Book
et example.Author
et vous souhaitez ajouter des fonctionnalités de recherche de texte libre à votre application pour pouvoir rechercher des livres.
Exemple 13.1. Livre et auteur d'entités avant l'ajout d'annotations spécifiques de Hibernate Search
package example; ... @Entity public class Book { @Id @GeneratedValue private Integer id; private String title; private String subtitle; @ManyToMany private Set<Author> authors = new HashSet<Author>(); private Date publicationDate; public Book() {} // standard getters/setters follow here ... }
package example; ... @Entity public class Author { @Id @GeneratedValue private Integer id; private String name; public Author() {} // standard getters/setters follow here ... }
Book
et Author
. La première annotation @Indexed
désigne la classe Book
comme indexable. Hibernate Search stocke un ID sans token dans l'index pour s'assurer de l'unicité d'index d'une entité donnée. @DocumentId
indique la propriété à utiliser et est similaire, dans la plupart des cas, à la clé primaire de la base de données. L'annotation @DocumentId
est facultative dans le cas où une annotation @Id
existe.
title
et subtitle
et annotez-les avec @Field
. Le paramètre index=Index.YES
s'assurera que le texte sera indexé tandis que analyze=Analyze.YES
s'assure que le texte sera analysé en utilisant l'analyseur Lucene par défaut. L'analyse signifie généralement diviser une phrase en mots et exclure certains termes courants comme « a
» (un/une) ou « the
» (le/la). Nous abordons les analyseurs plus en détail par la suite. Le troisième paramètre que nous indiquons dans @Field
, store=Store.NO
, permet de garantir que les données ne seront pas stockées dans l'index. Le fait ou non que ces données soient stockées dans l'index n'aura aucun impact sur la possibilité de les rechercher. Du point de vue de Lucene, il n'est pas nécessaire de préserver les données une fois l'index créé. L'avantage de les stocker réside dans la capacité de les récupérer par projections ( voir Section 13.3.1.10.5, « Projection »).
index=Index.YES
, analyze=Analyze.YES
et store=Store.NO
sont les valeurs par défaut de ces paramètres et peuvent être omises.
@DateBridge
n'a pas encore été abordée. Cette annotation fait partie des ponts de champ intégrés dans Hibernate Search. L'index Lucene se base uniquement sur des chaînes. C'est pour cette raison que Hibernate Search doit convertir les types de données des champs indexés en chaîne et vice-versa. Une gamme de ponts prédéfinis est fournie, y compris la classe DateBridge
qui convertira une classe java.util.Date
en une classe String
avec la résolution indiquée. Pour plus de détails, veuillez consulter Section 13.2.4, « Ponts ».
@IndexedEmbedded.
Cette annotation est utilisée pour indexer des entités associées (@ManyToMany
, @*ToOne
, @Embedded
et @ElementCollection
) comme faisant partie de l'entité propriétaire, ce qui est nécessaire puisqu'un document d'index Lucene est une structure de données plate ne connaissant rien aux relations d'objets. Pour faire en sorte que le nom des auteurs soit trouvable, vous devez vous assurer que les noms sont indexés comme faisant partie du livre. En plus de @IndexedEmbedded
, vous devrez signaler tous les champs de l'entité associée que vous souhaitez inclure dans l'index comme @Indexed
. Pour plus d'informations, veuillez consulter Section 13.2.1.3, « Objets associés et intégrés »
Exemple 13.2. Les entités après l'ajout d'annotations de Hibernate Search
package example; ... @Entity @Indexed public class Book { @Id @GeneratedValue private Integer id; @Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO) private String title; @Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO) private String subtitle; @Field(index = Index.YES, analyze=Analyze.NO, store = Store.YES) @DateBridge(resolution = Resolution.DAY) private Date publicationDate; @IndexedEmbedded @ManyToMany private Set<Author> authors = new HashSet<Author>(); public Book() { } // standard getters/setters follow here ... }
package example;
...
@Entity
public class Author {
@Id
@GeneratedValue
private Integer id;
@Field
private String name;
public Author() {
}
// standard getters/setters follow here
...
}
13.1.5. Indexation
Exemple 13.3. Indexation de données à l'aide d'une session Hibernate
FullTextSession fullTextSession = org.hibernate.search.Search.getFullTextSession(session); fullTextSession.createIndexer().startAndWait();
Exemple 13.4. Indexation de données à l'aide de JPA
EntityManager em = entityManagerFactory.createEntityManager(); FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em); fullTextEntityManager.createIndexer().startAndWait();
/var/lucene/indexes/example.Book
. Inspectez cet index avec Luke. Cela vous aidera à comprendre comment Hibernate Search fonctionne.
13.1.6. Recherche
org.hibernate.Query
pour obtenir les fonctionnalités requises de l'API Hibernate. Le code suivant prépare une requête contre les champs indexés. L'exécution du code renvoie une liste de Book
.
Exemple 13.5. Utiliser une session Hibernate Search pour créer et exécuter une recherche
FullTextSession fullTextSession = Search.getFullTextSession(session); Transaction tx = fullTextSession.beginTransaction(); // create native Lucene query using the query DSL // alternatively you can write the Lucene query using the Lucene query parser // or the Lucene programmatic API. The Hibernate Search DSL is recommended though QueryBuilder qb = fullTextSession.getSearchFactory() .buildQueryBuilder().forEntity( Book.class ).get(); org.apache.lucene.search.Query query = qb .keyword() .onFields("title", "subtitle", "authors.name", "publicationDate") .matching("Java rocks!") .createQuery(); // wrap Lucene query in a org.hibernate.Query org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery(query, Book.class); // execute search List result = hibQuery.list(); tx.commit(); session.close();
Exemple 13.6. Utilisation de JPA pour créer et exécuter une recherche
EntityManager em = entityManagerFactory.createEntityManager(); FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em); em.getTransaction().begin(); // create native Lucene query using the query DSL // alternatively you can write the Lucene query using the Lucene query parser // or the Lucene programmatic API. The Hibernate Search DSL is recommended though QueryBuilder qb = fullTextEntityManager.getSearchFactory() .buildQueryBuilder().forEntity( Book.class ).get(); org.apache.lucene.search.Query query = qb .keyword() .onFields("title", "subtitle", "authors.name", "publicationDate") .matching("Java rocks!") .createQuery(); // wrap Lucene query in a javax.persistence.Query javax.persistence.Query persistenceQuery = fullTextEntityManager.createFullTextQuery(query, Book.class); // execute search List result = persistenceQuery.getResultList(); em.getTransaction().commit(); em.close();
13.1.7. Analyseur
Refactoring: Improving the Design of Existing Code
et que des clics sont nécessaires pour les requêtes suivantes : refactor
, refactors
, refactored
et refactoring
. Sélectionnez une classe d'analyseur dans Lucene qui applique l'analyse de radical d'un mot lors de l'indexation et de la recherche. Hibernate Search offre plusieurs façons de configurer l'analyseur (voir Section 13.2.3.1, « Analyseur par défaut et analyseur par classe » pour plus d'informations) :
- Définir la propriété
analyzer
dans le fichier de configuration. La classe spécifiée devient l'analyseur par défaut. - Définissez l'annotation
au niveau de l'entité.@Analyzer
- Définissez l'annotation
@
au niveau du champ.Analyzer
@AnalyzerDef
avec l'annotation @Analyzer
. Le framework de l'analyseur Solr et ses fabriques sont utilisés dans la dernière option. Pour plus d'informations sur les classes de fabrique, voir la JavaDoc Solrf ou lire un section correspondante Wiki Solr (http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters)
StandardTokenizerFactory
est utilisé par deux fabriques de filtre : LowerCaseFilterFactory
et SnowballPorterFilterFactory
. Le générateur de jetons divise les mots à chaque caractère de ponctuation et tiret mais ne touche pas aux adresses e-mail et noms d'hôtes d'internet intacts. Le générateur de jetons est idéal pour cela mais également pour d'autres opérations d'ordre général. Le filtre de bas-de-casse convertit toutes les lettres dans le jeton en bas-de-casse et le filtre boule de neige applique une recherche de radical linguistique.
Exemple 13.7. Utilisation de @AnalyzerDef et du cadre Solr pour définir et utiliser un analyseur
@Indexed @AnalyzerDef( name = "customanalyzer", tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = { @TokenFilterDef(factory = LowerCaseFilterFactory.class), @TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = { @Parameter(name = "language", value = "English") }) }) public class Book implements Serializable { @Field @Analyzer(definition = "customanalyzer") private String title; @Field @Analyzer(definition = "customanalyzer") private String subtitle; @IndexedEmbedded private Set authors = new HashSet(); @Field(index = Index.YES, analyze = Analyze.NO, store = Store.YES) @DateBridge(resolution = Resolution.DAY) private Date publicationDate; public Book() { } // standard getters/setters follow here ... }
@AnalyzerDef
pour définir un analyseur, puis appliquez-le à des entités et propriétés à l'aide de @Analyzer
. Dans cet exemple, le customanalyzer
est défini mais pas appliqué à l'entité. L'analyseur est uniquement appliqué aux propriétés title
et subtitle
. Une définition d'analyseur est globale. Définissez l'analyseur pour une entité et réutilisez la définition pour d'autres entités si requis.