Chapitre 4. Rechercher vos données

Ce chapitre couvre

  • La structure d’une requête de recherche Elasticsearch et de sa réponse
  • Les filtres Elasticsearch et leur différence par rapport aux requêtes
  • Filtrer les bits et la mise en cache
  • Utilisation de requêtes et de filtres pris en charge par Elasticsearch

Maintenant que nous avons exploré la manière dont vous inserer des données dans Elasticsearch, voyons comment les y extraire: en effectuant une recherche. Après tout, à quoi sert-il d’indexer vos données dans un moteur de recherche si vous ne pouvez pas effectuer de recherche à travers celui-ci? Heureusement, Elasticsearch fournit une API riche pour la recherche des données, couvrant toute la gamme des capacités de recherche de Lucene. En raison du format que Elasticsearch autorise pour la construction des requêtes de recherche, les possibilités de design de ces requêtes sont illimitées. La meilleure façon de déterminer quelle requête combinée à quels filtres faut-il utiliser pour vos données est de les expérimenter. Par conséquent, n’hésitez pas à essayer ces combinaisons dans les données de votre projet pour déterminer laquelle correspond le mieux à vos besoins.



Données interrogeables
Dans ce chapitre, vous allez à nouveau utiliser le jeu de données formé autour du site Web de rencontre que nous avons abordé dans les exemples précédents. Cet ensemble de données contient deux types de documents différents: les groupes et les événements. Pour suivre et exécuter les requêtes vous-même, téléchargez et exécutez le script populate.sh pour renseigner un index Elasticsearch. Les exemples sont créés avec une nouvelle exécution du script; si vous souhaitez suivre le processus, exécutez à nouveau le script.

Pour télécharger le script, voir le code source du livre à l’adresse https://github.com/ftounga/elasticsearch.



Pour commencer, nous discuterons des composants communs à toutes les requêtes de recherche et à tous les résultats afin que vous compreniez à quoi ressemble une requête de recherche et le résultat de cette requête. Nous passerons ensuite à la discussion sur les requêtes et filtres DSL comme l’un des principaux éléments de l’API de recherche. Nous aborderons ensuite les différences entre les requêtes et les filtres, puis nous examinerons certains des filtres et requêtes les plus couramment utilisés. Si vous vous demandez comment Elasticsearch calcule le score des documents, ne vous inquiétez pas. Nous en discuterons au chapitre 6, où nous parlons des recherches et leur niveau de pertinence. Enfin, nous fournissons un guide rapide pour vous aider à choisir le type de combinaison de requête et de filtre à utiliser pour une application particulière.

Avant de commencer, revoyons ce qui se passe lorsque vous effectuez une recherche dans Elasticsearch (voir figure 4.1). La demande de recherche de l’API REST est d’abord envoyée au noeud auquel vous choisissez de vous connecter, qui l’envoie à son tour à tous les fragments (primaires ou répliqués) de l’index ou des index interrogés. Lorsque suffisamment d’informations ont été collectées sur tous les fragments pour trier et classer les résultats, seuls les fragments ayant le contenu du document demandé sont invités à renvoyer les données jugées pertinentes.

Ce comportement de routage de recherche est configurable. Le comportement par défaut est illustré à la figure 4.1 et s’intitule «query_then_fetch». Nous verrons comment le modifier ultérieurement au chapitre 10. Pour l’instant, examinons la structure de base partagée par toutes les demandes de recherche Elasticsearch.

4.1. STRUCTURE D’UNE REQUÊTE DE RECHERCHE

Les requêtes de recherche Elasticsearch sont basées sur des documents JSON ou des URL. Les requête sont envoyées au serveur et, comme toutes les requête de recherche suivent le même format, il est utile de comprendre les composants que vous pouvez modifier pour chaque demande de recherche. Avant de discuter des différents composants, nous devons parler de la portée de votre demande de recherche.

4.1.1. Spécifier une zone de recherche

Toutes les requête de recherche REST utilisent le endpoint _search et peuvent être soit une requête de type GET ou POST. Vous pouvez rechercher un cluster entier ou limiter la portée en spécifiant les noms des index ou des types dans l’URL de la requête. La liste suivante fournit des exemples d’URL de recherche qui limitent la portée des recherches.

En plus des index, vous pouvez également utiliser des alias pour effectuer une recherche dans plusieurs index. Cette méthode est souvent utilisée pour parcourir tous les index disponibles et horodatés. Pensez aux index au format logstash-yyyymmdd, avec un alias appelé logstash qui pointe vers tous les index. Vous pouvez également effectuer une recherche de base et la limiter à tous les index basés sur logstash: curl ‘localhost: 9200 / logstash / _search’. Pour des performances optimales, limitez vos requêtes au plus petit nombre possible d’index et de types, car tout ce qu’Elasticsearch n’a pas à faire se traduire par des réponses plus rapides. N’oubliez pas que chaque requête de recherche doit être envoyée à tous les fragments d’un index. Plus il y a d’indexs auxquels vous devez envoyer des requêtede recherche, plus il y a des fragments  qui seront impliqués.

Maintenant que vous savez comment limiter la portée de votre requête, l’étape suivante consiste à examiner ses composants de base .

4.1.2. Composants de base d’une requête de recherche

