Fermer

août 22, 2022

Spring boot avec Java API Client pour créer et exécuter des requêtes dans Elasticsearch.


Spring boot avec ES Java API Client pour créer et exécuter des requêtes dans Elasticsearch.

Conditions préalables:

Connaissances en Java, Spring boot, Elasticsearch, Kibana.

Concept:

Le but de ce blog est de présenter une idée pour connecter, construire des requêtes et interroger Elasticsearch via des applications Java.

Qu’est-ce qu’Elasticsearch :

Elasticsearch est un moteur de recherche et d’analyse distribué, gratuit et ouvert pour tous les types de données, y compris textuelles, numériques, géospatiales, structurées et non structurées. Il s’agit du composant central d’Elastic Stack, une collection d’outils gratuits et ouverts pour l’ingestion, l’enrichissement, le stockage, l’analyse et la visualisation de données, et est connu pour ses API REST simples, sa nature distribuée, sa vitesse et son évolutivité.

Technologies utilisées :

  1. Recherche élastique 8.3.3
  2. Botte à ressort 2.7.2
  3. Java 1.8
  4. Client d’API Java Elasticsearch 7.17.5
  5. Maven

Les outils utilisés:

  1. Kibana 8.3.3
  2. Facteur

Remarque : Le blog se concentre uniquement sur une partie de l’opération CRUD. Cliquez ici pour le code source complet avec les opérations CRUD.

Structure du projet :

Structure du projet

Étape 1: Créez une application de démarrage Spring à l’aide de Spring Initalizr et sélectionnez les dépendances comme indiqué dans l’instantané ci-dessous.

Initialisation du printemps

Étape 2: Ajoutez les dépendances supplémentaires indiquées dans le pom.xml fichier ci-dessous.

<?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>2.7.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.poc.es</groupId>
    <artifactId>elasticsearch-springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>elasticsearch-springboot</name>
    <description>Demo project for integrating elasticsearch with springboot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

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

        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
            <version>2.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
                <version>${project.parent.version}</version>
            </plugin>
        </plugins>
    </build>

</project>

Étape 3: Configuration d’Elasticsearch dans l’application de démarrage Spring.

application.yml

elastic:
  index: employees
es:
  hostname: localhost
  port: 9200
  username: admin
  password: password

ESRestClient.java

package com.poc.es.elasticsearchspringboot.config;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.Getter;
import lombok.Setter;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
@ConfigurationProperties("es")
@Getter
@Setter
public class ESRestClient {

    private String hostName;
    private int port;
    private String username;
    private String password;

    @Bean
    public ElasticsearchClient getElasticSearchClient() {

        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials(username, password));

        RestClientBuilder builder = RestClient.builder(new HttpHost(hostName, port))
                .setHttpClientConfigCallback(httpClientBuilder ->
                        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));

        // Create the low-level client
        RestClient restClient = builder.build();

        // Create the transport with a Jackson mapper
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());

        // And create the API client
        return new ElasticsearchClient(transport);
    }
}

Étape 4: Créer un contrôleur de repos ESRestController.java

@Autowired
private ESService esService;

@PostMapping("/index/fetchWithMust")
public ResponseEntity<List<Employee>> fetchEmployeesWithMustQuery(@RequestBody Employee employeeSearchRequest) throws IOException {
    List<Employee> employees = esService.fetchEmployeesWithMustQuery(employeeSearchRequest);
    return ResponseEntity.ok(employees);
}

@PostMapping("/index/fetchWithShould")
public ResponseEntity<List<Employee>> fetchEmployeesWithShouldQuery(@RequestBody Employee employeeSearchRequest) throws IOException {
    List<Employee> employees = esService.fetchEmployeesWithShouldQuery(employeeSearchRequest);
    return ResponseEntity.ok(employees);
}

Étape 5 : Créer un modèle Employé.java.

package com.poc.es.elasticsearchspringboot.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
    private String gender;
    private String jobTitle;
    private String phone;
    private Integer size;
}

Étape 6 : Créer une interface ESService.java et ESServiceImpl.java.

@Service
public class ESServiceImpl implements ESService {
  
  @Autowired
  private ESClientConnector esClientConnector;

