Chapitre 13. Hibernate Search
13.1. Introduction à Hibernate Search Copier lienLien copié sur presse-papiers!
13.1.1. Hibernate Search Copier lienLien copié sur presse-papiers!
13.1.2. Premières étapes de Recherche Hibernate Copier lienLien copié sur presse-papiers!
- Voir Configuration dans le Guide de configuration et d'administration pour configurer la Hibernate Search.
13.1.3. Activer Hibernate Search dans Maven Copier lienLien copié sur presse-papiers!
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 Copier lienLien copié sur presse-papiers!
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 Copier lienLien copié sur presse-papiers!
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 Copier lienLien copié sur presse-papiers!
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 Copier lienLien copié sur presse-papiers!
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é
analyzerdans 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.