Une fois que vous avez sélectionné les index à rechercher, vous devez configurer les composants les plus importants de la requête. Ces composants traitent de la quantité de documents à retourner, sélectionnent les meilleurs documents à retourner et configurent les documents que vous ne souhaitez pas inclure dans vos résultats:

  • query— Composant le plus important de votre requête de recherche, cette partie configure les meilleurs documents à renvoyer sur la base d’un score, ainsi que les documents que vous ne souhaitez pas renvoyer. Ce composant est configuré à l’aide de la requête et du filtre DSL. Un exemple est de rechercher tous les événements avec le mot «elasticsearch» dans le titre et limité aux événements de cette année.
  • size: représente le nombre de documents à renvoyer.
  • from— Avec le paramètre size, from est utilisé pour faire de la pagination. Soyez prudent, cependant; Afin de déterminer la deuxième page de 10 éléments, Elasticsearch doit calculer les 20 éléments les plus importants. Si votre ensemble de résultats augmente, obtenir une page quelque part au milieu coûterait cher.
  • _source – Spécifie comment le champ _source est renvoyé. La valeur par défaut est de renvoyer le champ complet. En configurant _source, vous filtrez les champs renvoyés. Utilisez cette option si vos documents indexés sont volumineux et que vous n’avez pas besoin de tout le contenu de votre résultat. Sachez que vous ne devez pas désactiver le champ _source dans vos mapping d’index si vous souhaitez l’utiliser. Voir la note pour connaître la différence entre le fait d’utiliser field et _source.
  • sort: le tri par défaut est basé sur le score d’un document. Si vous ne vous souciez pas de la partition ou si vous attendez un grand nombre de documents avec la même partition, l’ajout d’un tri vous permet de contrôler les documents à restituer.


Note
Avant la version 1 de Elasticsearch, field était le composant à utiliser pour filtrer les champs à renvoyer. C’est encore possible. le comportement consiste à renvoyer les champs stockés s’ils sont disponibles. Si aucun ne l’est, le champ est obtenu à partir de la source. Si vous ne stockez pas explicitement les champs dans l’index, il est préférable d’utiliser le composant _source. En utilisant un filtre basé sur _source, Elasticsearch n’a pas à rechercher d’abord parmis les champs stockés avant d’obtenir le champ à partir de _source.



Les champs from et size sont envoyés pour spécifier le décalage à partir duquel commencer les résultats et la taille de chaque «page» de résultats. Par exemple, si vous envoyez une valeur de 7 et une taille de 5, Elasticsearch renverra les 8ème, 9ème, 10ème, 11ème et 12ème résultats (car le paramètre from commence à 0, en spécifiant 7, on commence au 8ème résultat).  Si ces deux paramètres ne sont pas envoyés, Elasticsearch commence par défaut au premier résultat (le «0e») et renvoie 10 résultats dans la réponse. Il existe deux façons distinctes d’envoyer une requête de recherche à Elasticsearch.

Dans la section suivante, nous traitons de l’envoi d’une requête de recherche basée sur une URL; Après cela, nous discuterons des requêtes de recherche basées sur le corps de ces dernières. Les composants de base dont nous avons parlé seront utilisés dans les deux mécanismes.

Requête basée sur une URL
Dans cette section, vous allez créer une requête basée sur une URL en utilisant les quatre composants de base décrits dans la section précédente. La recherche basée sur une URL est censée être utile pour les requêtes rapides basées sur une commande curl. Toutes les fonctionnalités de recherche ne sont pas exposées lorsqu’on utilise une requête basée sur une URL. Dans la figure suivante, la requête recherchera tous les événements, mais vous voulez la deuxième page de 10 éléments.

Dans la figure 4.3, vous créez la requête pour renvoyer les 10 premiers événements par défaut de tous les événements, mais classés par date dans l’ordre croissant. Essayez également la même requête dans l’ordre décroissant (desc) et vérifiez si l’ordre des événements est modifié, comme indiqué dans la figure suivante:

Dans la figure 4.4, vous limitez les champs de la source que vous souhaitez dans la réponse. Imaginez que vous voulez seulement avoir le titre de l’événement avec la date de l’événement. Encore une fois, vous voulez que les événements soient classés par date. Vous configurez le composant _source pour demander le titre et la date uniquement. Plus d’options sur l’attribut _source sont expliquées dans la section suivante lorsque nous discuterons de la recherche basée sur le corps de la requête. La réponse dans la figure montre l’un des resultats retournés.

Jusqu’à présent, vous n’avez créé que des requêtes à l’aide de la requête match_all. La requête et le filtre DSL sont décrits dans la section 4.2, mais nous pensons qu’il est important de montrer comment vous pouvez créer une requête basée sur une URL dans laquelle vous souhaitez renvoyer uniquement les documents contenant le mot «elasticsearch» dans le titre, comme dans la figure suivante. Encore une fois, vous triez par date. Notez le q = title: elasticsearch. C’est ici que vous spécifiez que vous souhaitez interroger le champ title pour le mot «elasticsearch».

Avec q =, vous indiquez que vous souhaitez fournir une requête dans la requête de recherche. Avec title: elasticsearch, vous indiquez que vous recherchez le mot «elasticsearch» dans le champ de titre.

Nous vous laissons tester et vérifier que la réponse ne contient que des événements avec le mot «elasticsearch» dans le titre. N’hésitez pas à jouer avec d’autres mots et champs. Là encore, vous pouvez combiner les composants mentionnés de l’API de recherche dans une requête.

Maintenant que vous êtes à l’aise avec les requêtes utilisant l’URL, vous êtes prêt à passer aux requêtes basées sur le corps (http body).

4.1.3. Requête basée sur le body

Dans la section précédente, nous avons montré comment utiliser les composants de base dans les requêtes à base d’URL. C’est un bon moyen d’interagir avec Elasticsearch si vous êtes sur la ligne de commande, par exemple. Lors de l’exécution de recherches plus avancées, l’utilisation de recherches basées sur le corps de la requête vous donne plus de flexibilité et plus d’options. Même lorsque vous utilisez des recherches basées sur le corps de la requête, certains composants peuvent également encore être fournis dans l’URL. Nous nous concentrons dans cette section sur le corps de la requête car nous avons déjà traité de toutes les configurations basées sur les URL dans la section précédente. L’exemple dans la figure suivante, recherche la deuxième page de l’index get-together lorsque tous les documents correspondent.

