menu icon

Java pour Elasticsearch, épisode 1. Requêter le cluster

Découvrir comment intégrer Elasticsearch dans votre code est une aventure passionnante et, en réalité, plus simple qu'il n'y paraît. Dans ce premier article de notre série, nous allons explorer ensemble la mise en place d'un cluster de trois nœuds et la manière de s'y connecter en utilisant des certificats auto-générés, démontrant ainsi l'accessibilité et l'efficacité de ce processus.

Java pour Elasticsearch, épisode 1.  Requêter le cluster

Elasticsearch est un moteur de recherche très puissant et la mise en place d’une stack de développement n’est pas toujours simple.

Dans cette série d’articles, nous allons progresser pas à pas pour en percevoir un large éventail de possibilités.

Nous aborderons des sujets comme le requêtage, l’administration d’un cluster ou la recherche vectorielle.

Le code présenté est disponible sur Gitlab. La branche correspondante à cet article est la branche 01-dev-elastic.

Etape 1 : Déployer un cluster Elasticsearch local

Une des principales caractéristiques d’Elasticsearch est de pouvoir être déployé dans un cluster composé de plusieurs noeuds (nodes) ce qui permet plusieurs choses :

  • Assurer une scalabilité horizontale

  • Spécialiser certains nodes en fonction des besoins

  • Assurer une continuité de services

  • etc.

La communication avec un cluster se fait en http sur le port 9200.

Afin de nous approcher des conditions de prod, nous allons donc commencer par mettre en place un cluster comportant 3 nodes, non spécialisés.

Nous mettrons également en place un Kibana qui permet, entre autres choses, d’administrer le cluster, de le requêter ou de visualiser les données.

Afin de sécuriser les échanges avec le cluster, nous utiliserons TLS et des certificats auto-générés.

En effet, ne pas sécuriser les échanges serait inconcevable dans une environnement de production.

Afin de simplifier ce déploiement, nous utiliserons Docker.

Elastic fourni un fichier docker-compose sur son site.

Celui qui est proposé ici en est très fortement inspiré, seules quelques légères modifications ont été apportées.

Il faut noter que le dossier référencé dans le fichier .env dans la variable DATA_PATH doit être accessible à l’utilisateur qui lance Docker sur votre système, surtout si ce système est basé sur Linux.

Une fois le fichier .env modifié pour répondre à vos besoins, il est possible de lancer un

docker compose up -d

Une fois lancé, deux vérifications sont possibles.

D’abord dans un terminal, avec une commande curl

curl -k -X GET "https://localhost:9200/" --user "elastic:elasticpwd"

Ce qui doit renvoyer la réponse suivante :

