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).
Query DSL ?
Query DSL stands for Query Domain-Specific Language, which is a powerful feature of Elasticsearch that enables users to specify search queries in a flexible and intuitive way. It's a JSON-based language that allows you to define complex search queries with a simple and concise syntax.
Query DSL allows you to search for specific documents in your Elasticsearch index based on a wide range of criteria, such as keyword match, ranges of values, geographic locations, and more. You can also use Query DSL to apply filters to your search results and to specify sorting and aggregations.
Technology
- Spring Boot 3.0.6
- Docker
- Maven
- Elasticsearch 7.17.5
- IntelliJ IDEA
Configuration Spring Boot project :
<?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.0.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.henry</groupId>
<artifactId>SpringDataElasticsearchQueryDsl</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringDataElasticsearchQueryDsl</name>
<description>Demo Spring boot + elasticsearch + querydsl</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>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</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-web</artifactId>
</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>
<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.0.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.henry</groupId>
<artifactId>SpringDataElasticsearchQueryDsl</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringDataElasticsearchQueryDsl</name>
<description>Demo Spring boot + elasticsearch + querydsl</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>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</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-web</artifactId>
</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:
server:
port: 9000
servlet:
context-path: /
elasticsearch:
host: localhost
port: 9200
port: 9000
servlet:
context-path: /
elasticsearch:
host: localhost
port: 9200
Spring Data ElasticSearch Application
Configuration
package com.henry.configuration;
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 org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticsearchConfig {
@Value("${elasticsearch.host}")
private String EsHost;
@Value("${elasticsearch.port}")
private int EsPort;
@Bean
public ElasticsearchTransport client() {
// Create the low-level client
RestClient restClient = RestClient.builder(
new HttpHost(EsHost, EsPort)).build();
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
return transport;
}
// And create the API client
@Bean
public ElasticsearchClient elasticsearchClient() {
return new ElasticsearchClient(client());
}
}
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 org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticsearchConfig {
@Value("${elasticsearch.host}")
private String EsHost;
@Value("${elasticsearch.port}")
private int EsPort;
@Bean
public ElasticsearchTransport client() {
// Create the low-level client
RestClient restClient = RestClient.builder(
new HttpHost(EsHost, EsPort)).build();
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
return transport;
}
// And create the API client
@Bean
public ElasticsearchClient elasticsearchClient() {
return new ElasticsearchClient(client());
}
}
Model
package com.henry.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.Document;
@Document(indexName = "users")
public class
User {
@Id
private Long id;
private String name;
private String lastName;
@Transient
private String _class;
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;
}
public String get_class() {
return _class;
}
public void set_class(String _class) {
this._class = _class;
}
}
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.Document;
@Document(indexName = "users")
public class
User {
@Id
private Long id;
private String name;
private String lastName;
@Transient
private String _class;
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;
}
public String get_class() {
return _class;
}
public void set_class(String _class) {
this._class = _class;
}
}
Repository
package com.henry.repository;
import com.henry.model.User;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User,Long> {
Iterable<User> findByName(String name);
}
import com.henry.model.User;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User,Long> {
Iterable<User> findByName(String name);
}
Service
package com.henry.service;
import com.henry.model.User;
public sealed interface UserService permits UserServiceImpl {
User save(User user);
User update(Long id, User user);
void delete(Long id);
User findOne(Long id);
Iterable<User> findAll();
Iterable<User> findByName(String name);
}
import com.henry.model.User;
public sealed interface UserService permits UserServiceImpl {
User save(User user);
User update(Long id, User user);
void delete(Long id);
User findOne(Long id);
Iterable<User> findAll();
Iterable<User> findByName(String name);
}
ServiceImpl
package com.henry.service;
import com.henry.model.User;
import com.henry.repository.UserRepository;
import org.springframework.stereotype.Service;
@Service
public final class UserServiceImpl implements UserService {
private final UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public User save(User user) {
return userRepository.save(user);
}
@Override
public User update(Long id, User user) {
var obj = userRepository.findById(id);
obj.get().setName(user.getName());
obj.get().setLastName(user.getLastName());
return userRepository.save(obj.get());
}
@Override
public void delete(Long id) {
userRepository.deleteById(id);
}
@Override
public User findOne(Long id) {
return userRepository.findById(id).get();
}
@Override
public Iterable<User> findAll() {
return userRepository.findAll();
}
@Override
public Iterable<User> findByName(String name) {
return userRepository.findByName(name);
}
}
import com.henry.model.User;
import com.henry.repository.UserRepository;
import org.springframework.stereotype.Service;
@Service
public final class UserServiceImpl implements UserService {
private final UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public User save(User user) {
return userRepository.save(user);
}
@Override
public User update(Long id, User user) {
var obj = userRepository.findById(id);
obj.get().setName(user.getName());
obj.get().setLastName(user.getLastName());
return userRepository.save(obj.get());
}
@Override
public void delete(Long id) {
userRepository.deleteById(id);
}
@Override
public User findOne(Long id) {
return userRepository.findById(id).get();
}
@Override
public Iterable<User> findAll() {
return userRepository.findAll();
}
@Override
public Iterable<User> findByName(String name) {
return userRepository.findByName(name);
}
}
Controller
package com.henry.controller;
import com.henry.model.User;
import com.henry.service.UserService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/save")
public User add(@RequestBody User user) {
return userService.save(user);
}
@PutMapping("/update/{id}")
public User update(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}
@GetMapping("/findOne/{id}")
public User findOne(@PathVariable Long id) {
return userService.findOne(id);
}
@GetMapping("/all")
public Iterable<User> findAll() {
return userService.findAll();
}
@GetMapping("/findByName/{name}")
public Iterable<User> findByName(@PathVariable String name) {
return userService.findByName(name);
}
@DeleteMapping("/delete/{id}")
public void delete(@PathVariable Long id) {
userService.delete(id);
}
}
import com.henry.model.User;
import com.henry.service.UserService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/save")
public User add(@RequestBody User user) {
return userService.save(user);
}
@PutMapping("/update/{id}")
public User update(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}
@GetMapping("/findOne/{id}")
public User findOne(@PathVariable Long id) {
return userService.findOne(id);
}
@GetMapping("/all")
public Iterable<User> findAll() {
return userService.findAll();
}
@GetMapping("/findByName/{name}")
public Iterable<User> findByName(@PathVariable String name) {
return userService.findByName(name);
}
@DeleteMapping("/delete/{id}")
public void delete(@PathVariable Long id) {
userService.delete(id);
}
}
Resource
package com.henry.resource;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.henry.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Component
public class SearchQueryBuilder {
@Autowired
private ElasticsearchClient client;
public List<User> getByName(String text) throws IOException {
var list = new ArrayList<User>();
SearchResponse<User> search = client.search(s -> s
.index("users")
.query(q -> q
.term(t -> t
.field("name")
.value(v -> v.stringValue(text))
)),
User.class);
for (Hit<User> hit: search.hits().hits()) {
list.add(hit.source());
}
return list;
}
}
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.henry.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Component
public class SearchQueryBuilder {
@Autowired
private ElasticsearchClient client;
public List<User> getByName(String text) throws IOException {
var list = new ArrayList<User>();
SearchResponse<User> search = client.search(s -> s
.index("users")
.query(q -> q
.term(t -> t
.field("name")
.value(v -> v.stringValue(text))
)),
User.class);
for (Hit<User> hit: search.hits().hits()) {
list.add(hit.source());
}
return list;
}
}
package com.henry.resource;
import com.henry.model.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
@RestController
@RequestMapping("/user-resource")
public class UserResource {
private final SearchQueryBuilder searchQueryBuilder;
public UserResource(SearchQueryBuilder searchQueryBuilder) {
this.searchQueryBuilder = searchQueryBuilder;
}
@GetMapping("/{name}")
public List<User> getByName(@PathVariable String name) throws IOException {
return searchQueryBuilder.getByName(name);
}
}
import com.henry.model.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
@RestController
@RequestMapping("/user-resource")
public class UserResource {
private final SearchQueryBuilder searchQueryBuilder;
public UserResource(SearchQueryBuilder searchQueryBuilder) {
this.searchQueryBuilder = searchQueryBuilder;
}
@GetMapping("/{name}")
public List<User> getByName(@PathVariable String name) throws IOException {
return searchQueryBuilder.getByName(name);
}
}
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
http://localhost:9000/users/save
GET
http://localhost:9000/user-resource/henry
GET
http://localhost:9200/_search?q=Henry
Source Code
Here on GitHub.
References.
https://www.elastic.co/what-is/elasticsearch
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/connecting.html
No comments:
Post a Comment