Hormis le fait de remarquer la section « query« , qui est un objet dans chaque requête, ne faites pas encore attention à la section « match_all« . Nous en parlerons à la section 4.2 lorsque nous discuterons des requêtes et filtres DSL.

Champs retournés avec les résultats
L’élément suivant que toutes les requêtes partagent est la liste des champs qu’Elasticsearch doit renvoyer pour chaque document correspondant. Ceci est spécifié en envoyant le composant _source dans la requête. Si le paramètre _source n’est spécifiée dans la requête, Elasticsearch renvoie par défaut l’intégralité de l’attribut _source du document ou, si la source n’est pas stockée, uniquement les métadonnées relatives au document correspondant: _id, _type, _index et _score.

La requête précédente est utilisée dans la figure suivante, renvoyant les champs nom et date de chaque groupe correspondant.

Caractères génériques dans les champs renvoyés dans_source
Vous pouvez non seulement envoyer une liste de champs, mais vous pouvez également spécifier des caractères génériques. Par exemple, si vous souhaitez renvoyer à la fois les champs « name » et « nation », vous pouvez spécifier _source: « na * ». Vous pouvez également spécifier plusieurs caractères génériques à l’aide d’un tableau de chaînes génériques, tel que _source: [« name. * », « Address. * »].

Vous pouvez non seulement spécifier les champs à inclure, mais également ceux que vous ne souhaitez pas renvoyer. La figure suivante en donne un exemple.

Ordre de tri pour les résultats
Le dernier élément que la plupart des recherches incluent est l’ordre de tri des résultats. Si aucun ordre de tri n’est spécifié, Elasticsearch renvoie les documents correspondants triés par la valeur _score dans l’ordre décroissant, en commençant par les documents les plus pertinents (scores les plus élevés). Pour trier les champs par ordre croissant ou décroissant, spécifiez un tableau de map plutôt qu’un tableau de champs. Vous pouvez trier sur un nombre quelconque de champs en spécifiant une liste de champs ou de map de champs dans la valeur de tri. Par exemple, en utilisant la recherche précédente sur l’organiseur, vous pouvez renvoyer les résultats triés en premier par date de création, en commençant par le plus ancien. puis par le nom du groupe de rencontre, en ordre alphabétique inverse; et enfin par le _score du résultat, comme indiqué dans la figure suivante.

Tri sur des champs multivalués et « analysed »
Lors du tri sur des champs à valeurs multiples (balises, par exemple), vous ne savez pas comment le tri utilise les valeurs. Il en choisira une sur laquelle faire le tri, mais vous ne pouvez pas savoir laquelle. La même chose est vraie pour les champs analysed. Un champ analysed résultera également en plusieurs termes. Par conséquent, il est préférable de trier les champs non analysed ou numériques.



Les composants de base en action
Maintenant que nous avons abordé les composants de recherche de base, la figure suivante présente un exemple de requête qui les utilise tous.

Avant d’entrer dans les détails sur l’API de requête et de filtre, nous devons couvrir un autre élément: la structure de la réponse.

4.1.4. Comprendre la structure d’une réponse

Examinons un exemple de recherche et voyons à quoi ressemble la réponse. La figure suivante recherche des groupes portant sur «elasticsearch». Par souci de brièveté, nous avons utilisé la recherche basée sur une URL.

N’oubliez pas que si vous ne stockez ni la _source  du document, ni les champs, vous ne pourrez pas récupérer la valeur d’Elasticsearch!

Maintenant que vous connaissez les composants de base d’une requête de recherche, il ya un composant dont nous n’avons pas encore discuté: les requêtes et les filtres DSL. Cela a été fait à dessein, car le sujet est tellement vaste qu’il mérite sa propre section.

4.2. PRÉSENTATION DES REQUÊTES ET DES FILTRES DSL

Dans la section précédente, nous avons abordé les composants de base d’une requête. Nous avons parlé de la quantité de resultats à renvoyer et de la pagination en fonction des paramètres from et size. Nous avons également discuté du tri et du filtre des champs de la source à retourner. Dans cette section, nous parlerons du composant de base dont nous n’avons pas encore parlé en détail, le composant query. Jusqu’à présent, vous avez utilisé un composant de base, la query match_all. Vérifiez la figure suivante pour la voir en action.

Dans cette section, vous remplacez la query match_all par la query match et vous allez ajouter le filtre DSL  term à la requête de recherche. Après cela, nous explorons ce qui différencie les filtres, des query. Ensuite, nous examinons quelques autres requêtes et filtres de base. Nous terminons la section avec des requêtes composées et d’autres requêtes et filtres plus avancés. Ensuite, avant de passer aux analyseurs, nous vous aidons à choisir la bonne requête en fonction du besoin.

4.2.1. La query match et le filtre term

Jusqu’à présent, presque chaque requête que vous avez faite a renvoyé tous les documents. Dans cette section, nous montrons deux manières de limiter le nombre de documents à renvoyer. Nous commençons par une query match pour trouver des groupes contenant le mot «Hadoop» dans le titre. L’exemple de code suivant montre cette requête.

La requête renvoie trois événements au total. La structure de la réponse a été expliquée précédemment à la section 4.1.4. Si vous suivez, regardez le score du premier résultat. C’est le document intitulé «Utilisation de Hadoop avec Elasticsearch». Le score pour ce document est 1.3958796. Vous pouvez modifier la recherche en recherchant le mot «Hadoop» avec la lettre H. Le résultat sera le même. Essayez-le si vous ne nous croyez pas.

