Les requêtes Elasticsearch: Un guide du langage de requête DSL

Les requêtes Elasticsearch: Un guide du langage de requête DSL

La syntaxe des requêtes Elasticsearch peut être difficile et déroutante, même si la recherche est la fonction principale d’Elastic…umm…search. Pour vous aider, ce guide vous présentera les bases des requêtes de recherche courantes pour Elasticsearch et vous préparera à une future réussite de requête.

Elastic a décidé de fermer Elasticsearch et Kibana en mars 2020. En réponse, AWS a bifurqué Elasticsearch et Kibana pour créer OpenSearch et OpenSearch Dashboards, respectivement. Ces projets incluent de nombreuses fonctionnalités qui ne sont disponibles que dans les versions payantes d’Elasticsearch et de Kibana, telles que les contrôles d’accès basés sur les rôles, l’apprentissage automatique pour mettre en évidence les anomalies et les fonctionnalités de sécurité des données. Nous recommandons donc généralement OpenSearch.

Si vous êtes intéressé par OpenSearch, consultez le blog équivalent pour les requêtes OpenSearch.

Toutefois, ce blog traite des requêtes Elasticsearch, alors allons-y.

Syntaxe de requête Lucene

Elasticsearch fait partie de la pile ELK et est basé sur Lucene, la bibliothèque de recherche Apache, et expose la syntaxe de requête Lucene. C’est une partie si intégrante d’Elasticsearch que lorsque vous interrogez la racine d’un cluster Elasticsearch, il vous indique la version de Lucene :

Knowing the Lucene syntax and operators will go a long way in helping you build queries. Its use is in both the simple and the standard query string query. Here are some of the basics:

La connaissance de la syntaxe et des opérateurs Lucene vous aidera grandement à construire des requêtes. Son utilisation se fait dans les requêtes de chaîne de requête simples et standard. Voici quelques bases :

DSL de requête

Le DSL de requête peut être invoqué à l’aide de la plupart des API de recherche d’Elasticsearch. Pour simplifier, nous ne examinerons que l’API de recherche qui utilise le point de terminaison _search. Lors de l’appel de l’API de recherche, vous pouvez spécifier l’indice et/ou le type sur lesquels vous souhaitez effectuer la recherche. Vous pouvez même rechercher sur plusieurs indices en séparant leurs noms par des virgules ou en utilisant des jokers pour faire correspondre plusieurs indices :

Recherchez tous les indices Logstash :

curl localhost:9200/logstash-*/_search

Recherchez dans les indices clients, dans les types bigcorp et smallco :

curl localhost:9200/clients/bigcorp,smallco/_search

Nous utiliserons des recherches avec corps de requête Request Body Searches, les recherches doivent donc être invoquées de la manière suivante :

curl localhost:9200/_search -H "Content-Type: application/json" -d '{"query":{"match": {"my_field":"meaning"}}}'

Recherche URI

Le moyen le plus simple de rechercher votre cluster Elasticsearch est la recherche URI. Vous pouvez passer une requête simple à Elasticsearch en utilisant le paramètre de requête q. La requête suivante recherchera l’intégralité de votre cluster pour les documents ayant un champ de nom égal à “travis” :

curl “localhost:9200/_search?q=name:travis”

Avec la syntaxe Lucene, vous pouvez construire des recherches assez impressionnantes. Habituellement, vous devrez encoder l’URL des caractères tels que les espaces (nous les avons omis dans ces exemples pour plus de clarté) :

curl “localhost:9200/_search?q=name:john~1 AND (age:[30 TO 40} OR surname:K*) AND -city”

Un certain nombre d’options sont disponibles pour personnaliser la recherche URI, notamment en termes d’utilisation de l’analyseur (analyzer), de tolérance aux erreurs de requête (lenient) et de fourniture d’une explication du score (explain).

Bien que la recherche URI soit un moyen simple et efficace d’interroger votre cluster, vous constaterez rapidement qu’elle ne prend pas en charge toutes les fonctionnalités offertes par Elasticsearch. Le véritable pouvoir d’Elasticsearch se manifeste grâce aux recherches de corps de requête. L’utilisation de recherches de corps de requête vous permet de construire une demande de recherche complexe à l’aide de différents éléments et clauses de requête qui correspondront, filtreront, trieront et manipuleront des documents en fonction de critères multiples.