  @Override
  public List<Employee> fetchEmployeesWithMustQuery(Employee employee) throws IOException {
         return esClientConnector.fetchEmployeesWithMustQuery(employee);
  }

  @Override
  public List<Employee> fetchEmployeesWithShouldQuery(Employee employee) throws IOException {
         return esClientConnector.fetchEmployeesWithShouldQuery(employee);
  }
}

Étape 7 : Créer une classe de connecteur qui effectue des appels d’API Elasticsearch ESClientConnector.java.

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

@Autowired
private ElasticsearchClient elasticsearchClient;   

public List<Employee> fetchEmployeesWithMustQuery(Employee employee) throws IOException {
     List<Query> queries = prepareQueryList(employee);
     SearchResponse<Employee> employeeSearchResponse = elasticsearchClient.search(req->
             req.index(index)
                     .size(employee.getSize())
                     .query(query->
                             query.bool(bool->
                                     bool.must(queries))),
             Employee.class);

     return employeeSearchResponse.hits().hits().stream()
             .map(Hit::source).collect(Collectors.toList());
     }

 public List<Employee> fetchEmployeesWithShouldQuery(Employee employee) throws IOException {
     List<Query> queries = prepareQueryList(employee);
     SearchResponse<Employee> employeeSearchResponse = elasticsearchClient.search(req->
                     req.index(index)
                             .size(employee.getSize())
                             .query(query->
                                     query.bool(bool->
                                             bool.should(queries))),
             Employee.class);

     return employeeSearchResponse.hits().hits().stream()
             .map(Hit::source).collect(Collectors.toList());
     }

 private List<Query> prepareQueryList(Employee employee) {
      Map<String, String> conditionMap = new HashMap<>();
      conditionMap.put("firstName.keyword", employee.getFirstName());
      conditionMap.put("lastName.keyword", employee.getLastName());
      conditionMap.put("gender.keyword", employee.getGender());
      conditionMap.put("jobTitle.keyword", employee.getJobTitle());
      conditionMap.put("phone.keyword", employee.getPhone());
      conditionMap.put("email.keyword", employee.getEmail());

      return conditionMap.entrySet()
                          .stream()
                          .filter(entry->!ObjectUtils.isEmpty(entry.getValue()))
                          .map(entry->QueryBuilderUtils.termQuery(entry.getKey(), entry.getValue()))
                          .collect(Collectors.toList());
     }

Étape 8 : Créer une interface Util pour créer des requêtes ES QueryBuilderUtils.java

package com.poc.es.elasticsearchspringboot.utils;

import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryVariant;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;

public interface QueryBuilderUtils {

    public static Query termQuery(String field, String value) {
        QueryVariant queryVariant = new TermQuery.Builder()
                .caseInsensitive(true)
                .field(field).value(value).build();
        return new Query(queryVariant);
    }
}

Appels d’API via Postman :

Récupérez les données d’Elasticsearch à l’aide de la clause MUST :

Aller chercher

Journaux : Requête ES construite avec « DEVOIR » clause en Java

POST /employees/_search?typed_keys=true {
    "query": {
        "bool": {
            "must": [
                {
                    "term": {
                        "jobTitle.keyword": {
                            "value": "Senior Developer",
                            "case_insensitive": true
                        }
                    }
                },
                {
                    "term": {
                        "gender.keyword": {
                            "value": "Female",
                            "case_insensitive": true
                        }
                    }
                }
            ]
        }
    }
}

Récupérez les données d’Elasticsearch à l’aide de la clause SHOULD :

Aller chercher avec devrait

Journaux : Requête ES construite avec « DEVRAIT » clause en Java

POST /employees/_search?typed_keys=true {
    "query": {
        "bool": {
            "should": [
                {
                    "term": {
                        "jobTitle.keyword": {
                            "value": "Senior Developer",
                            "case_insensitive": true
                        }
                    }
                },
                {
                    "term": {
                        "gender.keyword": {
                            "value": "Female",
                            "case_insensitive": true
                        }
                    }
                }
            ]
        }
    }
}

URL Github du projet : https://github.com/sundharamurali/elasticsearch-springboot.git






Source link