{
"name" : "es01",  
  "cluster_name" : "adlean-cluster",
  "cluster_uuid" : "0ku-2DJrRKObuCUMT4nTfA",
  "version" : {
    "number" : "8.11.3",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "64cf052f3b56b1fd4449f5454cb88aca7e739d9a",
    "build_date" : "2023-12-08T11:33:53.634979452Z",
    "build_snapshot" : false,
    "lucene_version" : "9.8.0",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

Ensuite, se connecter à Kibana à l’adresse http://localhost:5601 qui va alors afficher la page de connexion.

Une fois connecté avec l’utilisateur “elastic”, la page d’accueil propose d’essayer des données d’exemple (Try sample data)

alt text
Page d'accueil de Kibana avec le choix 'Try sample data'

Sur la page de choix des données, il y a un menu “Other sample data sets” dans lequel nous allons choisir d’importer “Sample eCommerce orders” en cliquant sur le bouton “Add data”.

alt text
Choix des données d'exemple à importer

Etape 2 : Créer le projet java

Nous allons partir sur un projet Maven en Java 21, avec Spring Boot 3.2.

Pour ne pas se déconcentrer sur la création d’un interface utilisateur, nous utiliserons les tests unitaires afin de lancer nos différentes requêtes.

Le POM va donc ressembler à ça

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.adelean</groupId>
    <artifactId>dev-elastic-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>01-dev-elastic</name>
    <description>01-dev-elastic</description>
    <properties>
        <java.version>21</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>8.11.2</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.7.1</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Etape 3 : Créer la connexion à Elasticsearch

Pour créer la connexion, il faut utiliser le client java développé par Elastic.

Cependant, comme nous sommes sur un cluster utilisant SSL, il va falloir travailler avec les certificats.

Dans le répertoire défini dans la variable PATH_DATA du fichier .env, de nouveaux répertoires ont été créés.

Le certificat qui nous intéresse se trouve dans $PATH_DATA/certs/ca/ca.crt

Nous avons besoin de son empreinte.

Elle peut être obtenue de deux manières.

Soit en regardant dans les logs d’un node lors de son premier démarrage.

Soit en passant la commande suivante :

openssl s_client -connect localhost:9200 -servername localhost -showcerts </dev/null 2>/dev/null \
  | openssl x509 -fingerprint -sha256 -noout -in /dev/stdin

Dans src/main/resources, nous allons alimenter le fichier application.properties avec les éléments suivants :

elastic.host=localhost  
elastic.port=9200  
elastic.ca.fingerprint=<empreinte obtenue avec la commande précédente>

Une fois fait, on peut créer une classe de service qui va pouvoir instancier le client et l’utiliser.

package com.adelean.develastic01.services.supervision;

/* imports*/

@Service("IndicesService")
public class Indices {

    @Value("${elastic.host}")
    private String elasticHost;

    @Value("${elastic.port}")
    private int elasticPort;

    @Value("${elastic.ca.fingerprint}")
    private String fingerPrint;

    @Value("${elastic.user}")
    private String elasticUser;

    @Value("${elastic.password}")
    private String elasticPassword;

    private ElasticsearchClient getClient() {

        SSLContext sslContext = TransportUtils.sslContextFromCaFingerprint(fingerPrint);
        BasicCredentialsProvider credsProv = new BasicCredentialsProvider();
        credsProv.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(elasticUser, elasticPassword));

        RestClient restClient = RestClient
                .builder(new HttpHost(elasticHost, elasticPort, "https"))
                .setHttpClientConfigCallback( ccb -> ccb.setSSLContext(sslContext).setDefaultCredentialsProvider(credsProv))
                .build();

        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());

        return new ElasticsearchClient(transport);

    }

}

La classe TransportUtils est un utilitaire qui permet d’obtenir simplement un contexte SSL depuis l’empreinte d’un certificat ou depuis le fichier du certificat lui-même.

La première requête que nous allons coder va permettre de récupérer la liste des indexes présent sur le cluster.

public Set<String> listIndices() {

    GetIndexRequest request = new GetIndexRequest.Builder().index("*").build();

    try {
        GetIndexResponse response = getClient().indices().get(request);
        Map<String, IndexState> indices = response.result();

        return indices.keySet();

    } catch (IOException e) {
        throw new RuntimeException(e);
    }

}

Une fois fait, il ne reste plus qu'à créer la classe de test pour vérifier ce que nous renvoie cette première requête.

package com.adelean.develastic01.services.supervision;

/* imports */

@SpringBootTest
class IndicesTest {

    @Autowired
    private Indices indicesService;

    @Test
    void listAllIndices() {

        var result = indicesService.listIndices();

        assertTrue(result.size() > 1);

    }

}

La liste des index va ressembler à ça :

  • .internal.alerts-ml.anomaly-detection.alerts-default-000001
  • .internal.alerts-observability.slo.alerts-default-000001
  • kibana_sample_data_ecommerce
  • .internal.alerts-observability.metrics.alerts-default-000001
  • .kibana-observability-ai-assistant-kb-000001
  • .internal.alerts-observability.logs.alerts-default-000001
  • .internal.alerts-observability.uptime.alerts-default-000001
  • .internal.alerts-observability.apm.alerts-default-000001
  • .internal.alerts-stack.alerts-default-000001
  • .internal.alerts-security.alerts-default-000001
  • .internal.alerts-observability.threshold.alerts-default-000001
  • .kibana-observability-ai-assistant-conversations-000001

Au milieu de cette liste, on retrouve le kibana_sample_data_ecommerce.