La recherche de corps de requête

La recherche de corps de requête utilise un document JSON contenant divers éléments pour créer une recherche sur votre cluster Elasticsearch. Non seulement vous pouvez spécifier des critères de recherche, mais vous pouvez également spécifier la plage et le nombre de documents que vous souhaitez obtenir, les champs que vous souhaitez, ainsi que diverses autres options.

Le premier élément d’une recherche est l’élément de requête qui utilise le DSL de requête. L’utilisation du DSL de requête peut parfois être déroutante car il peut être utilisé pour combiner et construire des clauses de requête en une requête pouvant être profondément imbriquée. Comme la plupart de la documentation Elasticsearch ne fait référence qu’aux clauses individuellement, il est facile de perdre de vue où les clauses doivent être placées.

À lire aussi  Comment envoyer de l'argent sur un compte à l'étranger : Tout ce que vous devez savoir

Pour utiliser le DSL de requête, vous devez inclure un élément “query” dans le corps de votre recherche et le remplir avec une requête construite à l’aide du DSL :

{"query": { "match": { "my_field": "meaning" } } }

Dans ce cas, l’élément “query” contient une clause de requête “match” qui recherche le terme “meaning” dans le champ “my_field” de tous les documents de votre cluster.

L’élément de requête est utilisé avec d’autres éléments dans le corps de recherche :

{ "query": { "match": { "my_field": "meaning" } }, "fields": [ "name", "surname", "age" ], "from": 100, "size": 20 }

Ici, nous utilisons l’élément “fields” pour restreindre les champs à renvoyer, et les éléments “from” et “size” pour indiquer à Elasticsearch que nous recherchons des documents de 100 à 119 (à partir de 100 et comptant 20 documents).

Champs

Vous cherchez peut-être des événements où un champ spécifique contient certains termes. Vous spécifiez le champ, tapez deux-points, puis un espace, puis la chaîne entre guillemets ou la valeur sans guillemets. Voici quelques exemples de champs Lucene :

  • name: “Ned Stark”
  • status: 404

Faites attention aux valeurs avec des espaces comme “Ned Stark”. Vous devrez les mettre entre guillemets doubles pour vous assurer que la valeur entière est utilisée.

Filtres vs Requêtes

Les personnes ayant utilisé Elasticsearch avant la version 2 sont familières avec les filtres et les requêtes. Vous deviez construire un corps de requête en utilisant à la fois des filtres et des requêtes. La différence entre les deux est que les filtres étaient généralement plus rapides car ils vérifiaient uniquement si un document correspondait ou non, mais pas à quel point il correspondait bien. En d’autres termes, les filtres donnent une réponse booléenne tandis que les requêtes renvoient un score calculé indiquant à quel point un document correspond à une requête.

Score

Nous avons mentionné le fait qu’Elasticsearch renvoie un score avec tous les documents correspondants à une requête :

curl “localhost:9200/_search?q=application” "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": { "value": 1, "relation": "eq" }, "max_score": 2.3, "hits": [ { "_index": "logstash-2016.04.04", "_type": "_doc", "_id": "1", "_score": 2.3, "_source": { "message": "Log message from my application" } } ] } }

Ce score est calculé pour les documents dans Elasticsearch en fonction des requêtes fournies. Des facteurs tels que la longueur d’un champ, la fréquence à laquelle le terme spécifié apparaît dans le champ et (dans le cas des recherches avec joker et flou) la proximité du terme par rapport à la valeur spécifiée influencent tous le score. Le score calculé est ensuite utilisé pour trier les documents, généralement du score le plus élevé au plus bas, et les documents ayant les scores les plus élevés sont ensuite renvoyés au client. Il existe différentes façons d’influencer les scores des différentes requêtes, comme le paramètre de boost. Cela est particulièrement utile si vous voulez que certaines requêtes dans une requête complexe aient plus de poids que d’autres et que vous recherchez les documents les plus significatifs.