Imaginez maintenant que vous avez un site Web qui regroupe les événements par hôte. Vous obtenez donc cette belle liste d’agrégats et le nombre d’événements par hôte. Après avoir cliqué sur les événements organisés par Andy, vous souhaitez rechercher tous les événements organisés par Andy. Vous pouvez créer une requête avec la query match recherchant « Andy » dans le champ host. Si vous créez cette requête et l’exécutez, vous verrez qu’il y a trois événements organisés par Andy, tous ayant le même score. Nous vous entendons demander: «Pourquoi?». Lisez le chapitre 6 sur le fonctionnement du scoring.

C’est le bon moment d’introduire les filtres.

Les filtres sont similaires aux query présentées dans ce chapitre, mais ils diffèrent par la manière dont ils affectent le score et les performances de nombreuses actions de recherche. Plutôt que de calculer le score d’un terme particulier comme le font les query, un filtre sur une recherche renvoie une simple réponse à la question: «ce document correspond-il à notre requête?» avec une réponse par oui ou par non. La figure 4.2 montre la principale différence entre les requêtes et les filtres.

En raison de cette différence, les filtres peuvent être plus rapides qu’une requête classique et peuvent également être mis en cache. Une recherche utilisant un filtre ressemble à une recherche normale utilisant une query, mais la query est remplacée par une map »filtered » contenant la query d’origine et le filtre à appliquer, comme indiqué dans la figure suivante. Cette requête s’appelle une query filtrée dans les requêtes DSL. Une requête filtrée contient deux composants: la query et le filtre.

Ici, une requête pour les événements correspondants au mot clé « hadoop » est utilisée de façon conjointe à un filtre pour limiter encore plus les résultats (host = Andy). Dans la section filter, le filtre term est appliqué à tous les documents ayant l’hôte « andy« . En coulisse, Elasticsearch construit un ensemble de bits (bitset), qui est un ensemble binaire de bits indiquant si le document correspond à ce filtre ou pas. La figure 4.3 montre à quoi ce bitet ressemble.

Après avoir construit le jeu de bits, Elasticsearch peut maintenant l’utiliser pour filtrer (d’où le nom!) les documents sur lesquels il ne devrait pas effectuer de recherche en fonction de la partie query de la recherche. Le filtre limite le nombre de documents pour lesquels un score doit être calculé. Le score de l’ensemble limité de documents est calculé en fonction de la requête. De ce fait, l’ajout d’un filtre peut s’avérer beaucoup plus rapide que la combinaison de la requête entière en une seule recherche. En fonction du type de filtre utilisé, Elasticsearch peut mettre en cache les résultats dans un bitset. Si le filtre est utilisé pour une autre recherche, le bitset ne sera pas calculé à nouveau!

Il existe des filtres qui ne sont pas automatiquement mis en cache si Elasticsearch peut dire qu’ils ne seront plus jamais utilisés ou si les bitsets sont faciles à recréer. Un exemple de requête difficile à mettre en cache est un filtre qui limite les résultats à tous les documents de la dernière heure. Cette requête change toutes les secondes lorsque vous l’exécutez. Il n’ya donc aucune raison de la mettre en cache. Consultez la figure 4.17 pour en voir un exemple. De plus, Elasticsearch vous permet de spécifier manuellement si un filtre doit être mis en cache. Tout cela se traduit par des recherches plus rapides avec des filtres. Par conséquent, vous avez tout intérêt à transformer des éléments de votre requête en filtres si vous le pouvez.

Nous allons revenir sur les bitsets pour expliquer leur fonctionnement et leur incidence sur les performances au chapitre 10, qui explique comment accélérer les recherches. Nous espérons que vous avez une idée plus précise de ce que sont les filtres et les query, la difference qui existent entre ces 2 concepts, et surtout les avantages qu’ils offrent en terme de performance.

4.2.2. Les requêtes et filtres de base les plus utilisés

Bien qu’il existe plusieurs façons de rechercher des éléments dans Elasticsearch, certaines peuvent être meilleures que d’autres, en fonction de la manière dont les données sont stockées dans votre index. Dans cette section, vous allez apprendre à utiliser les différents types de requête pris en charge par Elasticsearch et essayer de mettre en place un exemple de chaque. Nous évaluerons les avantages et inconvénients d’utilisation de chaque requête et fournirons des notes de performance sur chacune d’entre elles afin que vous puissiez déterminer la requête correspondant le mieux à votre architecture de données.

Dans les sections précédentes de ce chapitre, un certain nombre de query et de filtres ont déjà été introduits. Vous avez commencé par la query match_all pour renvoyer tous les documents, puis sur la query  match pour limiter les résultats d’un mot existant dans un champ et vous avez utilisé le filtre term pour limiter les résultats à l’aide d’un terme dans un champ. Une query dont nous n’avons pas discutée mais que vous avez utilisée est query_string. Cette query a été utilisée dans la recherche basée sur une URL. Vous en saurez d’avantages dans cette section.

Dans cette section, non seulement nous récapitulerons ces query, mais nous introduirons maintenant des options plus avancées. Nous examinerons également des query plus avancées et des filtres tels que le filtre range, les query  prefix et simple_query_string. Commençons par les query les plus simples: match_all.

Query match_all
Nous allons vous donner une idée de ce que fait cette query. La query match_all est utile lorsque vous souhaitez utiliser un filtre au lieu d’une simple query (peut-être si vous ne vous souciez pas du tout du score des documents) ou si vous souhaitez renvoyer tous les documents parmi les index et les types que vous recherchez. La requête ressemble à ceci:

Utilisé avec un filtre:

