13.3. Recherche
- La création d'une
FullTextSession
- La création d'une requête Lucene à l'aide de la DSL de requête Hibernate Search (recommandé) ou l'API de requête Lucene
- L'inclusion d'une requête Lucene avec
org.hibernate.Query
- L'exécution de la recherche en appelant par exemple
list()
ouscroll()
FullTextSession
. Cette session spécifique à la recherche inclut une classe org.hibernate.Session
régulière afin de fournir une requête et des capacités d'indexation.
Exemple 13.30. Création d'une FullTextSession
Session session = sessionFactory.openSession(); ... FullTextSession fullTextSession = Search.getFullTextSession(session);
FullTextSession
pour construire une requête en texte intégral en utilisant la DSL de requête Hibernate Search ou la requête Lucene native.
final QueryBuilder b = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity( Myth.class ).get(); org.apache.lucene.search.Query luceneQuery = b.keyword() .onField("history").boostedTo(3) .matching("storm") .createQuery(); org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery ); List result = fullTextQuery.list(); //return a list of managed objects
Exemple 13.31. Création d'une requête Lucene à l'aide de QueryParser
SearchFactory searchFactory = fullTextSession.getSearchFactory(); org.apache.lucene.queryParser.QueryParser parser = new QueryParser("title", searchFactory.getAnalyzer(Myth.class) ); try { org.apache.lucene.search.Query luceneQuery = parser.parse( "history:storm^3" ); } catch (ParseException e) { //handle parsing failure } org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery); List result = fullTextQuery.list(); //return a list of managed objects
org.hibernate.Query
. Cette requête demeure dans le même paradigme que d'autres fonctions de requête Hibernate, telles que HQL (Hibernate Query Language), Native et Criteria. Utilisez des méthodes telles que list()
, uniqueResult()
, iterate()
et scroll()
avec la requête.
Exemple 13.32. Création d'une requête de recherche à l'aide de l'API JPA
EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager =
org.hibernate.search.jpa.Search.getFullTextEntityManager(em);
...
final QueryBuilder b = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder().forEntity( Myth.class ).get();
org.apache.lucene.search.Query luceneQuery =
b.keyword()
.onField("history").boostedTo(3)
.matching("storm")
.createQuery();
javax.persistence.Query fullTextQuery = fullTextEntityManager.createFullTextQuery( luceneQuery );
List result = fullTextQuery.getResultList(); //return a list of managed objects
Note
FullTextQuery
est extraite.
13.3.1. Création de requêtes
13.3.1.1. Générer une requête Lucene avec l'API Lucene
13.3.1.2. Génération d'une requête Lucene
QueryBuilder
pour cette tâche.
- Les noms de méthodes sont en anglais. Ainsi, les opérations API peuvent être lues et comprises comme une série d'expressions et instructions en anglais.
- Elle utilise le complètement automatique IDE ce qui facilite les complètements pour le préfixe d'entrée actuel et permet à l'utilisateur de choisir l'option qui convient.
- Elle utilise souvent le modèle de la méthode de chaîne.
- Les opérations API sont faciles à lire et à utiliser.
indexedentitytype
donné. Ce QueryBuilder
sait quel analyseur utiliser et quel pont de champ appliquer. Plusieurs QueryBuilder
(un pour chaque type d'entité impliqué dans la racine de votre requête) peuvent être créés. Le QueryBuilder
provient de SearchFactory
.
QueryBuilder mythQB = searchFactory.buildQueryBuilder().forEntity( Myth.class ).get();
QueryBuilder mythQB = searchFactory.buildQueryBuilder() .forEntity( Myth.class ) .overridesForField("history","stem_analyzer_definition") .get();
Query
assemblés avec l'API programmée de Lucene sont utilisées avec Hibernate Search DSL.
13.3.1.3. Requêtes de mots-clés
Query luceneQuery = mythQB.keyword().onField("history").matching("storm").createQuery();
Paramètre | Description |
---|---|
keyword() | Utilisez ce paramètre pour trouver un mot spécifique |
onField() | Utilisez ce paramètre pour spécifier le champ Lucene dans lequel rechercher le mot |
matching() | Utilisez ce paramètre pour spécifier la correspondance pour chaque chaîne |
createQuery() | Crée l'objet de requête de Lucene |
- La valeur « storm » est transmise à la classe
history
FieldBridge
, ce qui est utile lorsque les nombres ou dates sont impliqués. - La valeur de pont du champ est ensuite transmise à l'analyseur utilisé pour indexer le champ
history
, ce qui permet de garantir que la requête utilise la même transformation de terme que l'indexation (bas de casse, ngram, analyse de radical etc.). Si le processus d'analyse génère plusieurs termes pour un même mot, une requête booléenne sera utilisée avec la logiqueSHOULD
(semblable à une logiqueOR
).
@Indexed public class Myth { @Field(analyze = Analyze.NO) @DateBridge(resolution = Resolution.YEAR) public Date getCreationDate() { return creationDate; } public Date setCreationDate(Date creationDate) { this.creationDate = creationDate; } private Date creationDate; ... } Date birthdate = ...; Query luceneQuery = mythQb.keyword().onField("creationDate").matching(birthdate).createQuery();
Note
Date
devait être converti en sa représentation de chaîne (dans ce cas-là, l'année)
FieldBridge
ait une méthode objectToString
(ce qui est le cas pour toutes les mises en œuvre FieldBridge
intégrées).
@AnalyzerDef(name = "ngram", tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class ), filters = { @TokenFilterDef(factory = StandardFilterFactory.class), @TokenFilterDef(factory = LowerCaseFilterFactory.class), @TokenFilterDef(factory = StopFilterFactory.class), @TokenFilterDef(factory = NGramFilterFactory.class, params = { @Parameter(name = "minGramSize", value = "3"), @Parameter(name = "maxGramSize", value = "3") } ) } ) public class Myth { @Field(analyzer=@Analyzer(definition="ngram") public String getName() { return name; } public String setName(String name) { this.name = name; } private String name; ... } Date birthdate = ...; Query luceneQuery = mythQb.keyword().onField("name").matching("Sisiphus") .createQuery();
y
). Tout cela est fait de manière transparente pour l'utilisateur.
Note
ignoreAnalyzer()
ou ignoreFieldBridge()
peuvent être appelées.
//search document with storm or lightning in their history Query luceneQuery = mythQB.keyword().onField("history").matching("storm lightning").createQuery();
onFields
.
Query luceneQuery = mythQB .keyword() .onFields("history","description","name") .matching("storm") .createQuery();
andField()
.
Query luceneQuery = mythQB.keyword() .onField("history") .andField("name") .boostedTo(5) .andField("description") .matching("storm") .createQuery();
13.3.1.4. Requêtes Fuzzy (approximatives)
keyword
et ajoutez l'indicateur fuzzy
.
Query luceneQuery = mythQB .keyword() .fuzzy() .withThreshold( .8f ) .withPrefixLength( 1 ) .onField("history") .matching("starm") .createQuery();
threshold
est la limite au-dessus de laquelle deux termes sont considérés comme correspondant. Il s'agit d'une décimal entre 0 et 1 et la valeur par défaut est 0.5. Le prefixLength
est la longueur du préfix ignoré par l'« approximation ». Bien que la valeur par défaut soit 0, une valeur autre que 0 est recommandée pour les indexes contenant un grand nombre de termes distincts.
13.3.1.5. Requêtes Wildcard
?
représente un caractère unique et *
représente plusieurs caractères. Veuillez noter que dans un soucis de performance, il est recommandé que la requête ne commence pas par ?
ou *
.
Query luceneQuery = mythQB .keyword() .wildcard() .onField("history") .matching("sto*") .createQuery();
Note
*
ou ?
altéré est trop grand.
13.3.1.6. Requêtes de phrase
phrase()
.
Query luceneQuery = mythQB .phrase() .onField("history") .sentence("Thou shalt not kill") .createQuery();
Query luceneQuery = mythQB .phrase() .withSlop(3) .onField("history") .sentence("Thou kill") .createQuery();
13.3.1.7. Requêtes de portée
//look for 0 <= starred < 3 Query luceneQuery = mythQB .range() .onField("starred") .from(0).to(3).excludeLimit() .createQuery(); //look for myths strictly BC Date beforeChrist = ...; Query luceneQuery = mythQB .range() .onField("creationDate") .below(beforeChrist).excludeLimit() .createQuery();
13.3.1.8. Association de requêtes
SHOULD
(devrait) : la requête devrait contenir les éléments de correspondance de la sous-requête.MUST
(doit) : la requête doit contenir les éléments correspondants de la sous-requête.MUST NOT
(ne doit pas) : la requête ne doit pas contenir les éléments correspondants de la sous-requête.
Exemple 13.33. Requête MUST NOT
//look for popular modern myths that are not urban Date twentiethCentury = ...; Query luceneQuery = mythQB .bool() .must( mythQB.keyword().onField("description").matching("urban").createQuery() ) .not() .must( mythQB.range().onField("starred").above(4).createQuery() ) .must( mythQB .range() .onField("creationDate") .above(twentiethCentury) .createQuery() ) .createQuery();
Exemple 13.34. Requête SHOULD
//look for popular myths that are preferably urban Query luceneQuery = mythQB .bool() .should( mythQB.keyword().onField("description").matching("urban").createQuery() ) .must( mythQB.range().onField("starred").above(4).createQuery() ) .createQuery();
Exemple 13.35. Requête NOT
//look for all myths except religious ones Query luceneQuery = mythQB .all() .except( monthQb .keyword() .onField( "description_stem" ) .matching( "religion" ) .createQuery() ) .createQuery();
13.3.1.9. Options de requête
- La méthode
boostedTo
(sur les champs et types de requêtes) booste la requête entière ou le champ spécifique vers un facteur donné. - La méthode
withConstantScore
(sur requête) renvoie tous les résultats correspondants à la requête ayant un score égal au boost. - La méthode
filteredBy(Filter)
(sur requête) filtre les résultats de requêtes à l'aide de l'instanceFilter
. - La méthode
ignoreAnalyzer
(sur champ) ignore l'analyseur lors du traitement de ce champ. - La méthode
ignoreFieldBridge
(sur champ) ignore le pont de champ lors du traitement de ce champ.
Exemple 13.36. Associations d'options de requêtes
Query luceneQuery = mythQB .bool() .should( mythQB.keyword().onField("description").matching("urban").createQuery() ) .should( mythQB .keyword() .onField("name") .boostedTo(3) .ignoreAnalyzer() .matching("urban").createQuery() ) .must( mythQB .range() .boostedTo(5).withConstantScore() .onField("starred").above(4).createQuery() ) .createQuery();
13.3.1.10. Créer une requête Hibernate Search
13.3.1.10.1. Généralités
Exemple 13.37. Inclure une requête Lucene dans une requête Hibernate.
FullTextSession fullTextSession = Search.getFullTextSession( session ); org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );
Exemple 13.38. Filtrage des résultats de la recherche par type d'entité
fullTextQuery = fullTextSession .createFullTextQuery( luceneQuery, Customer.class ); // or fullTextQuery = fullTextSession .createFullTextQuery( luceneQuery, Item.class, Actor.class );
Customer
correspondants. La seconde partie du même exemple renvoie des Actor
et Item
correspondants. La restriction de type est polymorphe. Ainsi, si les deux sous-classes Salesman
et Customer
de la classe de base Person
apparaissent dans les résultats, spécifiez Person.class
pour filtre sur la base des types de résultat.
13.3.1.10.2. Pagination
Exemple 13.39. Définir une pagination pour une requête de recherche
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Customer.class ); fullTextQuery.setFirstResult(15); //start from the 15th element fullTextQuery.setMaxResults(10); //return 10 elements
Note
fulltextQuery.
getResultSize()
13.3.1.10.3. Tri
Exemple 13.40. Spécifier un Sort
Lucene
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( query, Book.class );
org.apache.lucene.search.Sort sort = new Sort(
new SortField("title", SortField.STRING));
query.setSort(sort);
List results = query.list();
Note
13.3.1.10.4. Stratégie de récupération
Exemple 13.41. Spécification de FetchMode
dans une requête
Criteria criteria = s.createCriteria( Book.class ).setFetchMode( "authors", FetchMode.JOIN ); s.createFullTextQuery( luceneQuery ).setCriteriaQuery( criteria );
Important
Criteria
car la méthode getResultSize()
émet une SearchException
lorsqu'elle est utilisée en association avec une classe Criteria
avec restriction.
setCriteriaQuery
.
13.3.1.10.5. Projection
Object[]
. Les projections permettent d'éviter les longs allers-retours dans la base de données, mais elles présentent également les contraintes suivantes :
- Les propriétés projetées doivent être stockées dans l'index (
@Field(store=Store.YES)
), ce qui augmente la taille de l'index. - Les propriétés projetées doivent utiliser un
FieldBridge
mettant en œuvreorg.hibernate.search.bridge.TwoWayFieldBridge
ouorg.hibernate.search.bridge.TwoWayStringBridge
, ce dernier étant la version la plus simple.Note
Tous les types d'Hibernate Search intégrés sont bidirectionnels. - Seules les propriétés simples de l'entité indexée ou de ses associations intégrées peuvent être projetées : une entité entière intégrée ne peut donc pas être projetée.
- La projection ne fonctionne pas sur les collections ou cartes indexées par le biais de
@IndexedEmbedded
Exemple 13.42. Utilisation de la projection pour récupérer les métadonnées
org.hibernate.search.FullTextQuery query =
s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( FullTextQuery.SCORE, FullTextQuery.THIS, "mainAuthor.name" );
List results = query.list();
Object[] firstResult = (Object[]) results.get(0);
float score = firstResult[0];
Book book = firstResult[1];
String authorName = firstResult[2];
FullTextQuery.THIS
: renvoie l'entité gérée et initialisée (tel que l'aurait fait une requête non projetée).FullTextQuery.DOCUMENT
: renvoie le document Lucene lié à l'objet projeté.FullTextQuery.OBJECT_CLASS
: renvoie la classe de l'entité indexée.FullTextQuery.SCORE
: renvoie le score du document dans la requête. Les scores sont utiles pour comparer deux résultats d'une requête donnée, mais sont inutiles pour comparer le résultat de différentes requêtes.FullTextQuery.ID
: la valeur de propriété de l'ID de l'objet projeté.FullTextQuery.DOCUMENT_ID
: l'ID du document Lucene. Utilisez cette valeur avec précaution car un ID de document Lucene peut changer entre deux ouvertures IndexReader différentes.FullTextQuery.EXPLANATION
: renvoie l'objet d'explication Lucene pour l'objet/document correspondant dans une requête donnée mais ne convient pas à la récupération de grandes quantités de données. L'exécution d'explication coûte autant que d'exécuter la requête Lucene entière par élément correspondant. La projection est donc recommandée.
13.3.1.10.6. Personnalisation de stratégies d'initialisation d'objet
Exemple 13.43. Vérifiez le cache de secon niveau avant d'utiliser une requête
FullTextQuery query = session.createFullTextQuery(luceneQuery, User.class); query.initializeObjectWith( ObjectLookupMethod.SECOND_LEVEL_CACHE, DatabaseRetrievalMethod.QUERY );
ObjectLookupMethod
définit la stratégie consistant à vérifier si un objet est facilement accessible (sans avoir à le trouver dans une base de données). Autres options :
ObjectLookupMethod.PERSISTENCE_CONTEXT
est utilisé si un grand nombre d'entités correspondantes sont déjà chargées dans le contexte de persistance (chargées dansSession
ouEntityManager
).ObjectLookupMethod.SECOND_LEVEL_CACHE
vérifie le contexte de persistance puis le cache de second niveau.
- Configurez et activez correctement le cache de second niveau.
- Activez le cache de second niveau pour l'entité souhaitée à l'aide d'annotations telles que
@Cacheable
. - Activez l'accès de lecture de cache de second niveau pour
Session
,EntityManager
ouQuery
. UtilisezCacheMode.NORMAL
dans les API natives de Hibernate ouCacheRetrieveMode.USE
dans les API de Persistance Java.
Avertissement
ObjectLookupMethod.SECOND_LEVEL_CACHE
. D'autres fournisseurs de cache de second niveau ne mettent pas en œuvre cette opération de manière efficace.
DatabaseRetrievalMethod
comme suit :
QUERY
(par défaut) utilise un ensemble de requêtes pour charger différents objets dans chaque lot. Cette approche est recommandée.FIND_BY_ID
charge un objet à la fois à l'aide de la sémantiqueSession
.get
ouEntityManager
.find
. Cette approche est recommandée si la taille du lot est définie pour l'entité, ce qui permet à Hibernate Core de charger des entités par lots.
13.3.1.10.7. Limiter le temps d'une requête
- Lever une exception lorsque la limite est atteinte.
- Limiter au nombre de résultats récupérés lorsque la limite de temps est levée
13.3.1.10.8. Lever une Exception de Limite de temps
QueryTimeoutException
est levée (org.hibernate.QueryTimeoutException
ou javax.persistence.QueryTimeoutException
suivant l'API programmatique).
Exemple 13.44. Définition d'un Timeout dans une exécution de requête
Query luceneQuery = ...; FullTextQuery query = fullTextSession.createFullTextQuery(luceneQuery, User.class); //define the timeout in seconds query.setTimeout(5); //alternatively, define the timeout in any given time unit query.setTimeout(450, TimeUnit.MILLISECONDS); try { query.list(); } catch (org.hibernate.QueryTimeoutException e) { //do something, too slow }
getResultSize()
, iterate()
et scroll()
respectent le timeout jusqu'à la fin de la méthode. C'est pourquoi Iterable
ou ScrollableResults
ignorent le timeout. De plus, la méthode explain()
ne respecte pas cette période de timeout. Cette méthode est utilisée pour déboguer et pour vérifier les raisons derrière la contre-performance d'une requête.
Exemple 13.45. Définition d'un Timeout dans une exécution de requête
Query luceneQuery = ...; FullTextQuery query = fullTextEM.createFullTextQuery(luceneQuery, User.class); //define the timeout in milliseconds query.setHint( "javax.persistence.query.timeout", 450 ); try { query.getResultList(); } catch (javax.persistence.QueryTimeoutException e) { //do something, too slow }
Important