Lorsqu’une requête est utilisée dans un contexte de filtre (comme expliqué précédemment), aucun score n’est calculé. Cela offre des performances améliorées généralement associées à l’utilisation de filtres, mais ne fournit pas les fonctionnalités d’ordonnancement et de signification associées aux scores.

Requêtes de niveau de terme

1. Requêtes de plage

Vous pouvez rechercher des champs dans une plage spécifique, en utilisant des crochets pour les recherches inclusives et des accolades pour les recherches exclusives :

  • age:[3 TO 10] – Renvoie des événements dont l’âge est compris entre 3 et 10
  • price:{100 TO 400} – Renvoie des événements avec des prix compris entre 101 et 399
  • name: [Adam TO Ziggy] – Renvoie des noms entre Adam et Ziggy, y compris ces noms

Comme vous pouvez le voir dans les exemples ci-dessus, vous pouvez également utiliser des plages dans des champs non numériques tels que les chaînes de caractères et les dates.

2. Requêtes avec joker

Une recherche ne serait pas une recherche sans les jokers. Vous pouvez utiliser le caractère * pour des jokers de plusieurs caractères ou le caractère ? pour des jokers d’un caractère :

  • Ma?s – Correspondra à Mars, Mass et Maps
  • Ma*s – Correspondra à Mars, Matches et Massachusetts

3. Requêtes regex (regexp)

Les requêtes regex (regexp) vous offrent encore plus de puissance. Placez simplement votre regex entre des barres obliques (/) :

  • /p[ea]n/ – Correspondra à la fois à pen et à pan
  • /<.+>/ – Corresponsra à du texte qui ressemble à une balise HTML
À lire aussi  Les diagrammes de processus : un outil essentiel pour visualiser et optimiser votre travail

4. Requêtes floues

La recherche floue utilise la distance de Damerau-Levenshtein pour faire correspondre des termes similaires en orthographe. C’est idéal lorsque votre ensemble de données contient des mots mal orthographiés.

Utilisez le tilde (~) pour trouver des termes similaires :

  • blow~ – Renvoie des résultats tels que “blew”, “brow” et “glow”

Utilisez le tilde (~) avec un nombre pour spécifier la distance maximale entre les mots :

  • john~2 – Corresponsra, entre autres, à “jean”, “johns”, “jhon” et “horn”

5. Texte libre

C’est aussi simple que cela. Tapez le terme ou la valeur que vous voulez trouver. Cela peut être un champ, une chaîne dans un champ, etc.

6. Requête de terme Elasticsearch

Également appelée requête de terme, elle renverra une correspondance exacte pour un terme donné. Prenez cet exemple d’une base de données de statistiques de baseball :

POST /mlb_index/_search
{
  "query": {
    "term" : {
        "pitcher_last": "rivera",
        "pitcher_first": "mariano",
        "boost": 1.0
    }
  }
}

Assurez-vous d’utiliser la requête de terme ici, PAS la requête de texte. La requête de terme recherchera la correspondance exacte ; la requête de texte filtrera automatiquement la ponctuation.

7. Requête de jeu de termes Elasticsearch

Similaire à la requête de terme, la requête de jeu de termes peut rechercher plusieurs valeurs en fonction de certaines conditions définies dans la requête PUT. Poursuivons l’exemple du baseball :

PUT /pitchers
{
  "mappings": {
    "properties": {
      "pitcher_last": {
        "type": "keyword"
      },
      "pitcher_first": {
        "type": "keyword"
      },
      "pitch_type": {
        "type": "keyword"
      }
    }
  }
}

Requêtes composites

Opérateurs booléens et la requête Bool

Comme la plupart des langages informatiques, Elasticsearch prend en charge les opérateurs AND, OR et NOT :

  • jack AND jill – Renvoie des événements contenant à la fois jack et jill
  • ahab NOT moby – Renvoie des événements contenant ahab mais pas moby
  • tom OR jerry – Renvoie des événements contenant tom ou jerry, ou les deux

Bien qu’il existe plusieurs types de clauses de requête, celle que vous utiliserez le plus est la requête Bool car elle permet de combiner plusieurs clauses pour créer des requêtes complexes.

