프로그래밍
Springboot 와 Elasticsearch 연동하기
tedhong
2023. 6. 2. 17:40
Springboot 에서 Elasticsearch 를 연동하는 방법에 대해 알아봅니다.
버전
- Java : OpenJDK 17
- Springboot : 3.1.0
필수 의존성
- spring-boot-starter-data-elasticsearch
- spring-boot-starter-web
- lombok
클래스
- 프로퍼티
- host 와 port 를 application.properties 파일에 저장합니다.
# ElasticSearch
elasticsearch.host = localhost
elasticsearch.port = 9200
- Config
- Elasticsearch Client 를 생성하는 설정 클래스입니다.
- application.properties 에서 host 와 port 를 가져와 RestClient 객체를 생성합니다.
- ElasticSearchConfig.java
@Configuration
@EnableElasticsearchRepositories // elasticsearch repository 허용
public class ElasticSearchConfig{
@Value("${elasticsearch.dev.host}")
String host;
@Value("${elasticsearch.port}")
int port;
@Bean
public RestClient getRestClient() {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("", ""));
return RestClient.builder(new HttpHost(host, port))
.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.disableAuthCaching();
httpClientBuilder.setDefaultHeaders(List.of(
new BasicHeader(
HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)));
httpClientBuilder.addInterceptorLast((HttpResponseInterceptor)
(response, context) ->
response.addHeader("X-Elastic-Product", "Elasticsearch"));
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}).build();
}
@Bean
public ElasticsearchTransport getElasticsearchTransport() {
return new RestClientTransport(getRestClient(), new JacksonJsonpMapper());
}
@Bean
public ElasticsearchClient getElasticsearchClient() {
return new ElasticsearchClient(getElasticsearchTransport());
}
}
- Repository
- ElasticsearchRepository 를 상속받는 인터페이스 입니다.
이 인터페이스를 통해 CRUD, 검색, 페이징, 정렬등의 작업을 할 수 있습니다.
상속시 필요한 인자 2가지는 Document 타입과 ID 의 타입 입니다. - SearchResultRepo.java
- ElasticsearchRepository 를 상속받는 인터페이스 입니다.
public interface SearchResultRepo extends ElasticsearchRepository<SearchResultDocument, Integer> {
}
- Document
- 검색 결과 Document 클래스입니다.
@Document 어노테이션에 정의된 indexName 으로 document (= database) 의 이름이 정의 되고
각 @Field 의 타입과 변수명으로 field (=column) 이 정의됩니다. - SearchResultDocument.java
- 검색 결과 Document 클래스입니다.
@Getter
@Builder
@AllArgsConstructor
@Document(indexName = "search_result")
public class SearchResultDocument {
@Id
@Field(type = FieldType.Integer)
private int id;
@Field(type = FieldType.Text)
private String keywords;
@Field(type = FieldType.Text)
private String resultData;
// @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyyMMdd")
@Field(type = FieldType.Date, format = {DateFormat.date_hour_minute_second_millis, DateFormat.epoch_millis})
private Date createDate;
}
- Service
- Service 클래스입니다.
- Elasticsearch 의 기능 이용을 위해 SearchResultRepo, ElasticsearchOperations 객체를 사용합니다.
각 객체의 의존성은 Service 가 생성 될 때 주입 됩니다. - SearchResultService.java
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class SearchResultService {
private final SearchResultRepo searchResultRepo;
private final ElasticsearchOperations elasticsearchOperations;
public Page<SearchResultDocument> findAll(){
return (Page<SearchResultDocument>) searchResultRepo.findAll();
}
public SearchResultDocument saveResult(SearchResultDocument doc){
return searchResultRepo.save(doc);
}
public Optional<SearchResultDocument> getResultById(int id){
return searchResultRepo.findById(id);
}
public void deleteResult(int id){
searchResultRepo.deleteById(id);
}
public void deleteResult(SearchResultDocument doc){
searchResultRepo.delete(doc);
}
public List<SearchResultDocument> searchResult(String keyword) {
IndexCoordinates indexCoordinates = IndexCoordinates.of("search_result");
Criteria criteria = new Criteria("keywords").contains(keyword)
.or(new Criteria("resultData").contains(keyword));
CriteriaQuery query = new CriteriaQuery(criteria);
SearchHits<SearchResultDocument> searchHits = elasticsearchOperations.search(query, SearchResultDocument.class, indexCoordinates);
return searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());
}
}
- Controller
- Service 객체를 이용해 비즈니스 로직을 처리하는 Controller 클래스 입니다.
- RestController 로 지정되어 RESTful 하게 사용할 수 있습니다.
- SearchResultController.java
@RequiredArgsConstructor
@RequestMapping("/")
@RestController
public class SearchResultController {
private final SearchResultService searchResultService;
@PostMapping("/save")
public ResponseEntity<SearchResultDocument> createSearchResult(
@RequestParam("keywords") String keywords,
@RequestParam("resultData") String resultData
) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
SearchResultDocument searchResult = null;
try {
searchResult = SearchResultDocument.builder()
.keywords(keywords)
.resultData(resultData)
.createDate(format.parse(format.format(new Date())))
.build();
} catch (ParseException e) {
System.out.println(e.toString());
}
SearchResultDocument createdSearchResult = searchResultService.saveResult(searchResult);
return new ResponseEntity<>(createdSearchResult, HttpStatus.CREATED);
}
@GetMapping("/{id}")
public ResponseEntity<SearchResultDocument> getSearchResultById(@PathVariable("id") int id) {
return searchResultService.getResultById(id)
.map(result -> new ResponseEntity<>(result, HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
@GetMapping("/findall")
public ResponseEntity<Page<SearchResultDocument>> getAllSearchResults() {
Page<SearchResultDocument> searchResults = searchResultService.findAll();
return new ResponseEntity<>(searchResults, HttpStatus.OK);
}
@DeleteMapping("/delete/{id}")
public ResponseEntity<Void> deleteSearchResult(@PathVariable("id") int id) {
searchResultService.deleteResult(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@GetMapping("/search")
public ResponseEntity<List<SearchResultDocument>> searchByKeyword(@RequestParam("keyword") String keyword) {
List<SearchResultDocument> searchResults = searchResultService.searchResult(keyword);
return new ResponseEntity<>(searchResults, HttpStatus.OK);
}
}
예제 코드
github : TedHong/springboot-elasticsearch: Springboot 에서 Elasticsearch 연동하기 (github.com)