Intégration native d'Elasticsearch dans Spring Boot

Configuration des dépendances

Modifiez votre descripteur de construction Maven pour inclure le module ddeicated aux bases de recherche :

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  <version>2.7.5</version>
</dependency>

Assignation des paramètres de connexion

Le service attend une adresse réseau via la configuration de l'application :

elasticsearch:
  uri: localhost:9200

Création de la configuration du client

L'injection se fait en surchargeant la méthode abstraite fournie par le framework :

package com.tech.demo.configuration;

import lombok.Data;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;

@Data
@Configuration
public class SearchNodeInitializer extends AbstractElasticsearchConfiguration {
    @Value("${elasticsearch.uri}")
    private String networkEndpoint;

    @Bean
    @Override
    public RestHighLevelClient searchClient() {
        var config = ClientConfiguration.builder()
                .connectedTo(networkEndpoint)
                .build();
        return RestClients.create(config).rest();
    }
}

Modélisation de la donnée indexable

Définissez un objet supportant les annotations nécessaires au mappage :

package com.tech.demo.models;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Data
@Document(indexName = "user_catalog", createIndex = true)
public class IndexedProfile {
    @Id
    private Long identifier;

    @Field(type = FieldType.Keyword)
    private String fullName;

    @Field(type = FieldType.Integer)
    private Integer yearsActive;

    @Field(type = FieldType.Text)
    private String biography;

    public IndexedProfile(Long identifier, String fullName, Integer yearsActive, String biography){
        this.identifier = identifier;
        this.fullName = fullName;
        this.yearsActive = yearsActive;
        this.biography = biography;
    }

    public IndexedProfile(){ }
}

Exposition des opérations CRUD

Implémentez un contrôleur REST pour piloter le stockage :

package com.tech.demo.api;

import com.tech.demo.models.IndexedProfile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.LinkedHashMap;
import java.util.Map;

@RestController
@RequestMapping("/management/items")
public class IndexManagementHandler {

    @Autowired
    private ElasticsearchOperations storageEngine;

    @RequestMapping(value = "/retrieve")
    public Map<string object=""> fetchAll() {
        SearchHits<indexedprofile> results = storageEngine.search(Query.findAll(), IndexedProfile.class);
        Map<string object=""> payload = new LinkedHashMap<>();
        payload.put("success", true);
        payload.put("entries", results.getSearchHits());
        return payload;
    }

    @RequestMapping(value = "/store")
    public Map<string object=""> persistNew() {
        IndexedProfile profile = new IndexedProfile(1L, "Alice Martin", 29, "Ingénieure logicielle");
        storageEngine.save(profile);
        Map<string object=""> payload = new LinkedHashMap<>();
        payload.put("success", true);
        payload.put("message", "Item enregistré avec succès");
        return payload;
    }

    @RequestMapping(value = "/modify")
    public Map<string object=""> refreshExisting() {
        IndexedProfile profile = new IndexedProfile(1L, "Bob Dupont", 31, "Chef de projet");
        storageEngine.save(profile);
        Map<string object=""> payload = new LinkedHashMap<>();
        payload.put("success", true);
        payload.put("message", "Données mises à jour");
        return payload;
    }

    @RequestMapping(value = "/fetch/{identifier}")
    public Map<string object=""> findById(@PathVariable Long identifier) {
        IndexedProfile profile = storageEngine.get(identifier.toString(), IndexedProfile.class);
        Map<string object=""> payload = new LinkedHashMap<>();
        payload.put("success", true);
        payload.put("record", profile);
        return payload;
    }

    @RequestMapping(value = "/remove/{identifier}")
    public Map<string object=""> deleteById(@PathVariable Long identifier) {
        IndexedProfile target = new IndexedProfile();
        target.setIdentifier(identifier);
        String operationResult = storageEngine.delete(target);
        Map<string object=""> payload = new LinkedHashMap<>();
        payload.put("success", true);
        payload.put("action_result", operationResult);
        return payload;
    }

    @RequestMapping(value = "/purge")
    public Map<string object=""> wipeAll() {
        storageEngine.delete(Query.findAll(), IndexedProfile.class);
        Map<string object=""> payload = new LinkedHashMap<>();
        payload.put("success", true);
        payload.put("status", "Index vidé");
        return payload;
    }
}</string></string></string></string></string></string></string></string></string></string></string></indexedprofile></string>

Résultats retournés

Appel vers /management/items/retrieve :

{
  "success": true,
  "entries": [
    {
      "index": "user_catalog",
      "id": "1",
      "score": 1.0,
      "content": {
        "identifier": 1,
        "fullName": "Alice Martin",
        "yearsActive": 29,
        "biography": "Ingénieure logicielle"
      }
    }
  ]
}

Saisie via /management/items/fetch/1 :

{
  "success": true,
  "record": {
    "identifier": 1,
    "fullName": "Alice Martin",
    "yearsActive": 29,
    "biography": "Ingénieure logicielle"
  }
}

Étiquettes: spring-data-elasticsearch rest-high-level-client spring-boot-nosql elastic-search-mapping java-api-design

Publié le 18 septembre à 09h32