La requête Bool est probablement la plus utilisée car elle peut combiner les fonctionnalités de certaines autres clauses de requête composites telles que les clauses And, Or, Filter et Not. Elle est tellement utilisée que ces quatre clauses ont été progressivement remplacées par la requête Bool dans diverses versions. Son utilisation est mieux expliquée par un exemple :

curl localhost:9200/_search -d {
  "query": {
    "bool": {
      "must": [
        {"fuzzy": {"name": "john", "fuzziness": 2}}
      ],
      "must_not": [
        {"match": {"city": "nyc"}}
      ],
      "should": [
        {"range": {"age": {"from": 30, "to": 40}}},
        {"wildcard": {"surname": "K*"}}
      ]
    }
  }
}

Dans l’élément de requête, nous avons ajouté la clause booléenne qui indique qu’il s’agit d’une requête booléenne. Il y a beaucoup de choses là-dedans, nous allons donc la couvrir clause par clause, en commençant par le haut :

must

Toutes les requêtes dans cette clause doivent correspondre à un document pour qu’ES le renvoie. Pensez à cela comme vos requêtes AND. La requête que nous avons utilisée ici est la requête floue, et elle correspondra à tous les documents ayant un champ de nom qui correspond à “john” de manière floue. Le paramètre “fuzziness” supplémentaire indique à Elasticsearch qu’il doit utiliser une distance de Damerau-Levenshtein de 2 pour déterminer la flou.

must_not

Tous les documents qui correspondent à la requête dans cette clause seront exclus de l’ensemble de résultats. Il s’agit de l’opérateur de requête NOT ou moins (-) du DSL de requête. Dans ce cas, nous effectuons une simple requête match, recherchant des documents contenant le terme “city”. Il s’agit de la clause must_not, donc les documents correspondants seront exclus.

should

Jusqu’à présent, nous avons traité des absolus : must et must_not. Should n’est pas absolu et équivaut à l’opérateur OR. Elasticsearch renverra tous les documents qui correspondent à une ou plusieurs des requêtes de la clause should.

La première requête que nous avons fournie recherche des documents où le champ d’âge est compris entre 30 et 40. La deuxième requête effectue une recherche avec joker sur le champ de nom, cherchant des valeurs qui commencent par “K”.

La requête contenait trois clauses différentes, donc Elasticsearch ne renverra que les documents qui correspondent aux critères de toutes ces clauses. Ces requêtes peuvent être imbriquées, vous pouvez donc construire des requêtes très complexes en spécifiant une requête booléenne en tant que requête must, must_not, should ou filter.

filter

Il reste une clause de requête composite que nous n’avons pas discutée, la clause de filtre. Voici un exemple où nous l’utilisons :

curl localhost:9200/_search -d { "query": { "bool": { "must": [ { "match_all": {}}, ], "filter": [ { "term": { "email": "joe@bloggs.com" }} ] } } }

La requête “match_all” dans la clause must indique à Elasticsearch de renvoyer tous les documents. Cela peut ne pas sembler être une recherche très utile, mais elle est pratique lorsque vous l’utilisez conjointement avec un filtre comme nous l’avons fait ici. Le filtre que nous avons spécifié est une requête de terme, demandant tous les documents contenant un champ d’e-mail avec la valeur “joe@bloggs.com”.

À lire aussi  Beyond Body: Comprendre ce programme de remise en forme

Nous avons utilisé un filtre pour spécifier les documents que nous voulons, ils seront donc tous renvoyés avec un score de 1. Les filtres n’entrent pas dans le calcul des scores, donc la requête match_all donne à tous les documents un score de 1.

Une chose à noter est que cette requête ne fonctionnera pas si le champ d’e-mail est analysé, ce qui est la valeur par défaut des champs dans Elasticsearch. La raison en serait mieux discutée dans un autre article de blog, mais cela revient au fait qu’Elasticsearch analyse à la fois les champs et les requêtes lorsqu’ils sont envoyés. Dans ce cas, le champ d’e-mail se décompose en trois parties : joe, bloggs et com. Cela signifie qu’il correspondra aux recherches et aux documents pour les trois de ces termes.

Requêtes de boost

Il existe trois types de requêtes de boost dans Elasticsearch : les requêtes positives, négatives et négatives_boost. Les requêtes positives sont en réalité les requêtes principales auxquelles vous souhaitez accumuler des points de score de pertinence.

