Intégration d'Elasticsearch avec Spring Boot : Indexation et Opérations CRUD

Confgiuration du projet Spring Boot

Dépendances Maven requises :

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>9.0.0</version>
</dependency>

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>${elasticsearch.version}</version>
</dependency>

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.2</version>
</dependency>

Configuration de l'application

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/es
    username: root
    password: root

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true

Modélisation des données

Entité métier

@Data
@TableName("tb_hotel")
public class HotelEntity {
    @TableId(type = IdType.INPUT)
    private Long id;
    private String nom;
    private String adresse;
    private Integer prix;
    private Integer score;
    private String marque;
    private String ville;
    private String categorie;
    private String secteur;
    private String longitude;
    private String latitude;
    private String image;
}

Mapper MyBatis

public interface HotelMapper 
    extends BaseMapper<HotelEntity> {}

Configuration d'Elasticsearch

Définition du mapping d'index

PUT /hotel
{
  "mappings": {
    "properties": {
      "id": {"type": "keyword"},
      "nom": {
        "type": "text",
        "analyzer": "ik_max_word",
        "copy_to": "champs_combines"
      },
      "adresse": {"type": "keyword", "index": false},
      "prix": {"type": "integer"},
      "score": {"type": "integer"},
      "marque": {
        "type": "keyword",
        "copy_to": "champs_combines"
      },
      "ville": {
        "type": "keyword",
        "copy_to": "champs_combines"
      },
      "categorie": {
        "type": "keyword",
        "copy_to": "champs_combines"
      },
      "secteur": {"type": "keyword"},
      "position": {"type": "geo_point"},
      "image": {"type": "keyword", "index": false},
      "champs_combines": {
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

Document Elasticsearch

@Data
@NoArgsConstructor
public class HotelDocument {
    private Long id;
    private String nom;
    private String adresse;
    private Integer prix;
    private Integer score;
    private String marque;
    private String ville;
    private String categorie;
    private String secteur;
    private String position;
    private String image;
}

Opérations di'ndexation

Gestionnaire d'index

@SpringBootTest
public class IndexManagerTest {

    private final String MAPPING_JSON = "{...}";
    
    private RestHighLevelClient client;

    @BeforeEach
    void initClient() {
        client = new RestHighLevelClient(
            RestClient.builder(HttpHost.create("http://localhost:9200"))
        );
    }

    @Test
    void creerIndex() throws IOException {
        CreateIndexRequest requete = new CreateIndexRequest("hotel");
        requete.source(MAPPING_JSON, XContentType.JSON);
        client.indices().create(requete, RequestOptions.DEFAULT);
    }

    @Test
    void supprimerIndex() throws IOException {
        DeleteIndexRequest requete = new DeleteIndexRequest("hotel");
        client.indices().delete(requete, RequestOptions.DEFAULT);
    }
}

Opérations sur les documents

Insertion de document

@Test
void ajouterDocument() throws IOException {
    HotelEntity entite = hotelService.getById(61083L);
    HotelDocument document = new HotelDocument();
    BeanUtils.copyProperties(entite, document);
    document.setPosition(entite.getLatitude() + ", " + entite.getLongitude());
    
    IndexRequest requete = new IndexRequest("hotel")
        .id(entite.getId().toString())
        .source(JSON.toJSONString(document), XContentType.JSON);
    
    client.index(requete, RequestOptions.DEFAULT);
}

Requête de document

@Test
void obtenirDocument() throws IOException {
    GetRequest requete = new GetRequest("hotel", "61083");
    GetResponse reponse = client.get(requete, RequestOptions.DEFAULT);
    String json = reponse.getSourceAsString();
}

Mise à jour partielle

@Test
void modifierDocument() throws IOException {
    UpdateRequest requete = new UpdateRequest("hotel", "61083");
    requete.doc("prix", 150, "score", 42);
    client.update(requete, RequestOptions.DEFAULT);
}

Suppression de document

@Test
void supprimerDocument() throws IOException {
    DeleteRequest requete = new DeleteRequest("hotel", "61083");
    client.delete(requete, RequestOptions.DEFAULT);
}

Insertion par lot

@Test
void insererLotDocuments() throws IOException {
    List<HotelEntity> hotels = hotelService.list();
    BulkRequest requeteLot = new BulkRequest();
    
    for (HotelEntity entite : hotels) {
        HotelDocument doc = new HotelDocument();
        BeanUtils.copyProperties(entite, doc);
        doc.setPosition(entite.getLatitude() + ", " + entite.getLongitude());
        
        IndexRequest requete = new IndexRequest("hotel")
            .id(entite.getId().toString())
            .source(JSON.toJSONString(doc), XContentType.JSON);
        
        requeteLot.add(requete);
    }
    client.bulk(requeteLot, RequestOptions.DEFAULT);
}

Étiquettes: Elasticsearch SpringBoot Indexation MyBatis crud

Publié le 10 juillet à 19h03