Dans la console Kibana, accessible via le menu “Management” -> “Dev Tools”, la requête équivalente est

GET _cat/indices

Dans le prochain article, nous verrons comment améliorer ce code et faire d’autres requêtes.

Java pour Elasticsearch, épisode 2. Chercher des données

05/01/2025

Mutualiser le code créé précédemment puis chercher des données sur des critères précis. Utiliser la notation lambda pour l'utilisation de la boîte à outils. Stocker les résutats dans des objets spécifiques.

Lire l'article

Retour de la conférence DevFest Toulouse

19/11/2023

Nous sommes de retour du DevFest Toulouse, occasion pour nous d'assister à plusieurs conférences, nous former et partager une version personnalisée de notre presentation Cloner ChatGPT avec Hugging Face et Elasticsearch.

Lire l'article

Plonger dans le NLP avec Elastic

01/04/2023

Un aperçu sur le NLP et un guide pratique sur la façon dont il peut être utilisé avec la Suite Elastic pour améliorer les capacités de recherche.

Lire l'article

Assurer la scalabilité d’un moteur de recherche pour des milliers de magasins en ligne – retour sur la conférence ElasticON

10/03/2023

Retour sur la présentation Assurer la scalabilité d’un moteur de recherche pour des milliers de magasins en ligne par Roudy Khoury et Aline Paponaud à ElasticON 2023

Lire l'article

Question answering, une approche plus humaine à nos recherches sur all.site.

19/01/2023

Tout sur les Question-Answering et comment l'implémenter en utilisant flask et elasticsearch.

Lire l'article

Retour d’Expérience - Fine-tuning d’un modèle VOSK

05/01/2022

all.site est un moteur de recherche collaboratif. Il fonctionne comme Bing ou Google mais il a l’avantage de pouvoir aller plus loin en indexant par exemple les contenus média et en organisant les données de systèmes comme Slack, Confluence ou l’ensemble des informations présentes dans l’intranet d’une entreprise.

Lire l'article

Retour d’Expérience - Indexation des transcriptions de fichiers média

17/12/2021

all.site est un moteur de recherche collaboratif. Il fonctionne comme Bing ou Google mais il a l’avantage de pouvoir aller plus loin en indexant par exemple les contenus média et en organisant les données de systèmes comme Slack, Confluence ou l’ensemble des informations présentes dans l’intranet d’une entreprise.

Lire l'article

La revue de presse du 25 Novembre 2021

25/11/2021

Bientôt le weekend, bientôt l'hiver, alors une petite revue de presse pour occuper vos longues soirées...

Lire l'article

Nouveau meetup Search & Data - E-Commerce Search et Open Source

28/10/2021

La cinquième édition du meetup Search and Data est dédiée au search e-commerce et à l'open source. Un bel agenda pour cette édition de rentrée et de reprise.

Lire l'article

Expédition vers Synonym Graph dans Elasticsearch

21/04/2021

Dans cet article, nous expliquons comment nous sommes passés des anciens filtres de synonymes d'Elasticsearch aux nouveaux filtres de type graphe, les Synonym Graph Token Filter.

Lire l'article

Quand les requêtes sont très verbeuses

22/02/2021

Dans cet article, nous présentons une méthode simple pour réécrire les requêtes utilisateurs afin qu'un moteur de recherche basé sur des mots clés puisse mieux les comprendre. Cette méthode est très utile dans le contexte d'une recherche vocale ou une conversation avec un chatbot, contexte dans lequel les requêtes utilisateur sont généralement plus verbeuses.

Lire l'article

Enrichir les données et réécrire les requêtes avec le percolator Elasticsearch

26/04/2019

Cet article est une transcription de notre intervention cette semaine à Haystack - une conférence sur l'amélioration de la pertinence des moteurs de recherche. Nous avons montré une méthode permettant d'enrichir et de réécrire les requêtes des utilisateurs en utilisant Wikidata et le percolator Elasticsearch.

Lire l'article

A2 le moteur qui sublime Elasticsearch

13/06/2018

Elasticsearch est une technologie ouverte qui permet aux intégrateurs de construire des solutions toujours plus innovantes et puissantes.

Lire l'article