Le negative_boost est une valeur comprise entre 0 et 1, que vous multiplierez par les résultats négatifs de la requête (si vous définissez le negative_boost à 0,25, cela réduit la valeur de la requête négative à un quart de la requête positive ; 0,5 pour la moitié de la requête positive ; 0,1 pour un dixième de la valeur de la requête positive, etc. Cela vous donne une grande flexibilité pour scinder vos requêtes.

Requêtes de score constant

C’est un outil précieux pour segmenter certaines requêtes auxquelles vous souhaitez donner un coup de pouce. Le code “constant_score” : {} isole certains termes de recherche et les associe à une valeur de boost séparée :

GET /_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          “type”: "nginx"
        }
      },
      "boost": 1.5
    }
  }
}

Ainsi, dans cet exemple, vous accordez une plus grande valeur à tous les journaux NGINX qu’à d’autres (probablement à d’autres journaux de serveur tels que les journaux apache2 ou les journaux IIS).

Requêtes de score de disjonction maximale

Imaginez si les résultats de votre recherche Google pouvaient être séparés entre les résultats qui incluent plusieurs choses que vous recherchez et seulement quelques-unes. C’est ce que cela fait.

Vous pouvez regrouper les requêtes ensemble en tant que champs imbriqués dans le paramètre “queries”: [].

GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "term": {
            "type": "nginx"
          }
        },
        {
          "term": {
            "fuzzy": "server"
          }
        }
      ],
      "tie_breaker": 0.5
    }
  }
}

Requêtes de score de fonction

Les requêtes de score de fonction, comme leur nom l’indique, facilitent l’utilisation d’une fonction pour calculer un score. Définissez une requête et définissez les règles pour augmenter le score d’un résultat.

Performances des requêtes

Les performances de votre requête Elasticsearch dépendront en grande partie de l’efficacité de votre requête, que vous pouvez apprendre grâce aux bonnes pratiques décrites dans les sections précédentes.

La performance de la requête sera également impactée par les configurations en arrière-plan, notamment l’allocation des ressources, la mise en mémoire tampon des données et la fluidité de la connexion entre tous les composants du pipeline de données.

Les requêtes mal conçues peuvent non seulement entraîner un retour lent des résultats, mais elles peuvent également entraîner une tension importante sur votre pile ELK et même provoquer un crash de celle-ci.

Mettre en place un pipeline de données ELK pour obtenir des requêtes rapides n’est pas insurmontable. Surtout pour les implémentations ELK à petite échelle (quelques nœuds de votre cluster), des milliers d’équipes d’ingénieurs utilisent ELK pour rechercher rapidement leurs données de journal.

Mais à mesure que les volumes de journaux augmentent, certaines parties du pipeline de données peuvent être mises à rude épreuve et dégrader les performances des requêtes. Peut-être qu’un index doit être partagé, peut-être que la connexion entre Kibana et Elasticsearch est rompue, peut-être que le pipeline a besoin d’un composant de mise en file d’attente de données. Les facteurs qui peuvent influencer les performances des requêtes sont nombreux et variés.

C’est pourquoi le logiciel de gestion des journaux Logz.io propose OpenSearch en tant que service. Les utilisateurs d’ELK peuvent facilement migrer leurs données vers le service cloud de qualité entreprise de Logz.io pour l’ingestion, le stockage, le traitement et l’analyse, le tout sans les difficultés de mise à l’échelle et de gestion de l’ensemble du pipeline de données. L’architecture nativement cloud de Logz.io a été optimisée pendant des années pour offrir des requêtes de journal rapides et fiables.

Conclusion

La chose la plus difficile à propos d’Elasticsearch est la profondeur et l’étendue des fonctionnalités disponibles. Nous avons essayé de couvrir les éléments essentiels aussi en détail que possible sans vous submerger d’informations.

Posez toutes les questions que vous pourriez avoir dans les commentaires et recherchez des articles approfondis sur certaines des fonctionnalités que nous avons mentionnées. Vous pouvez également lire mon précédent tutoriel sur Elasticsearch pour en savoir plus.