Simple, n’est ce pas? Pas très utile cependant pour un moteur de recherche, car les utilisateurs ont rarement besoin de récupérer toutes les données. Vous pouvez même en réduire cette requête. En effet la query match_all est la valeur par défaut. Par conséquent, elle peut être complètement omise. Examinons une requête un peu plus utile.

Query Query_string 
Au chapitre 2, vous avez utilisé la query query_string pour voir à quel point il est facile d’obtenir un serveur Elasticsearch opérationnel, mais nous y reviendrons plus en détail afin que vous puissiez voir comment il diffère des autres query.

Comme indiqué dans la figure suivante, une recherche avec la query query_string peut être effectuée à partir de l’URL  ou envoyée dans le corps de la requête elle même. Dans cet exemple, vous recherchez des documents contenant «nosql». La requête doit renvoyer un document.

Par défaut, une requête query_string interroge le champ _all qui, si vous vous en souvenez au chapitre 3, est constitué de tous les champs combinés. Vous pouvez changer cela en spécifiant un champ avec la query, tel que description: nosql, ou en spécifiant un champ default_field avec la requête, comme indiqué dans la figure suivante.

Comme vous l’avez peut-être deviné, cette syntaxe offre bien plus que la recherche d’un seul mot. Sous le capot, il s’agit de la syntaxe complète des requêtes Lucene, qui permet de combiner la recherche de différents termes avec des opérateurs booléens tels que AND et OR, ainsi que d’exclure des documents des résultats à l’aide de l’opérateur de signe moins (-). La requête suivante recherche tous les groupes avec «nosql» dans le nom mais sans «mongodb» dans la description:

Pour rechercher tous les groupes de recherche et Lucene créés entre 1999 et 2001, vous pouvez utiliser les éléments suivants:



Reportez-vous à www.lucenetutorial.com/lucene-query-syntax.html pour obtenir un exemple complet de la syntaxe query_string.



Précautions avec Query_string 
Bien que la requête query_string soit l’une des requêtes les plus puissantes disponibles dans Elasticsearch, elle peut parfois être l’une des plus difficiles à lire et à étendre. Il peut être tentant de permettre à vos utilisateurs de spécifier leurs propres requêtes avec cette syntaxe, mais comprenez bien qu’il est difficile d’expliquer la signification de requêtes complexes telles que celles-ci:

name:search^2 AND (tags:lucene OR tags: »big data »~2) AND -description:analytics AND created_on:[2006-05-01 TO 2007-03-29]



Un gros inconvénient de la query query_string est qu’elle est très puissante. Donner ce pouvoir à aux utilisateurs de votre site Web pourrait mettre votre cluster Elasticsearch en danger. Si les utilisateurs commencent à saisir des requêtes avec un format incorrect, ils récupèreront des exceptions. Il est également possible de faire des combinaisons qui renverraient le monde et mettraient ainsi votre cluster en surcharge. Voir la note précédente pour un exemple.

Les remplacements suggérés pour la query query_string sont: term, terms, match, ou multi_match, qui vous permettent toutes de rechercher des chaînes dans un champ ou des champs dans un document. Un autre bon remplacement est la query simple-query-string; elle est censée permettre un accès facile à la syntaxe query utilisant les opérateurs +, -, AND, OU. Plus d’informations sur ces query dans les sections suivantes.

La query et filtre term
les query et filtres term font partie des requêtes les plus simples pouvant être exécutées, vous permettant de spécifier un champ et un terme à rechercher dans vos documents. Notez que, comme le terme recherché n’est pas analysé, il doit correspondre exactement à un terme du document pour que le résultat soit trouvé. Nous verrons comment les jetons (tokens), qui sont des fragments de texte individuels indexés par Elasticsearch, sont analysés au chapitre 5. Si vous connaissez bien Lucene, il peut être utile de savoir que la query term renvoie directement au mot clé TermQuery de Lucene.

La figure suivante présente la query term qui recherche des groupes avec la balise « elasticsearch ».

Comme la query term, un filtre term peut être utilisé lorsque vous souhaitez limiter les résultats aux documents contenant le terme mais sans affecter le score. Comparez les scores des documents de la figure précédente avec ceux de la figure suivante: vous remarquerez que le filtre ne se donne pas la peine de calculer et donc d’influencer le score; En raison de la query match_all, le score de tous les documents est 1.0.

Query Terms

Semblable à la query term, terms(notez le s!) peut rechercher plusieurs termes dans le champ d’un document. Par exemple, la figure suivante recherche les groupes à partir des mots clés «jvm» ou «hadoop».

Pour forcer un nombre minimal de termes correspondants dans un document avant qu’il ne soit considéré comme correspondant à la requête, spécifiez le paramètre minimum_should_match:

La synthaxe peut paraître limitée en effet. Vous vous demandez probablement aussi ce qui se passerait si vous deviez combiner plusieurs requêtes en une seule. Plus d’informations sur la combinaison de requêtes multiples via la query term sont abordées à la section 4.3 sur les requêtes composées.

4.2.3. Query match et le filtre term

Semblable à la query term, la query match est une hash map contenant le champ que vous souhaitez rechercher ainsi que la chaîne que vous souhaitez rechercher, ce qui peut être un champ ou le champ spécial _all pour effectuer une recherche simultanée dans tous les champs. . Voici un exemple de query match, recherchant des groupes dont le nom contient «elasticsearch»:

La query match peut se comporter de différentes manières. les deux comportements les plus importants sont le booléen et phrase.

Comportement de type query booléen

Par défaut, la query match utilise le comportement booléen et l’opérateur OR. Par exemple, si vous recherchez le texte «Elasticsearch Denver», Elasticsearch recherche «Elasticsearch OR Denver», ce qui correspond aux groupes de rendez-vous de «Elasticsearch Amsterdam» et «Denver Clojure Group».

