Spring Boot + Reactive Spring Data + Reactive Elasticsearch example
Elasticsearch:
By official documentation Elasticsearch is a distributed, free and open search and analytics engine for all types of data, including textual, numerical, geospatial, structured, and unstructured. Elasticsearch is built on Apache Lucene and was first released in 2010 by Elasticsearch N.V. (now known as Elastic).
Technology
- Spring Boot 2.7.2
- Java 17 (Zulu)
- Docker
- Maven
- Elasticsearch 7.17.5
- IntelliJ IDEA
- Postman
Project Structure
Configuration Spring Boot project
pom.xml
<?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.henry</groupId>
<artifactId>SpringDataElasticsearch</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringDataElasticsearch</name>
<description>Integration SpringData + Elasticsearch</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<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>
</plugin>
</plugins>
</build>
</project>
application.yml: Configuration reactive elasticsearch
server:
port: 9000
servlet:
context-path: /
spring:
data:
elasticsearch:
client:
reactive:
endpoints: localhost:9200
elasticsearch:
rest:
uris: http://localhost:9200
Model
package com.henry.SpringDataElasticsearch.model;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.annotation.Id;
@Document(indexName = "users")
public class User {
@Id
private Long id;
private String name;
private String lastName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "Users{" +
"id=" + id +
", name='" + name + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
Repository
package com.henry.SpringDataElasticsearch.repository;
import com.henry.SpringDataElasticsearch.model.User;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
@Repository
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByName(String name);
}
Service
package com.henry.SpringDataElasticsearch.service;
import com.henry.SpringDataElasticsearch.model.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface UserService {
Mono<User> save(User user);
Mono<User> update(Long id, User user);
Mono<Void> delete(Long id);
Mono<User> findOne(Long id);
Flux<User> findAll();
Flux<User> findByName(String name);
}
ServiceImpl
package com.henry.SpringDataElasticsearch.service.impl;
import com.henry.SpringDataElasticsearch.model.User;
import com.henry.SpringDataElasticsearch.repository.UserRepository;
import com.henry.SpringDataElasticsearch.service.UserService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public Mono<User> save(User user) {
return userRepository.save(user);
}
@Override
public Mono<User> update(Long id, User user) {
return userRepository.findById(id)
.flatMap(u -> {
u.setName(user.getName());
u.setLastName(user.getLastName());
return userRepository.save(u);
});
}
@Override
public Mono<Void> delete(Long id) {
var del = userRepository.deleteById(id);
return del;
}
@Override
public Mono<User> findOne(Long id) {
return userRepository.findById(id);
}
@Override
public Flux<User> findAll() {
return userRepository.findAll();
}
@Override
public Flux<User> findByName(String name) {
return userRepository.findByName(name);
}
}
Controller
package com.henry.SpringDataElasticsearch.controller;
import com.henry.SpringDataElasticsearch.model.User;
import com.henry.SpringDataElasticsearch.service.UserService;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/save")
public Mono<User> add(@RequestBody User user) {
return userService.save(user);
}
@PutMapping("/update/{id}")
public Mono<User> update(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}
@GetMapping("/findOne/{id}")
public Mono<User> findOne(@PathVariable Long id) {
return userService.findOne(id);
}
@GetMapping("/all")
public Flux<User> findAll() {
return userService.findAll();
}
@GetMapping("/findByName/{name}")
public Flux<User> findByName(@PathVariable String name) {
return userService.findByName(name);
}
@DeleteMapping("/delete/{id}")
public Mono<Void> delete(@PathVariable Long id) {
return userService.delete(id);
}
}
Downloading and installing Elasticsearch
docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.17.5
Run & Test
Run Spring Boot application with command: mvn spring-boot:run. by console, IntelliJ etc.
POST
localhost:9000/users/save
PUT
localhost:9000/users/update/1
GET
localhost:9000/users/all
localhost:9000/users/findByName/henry
DELETE
localhost:9000/users/delete/1
Basic examples retrieves documents in Elasticsearch are represented in JSON format.
localhost:9200/_search?q=Henry
http://localhost:9200/_search?q=lastName:user
References.
https://www.elastic.co/what-is/elasticsearch
https://www.elastic.co/blog/a-practical-introduction-to-elasticsearch
https://www.springcloud.io/post/2022-03/getting-started-with-spring-webflux/#gsc.tab=0
https://piotrminkowski.com/2019/10/25/reactive-elasticsearch-with-spring-boot/
https://mkyong.com/spring-boot/spring-boot-spring-data-elasticsearch-example/
https://www.youtube.com/watch?v=bYiNlCaaRiI
https://www.youtube.com/watch?v=rfjsaccL_e0
https://www.youtube.com/watch?v=uSFNaYlc5ek
No comments:
Post a Comment