Pour rechercher des résultats contenant «Elasticsearch» et «Denver», modifiez l’opérateur en modifiant le contenu du champ name dans le champs match dans un objet et en définissant le champ opérateur sur AND:

La seconde manière importante dont une query match peut se comporter est comme une query phrase.

Comportement de type query phrase

Une query phrase est utile lors de la recherche d’une phrase spécifique dans un document, avec une marge de manœuvre suffisante entre les positions de chaque mot. Cette marge est appelée slop, un nombre représentant la distance entre les jetons d’une phrase. Supposons que vous essayez de vous rappeler le nom d’un groupe get-together; vous vous rappelez qu’il contenait les mots «Enterprise» et «London», mais vous ne vous rappelez pas du reste du nom. Vous pouvez rechercher la phrase «entreprise london» avec un slop défini sur 1 ou 2 au lieu de 0 par défaut pour trouver les résultats contenant cette phrase sans connaître le titre exact du groupe:

4.2.4. Query Phrase_prefix

Semblable à la query match_phrase, la query match_phrase_prefix vous permet d’aller plus loin et de rechercher une phrase, mais elle permet une correspondance de préfixe sur le dernier terme de la phrase. Ce comportement est extrêmement utile pour fournir une saisie semi-automatique en cours d’exécution pour une zone de recherche, où l’utilisateur obtient des suggestions de recherche lors de la saisie d’un terme de recherche. Lorsque vous effectuer une recherche avec ce type de comportement, il est judicieux de définir le nombre maximal d’expansions pour le préfixe en définissant le paramètre max_expansions afin que la recherche soit renvoyée dans un délai raisonnable.

Dans l’exemple suivant, «elasticsearch den» est utilisé comme query phrase_prefix. Elasticsearch prend le texte «den» et examine toutes les valeurs du champ name pour rechercher celles commençant par «den» («Denver», par exemple). Etant donné que cela pourrait potentiellement retourner un résultat important, le nombre d’expansions devrait être limité:

Les Boolean et Phrase query constituent un excellent choix pour accepter les entrées de l’utilisateur. ils vous permettent d’avoir une entrée utilisateur beaucoup moins sujette aux erreurs, et contrairement à une requête query_string, une requête match n’étouffera pas les caractères réservés comme +, -,? et!

Faire correspondre plusieurs champs avec multi_match
Bien qu’il puisse être tentant de penser que la query multi_match se comporte comme la query term en recherchant plusieurs correspondances dans un champ, son comportement est légèrement différent. Au lieu de cela, il vous permet de rechercher une valeur dans plusieurs champs. Cela peut être utile dans l’exemple get-together où vous pouvez rechercher une chaîne de charactères à la fois dans le nom du groupe et dans la description:

Tout comme la query match peut être transformée en une query phrase, une query préfix ou une query phrase_prefix, la query multi_match peut être transformée en une query phrase ou une query phrase_prefix en spécifiant la clé type. Considérez la query multi_match exactement comme la query match, sauf que vous pouvez spécifier plusieurs champs pour la recherche au lieu d’un seul champ.

Avec toutes les différentes query match, il est possible de trouver un moyen de rechercher presque tout, raison pour laquelle la query match et ses cousines sont considérés comme les types de query de prédilection pour la plupart des utilisations. Nous vous recommandons vivement de les utiliser autant que possible. Nous couvrirons toutes de même quelques autres types de query prises en charge par Elasticsearch.

4.3. Combinaison de query ou de querycomposées

Après avoir étudié et utilisé différents types de query, vous aurez probablement besoin de combiner plusieurs types de query. C’est là que la query bool d’Elasticsearch entre en jeu.

4.3.1. query bool

La query bool vous permet de combiner un nombre illimité de query en une seule query en spécifiant une clause qui indique les parties qui must, should ou should_not correspondre aux données de votre index Elasticsearch:

  • Si vous spécifiez que la partie d’une query bool doit correspondre(must), seuls les résultats correspondant à cette requête (ou à ces requêtes) sont renvoyés.
  • Spécifier qu’une partie d’une query doit correspondre (should) signifie qu’un nombre spécifié de clauses doit correspondre pour qu’un document soit renvoyé.
  • Si aucune clause must n’est spécifiée, au moins une clause should doit correspondre pour que le document soit renvoyé.
  • Enfin, la clause must_not entraîne l’exclusion des documents correspondants.

Le tableau 4.1 répertorie les trois clauses et leurs équivalents binaires.

Comprendre la différence entre must, should et must_not peut être plus facile grâce à un exemple. Dans la figure suivante, vous recherchez des événements auxquels David a participé, Clint ou Andy doivent y participer et ils ne doivent pas être plus âgés que le 30 juin 2013.

4.3.2. filtre bool

La version filtre  de la query bool agit presque exactement comme la version query, mais au lieu de combiner des query, elle combine des filtres. L’équivalent avec un filtre de l’exemple précédent est présenté dans la figure suivante.

Figure 4.21. Combinaison de filtres avec le filtre bool

Comme vous l’avez vu dans la query bool (figure 4.20), le paramètre minimum_should_match de la version querye vous permet de spécifier le nombre minimal de clauses should qui doivent correspondre pour qu’un résultat soit renvoyé. Dans la figure 4.21, la valeur par défaut de 1 est utilisée; le filtre bool ne supporte pas cette propriété.

Améliorer la query bool

La query bool fournie est légèrement artificielle, mais elle inclut les trois options: must, should et must_not. Vous pouvez réécrire cette requête bool dans une meilleure forme, comme ceci:

Notez que cette requête est plus petite que la requête précédente. En inversant la query range de lt (less than) à gte (supérieure ou égale à), vous pouvez la déplacer de la section must_not vers la section must. Vous pouvez également réduire les deux query should en une seule query terms au lieu de deux. Maintenant, vous pouvez remplacer le minimum_should_match de 1 et la clause should en déplaçant également la query terms dans la clause must. Elasticsearch a un langage flexible, n’hésitez donc pas à expérimenter la façon dont les requêtes sont formées lorsque vous les envoyez à Elasticsearch!

Avec la query et le filtre bool à votre actif, vous pouvez combiner un nombre illimité de query et de filtres. Nous pouvons maintenant revenir aux autres types de query prises en charge par Elasticsearch. Vous connaissez déjà la query term, mais que se passe-t-il si vous voulez qu’Elasticsearch analyse les données que vous envoyez? La query match est exactement ce dont vous avez besoin.



L’option minimum_should_match possède des fonctionnalités cachées pour les valeurs par défaut. Si vous spécifiez une clause must, minimum_should_match a la valeur par défaut 0. S’il n’y a pas de clause Must, la valeur par défaut est 1.



4.4. AU-DELÀ DES QUERIES MATCH ET FILTER

Les queries générale dont nous avons discutées jusqu’à présent, telles que query_string et match, sont particulièrement utiles lorsque l’utilisateur est confronté à un champ de recherche, car vous pouvez exécuter une telle requête avec les mots saisis par l’utilisateur.

Pour limiter la portée des recherches, certaines interfaces utilisateur incluent également d’autres éléments en regard de la zone de recherche, tels qu’un widget d’agenda permettant de rechercher des groupes nouvellement créés ou une case à cocher pour le filtre d’événements pour lesquels un emplacement est déjà défini.

4.4.1. Query et filtre Range

La query et le filtre range sont explicites; ils sont utilisés pour rechercher des valeurs dans une certaine plage et peuvent être utilisés pour des nombres, des dates et même des chaînes de charactères.

Pour utiliser la query range, vous spécifiez les valeurs supérieures et inférieures d’un champ. Par exemple, pour rechercher tous les groupes créés après le 1er juin et avant le 1er septembre 2012 dans l’index, utilisez la requête suivante:

Ou vous pouvez utiliser un filtre à la place:

Voir le tableau 4.2 pour la signification des paramètres gt, gte, lt et lte.

La query range prend également en charge les plages de chaînes de charactères. Par conséquent, si vous souhaitez rechercher tous les groupes de rencontre entre « c » et « e », vous pouvez effectuer la recherche en utilisant les éléments suivants:

Lorsque vous utilisez la query, réfléchissez longuement à savoir si un filtre serait un meilleur choix. Étant donné que les documents entrant dans la plage de la query ont une correspondance binaire («Oui, ce document est dans la plage» ou «Non, ce document n’est pas dans la plage»), la query range ne doit pas nécessairement être une question. Pour de meilleures performances, cela devrait être un filtre. Si vous ne savez pas s’il faut faire une query ou un filtre, faites un filtre. Dans 99% des cas, transformer une query range en filtre est la bonne chose à faire.

4.4.2. La query Préfixe et le filtre

Semblable à la query term, la query préfixe et le filtre vous permettent de rechercher un terme contenant le préfixe donné, le préfixe n’ayant pas été analysé avant la recherche. Par exemple, pour rechercher dans l’index tous les événements commençant par «liber», la requête suivante est utilisée:

Et, de même, au lieu d’une query, vous pouvez utiliser un filtre qui a presque la même syntaxe:

Mais attendez! Que se passe-t-il si vous envoyez la même demande mais avec « Liber » au lieu de « liber »? Comme le préfixe de recherche n’a pas été analysé avant d’être envoyé, il ne trouvera pas les termes qui ont été mis en minuscule dans l’index. Cela est dû à la façon dont Elasticsearch analyse les documents et les requêtes, ce que nous allons approfondir au chapitre 5. En raison de ce comportement, la query prefixe utilisé précedemment est un bon compromis. Par exemple, vous pouvez fournir une zone de saisie de catégories lorsque les catégories existantes sont déjà connues. Si un utilisateur tapait des termes faisant partie d’un index, vous pouviez prendre le texte saisi dans un champ de recherche, le mettre en minuscule, puis utiliser une query préfixe pour voir quels autres résultats s’afficheraient. Une fois que vous avez obtenu les résultats de la query préfixe, vous pouvez les proposer sous forme de suggestions lors de la saisie utilisateur. Mais si vous avez besoin d’analyser le terme ou si vous souhaitez que les résultats soient flous, il est probablement préférable de vous en tenir à la requête match_phrase_prefix pour une fonctionnalité de saisie semi-automatique. Nous parlerons davantage de suggestions et les différents types dans l’annexe F.

4.4.3. La query générique(wildcard)

Vous pouvez être tenté de penser à la query générique comme un moyen de faire des recherches avec des expressions régulières, mais en réalité, la query générique est plus proche de la façon dont elle est utilisée dans un shell; par exemple:

correspond à des mots tels que «myfoobar», «foocar» et «thefoodar».

À l’aide d’une chaîne, vous pouvez autoriser Elasticsearch à substituer un nombre quelconque de caractères (y compris aucun) au caractère générique * ou un seul caractère au caractère? joker.

Par exemple, une requête pour «ba * n» correspond à  “bacon,” “barn,” “ban,” et “baboon” , «ban» et «babouin» car le * peut être toute séquence de caractères, alors qu’une requête pour «ba? N» ne correspond qu’à « barn » parce que? doit correspondre à un seul charactère. La figure 4.22 illustre la query wildcard avec un nouvel index appelé wildcard-test.

Vous pouvez également mélanger et assortir plusieurs * et? caractères pour correspondre à un modèle générique plus complexe, mais gardez à l’esprit que lorsqu’une chaîne est analysée, les espaces sont supprimés par défaut, alors? ne peut pas correspondre à un espace si les espaces ne sont pas indexés.

Il convient de noter que l’utilisation de la query wildcard est très couteuses comparées aux queries telles que match; plus tôt un caractère générique (* ou?) apparaît dans le terme de la requête, plus Lucene et Elasticsearch doivent travailler pour le faire correspondre. Prenons, par exemple, le terme de recherche «h *»; Elasticsearch doit maintenant correspondre à chaque terme commençant par «h». Si le terme était « hi * », Elasticsearch n’aurait qu’à rechercher dans chaque terme commençant par « hi », qui est un sous-ensemble plus petit de tous les termes commençant par « h ». En raison de cette surcharge et des considérations de performances, veillez à tester la query wildcard sur une copie de vos données avant de mettre de telle requêtes en production! Nous parlerons davantage d’une query similaire, la query regexp, au chapitre 6, où nous parlons de la recherche avec pertinence.

4.5 RECHERCHE D’EXISTENCE DE CHAMPS AVEC DES FILTRES

Parfois, lors de l’interrogation d’Elasticsearch, il peut être utile de rechercher tous les documents ne contenant pas de champ ou dont la valeur est manquante. Dans l’index get-together, par exemple, vous pouvez rechercher tous les groupes qui n’ont pas été révisés(reviews). Par ailleurs, vous pouvez également rechercher tous les documents contenant un champ, quel que soit son contenu. C’est ici qu’interviennent les filtres exists et missing, qui agissent tous les deux uniquement comme des filtres et non comme des query classiques.

4.5.1. Filtre exists

Comme son nom l’indique, le filtre exists vous permet de filtrer toute requête sur des documents ayant une valeur dans un champ particulier, quelle que soit cette valeur. Voici à quoi il ressemble:

De même, vous pouvez utiliser le filtre missing.

4.5.2. Filtre missing

Le filtre missing vous permet de rechercher des documents sans valeur ou dont la valeur est une valeur par défaut (également appelée valeur NULL ou null_value dans le mapping) spécifiée lors du mapping. Pour rechercher par exemple des documents pour lesquels il manque un champ reviews , utilisez un filtre comme celui-ci:

Si vous souhaitez étendre ce filtre afin qu’il corresponde également aux documents ne contenant pas le champ et pouvant contenir le champ null_value, vous pouvez spécifier une valeur boolean pour les champs existence et null_value. La réponse inclut des documents pour lesquels null_value est définie, comme indiqué dans la figure suivante.

Les filtres missing et exists sont mis en cache par défaut.

4.5.3. Transformer n’importe quelle query en filtre

Jusqu’à présent, nous avons parlé des différents types de query et de filtres pris en charge par Elasticsearch, mais nous nous sommes limités à utiliser uniquement les filtres déjà fournis. Parfois, vous voudrez peut-être utiliser une query telle que query_string, qui n’a pas de filtre équivalent, et la transformer en filtre. Vous en avez rarement besoin, mais vous pouvez l’utiliser si vous avez besoin d’une recherche en texte intégral. Elasticsearch vous permet de faire cela avec le filtre query, qui prend n’importe quelle query en paramêtre et en fait un filtre.

Pour transformer la query query_string recherchant un nom correspondant à « denver clojure » en filtre, vous devez utiliser une recherche comme celle-ci:

En utilisant cela, vous pouvez obtenir certains des avantages d’un filtre (par exemple, ne pas avoir à calculer un score pour cette partie de la requête). Vous pouvez également choisir de mettre ce filtre en cache s’il s’avère être utilisé plusieurs fois. La syntaxe de mise en cache est légèrement différente de l’ajout de la clé _cache, comme indiqué dans la figure suivante.

4.6. CHOISIR LA MEILLEURE REQUETE POUR VOTRE BESOIN

Maintenant que nous avons abordé certaines des query les plus courantes d’Elasticsearch, voyons comment décider des query à utiliser et à quel moment. Bien qu’il n’y ait pas de règles absolue qui vous dira quand et pourquoi  utiliser chacune d’elles. Le tableau 4.3 vous aide à déterminer quelle requête utiliser dans le cas général.

4.7. RÉSUMÉ

Les filtres peuvent accélérer les requêtes en ignorant les calculs d’évaluation et en les mettant en cache. Dans ce chapitre, vous avez appris ce qui suit:

  • Les queries utilisant intuitives sur le plan humain, telles que les queries match et query_string, conviennent aux zones de recherche.
  • La query match est la query de référence pour la recherche en texte intégral, mais la requête query_string est à la fois plus flexible et plus complexe car elle expose toute la syntaxe des requêtes Lucene.
  • La requête match comporte plusieurs sous-types: boolean, phrase et phrase_prefix. La principale différence est que le type boolean correspond à des mots individuels, alors que les types phrases tiennent compte de l’ordre des mots, comme s’ils étaient dans une expression.
  • Les queries spécialisées telles que préfixes et wildcard sont également prises en charge.
  • Pour filtrer les documents dans lesquels un champ n’existe pas, utilisez le filtre missing.
  • Le filtre exists fait exactement le contraire. Il ne renvoie que les documents ayant la valeur de champ spécifiée.

D’autres types de queries sont disponibles pour vous permettre d’ajuster la pertinence de vos résultats. Nous en discuterons au chapitre 6. La correspondance entre les résultats et leur pertinence est fortement influencée par la manière dont le texte est analysé. Le chapitre 5 couvre les détails de l’analyse.