티스토리 뷰
회사에서 작은 프로젝트를 진행하는데 Elasticsearch 를 써볼 기회가 생겼다.
들어가기 전에 Elasticsearch 가 뭔지에 대해 간단히 정리해보자
Elasticsearch 란?
Elasticsearch는 텍스트, 숫자, 위치 기반 정보, 정형 및 비정형 데이터 등 모든 유형의 데이터를 위한 무료 검색 및 분석 엔진으로 분산형과 개방형을 특징으로 합니다. Elasticsearch는 Apache Lucene을 기반으로 구축되었으며, Elasticsearch N.V.(현재 명칭 Elastic)가 2010년에 최초로 출시했습니다. 간단한 REST API, 분산형 특징, 속도, 확장성으로 유명한 Elasticsearch는 데이터 수집, 보강, 저장, 분석, 시각화를 위한 무료 개방형 도구 모음인 Elastic Stack의 핵심 구성 요소입니다. 보통 ELK Stack(Elasticsearch, Logstash, Kibana의 머리글자)이라고 하는 Elastic Stack에는 이제 데이터를 Elasticsearch로 전송하기 위한 경량의 다양한 데이터 수집 에이전트인 Beats가 포함되어 있습니다.
- Elasticsearch 홈페이지
프로젝트에서 Elasticsearch(이하 ES)의 사용 이유는 ES에서 제공하는 특정 검색 기능을 쓰기 위해서였다.
이 기능을 쓰기 위해서 우선 테이블 역할을 하는 인덱스(index)를 생성하고 데이터를 저장해야하고, 검색 API를 요청해야 한다.
그 전에 로컬 설치 버전을 쓰 것이냐 클라우드 버전을 쓸 것이냐를 결정해야한다.
로컬 설치 vs Elastic Cloud
일단 맛만보는데, 로컬 설치과정까지 거치고 싶진 않았다.
14일 무료 체험판을 이용해보자.
Elastic Cloud 설정
Elasticsearch Cloud에 가입하고 나면 이런게 나온다.
앞서 언급한 것 처럼, 데이터를 저장하고 검색하는 기능을 원하기 때문에
SEARCH의 Search across databases and business systems를 선택했다.
그러면 우측에 아래와 같은 창이 뜨는데, 일단 Start를 클릭
DB 커넥터를 선택하라고 나오는데, 외부 DB를 연동해서 쓰고싶진 않고
ES에서 제공하는 Index를 쓰고 싶으니 좌측에 Indices를 클릭한다.
Create a new index 클릭
다음은 데이터 수집 방법을 선택한다.
웹 크롤러는 일단 아니고, 커넥터는 아까 처음에 봤던 select a connector 페이지로 간다.
일단 API를 이용해 데이터를 저장하고 싶으니 API를 선택.
인덱스의 이름은 대충 hello-world라고 지었다.
이제 SpringBoot에서 사용할 수 있는 상태가 됐다.
Elasticsearch의 Client
들어가기전에 간단히 짚고 넘어갈게 있다.
자바에서 ES API를 이용하기 위해서는, ES에서 만들어놓은 클라이언트를 이용해야 한다.
ES 7 버전까지만해도 Client에는 HighLevelClient와 LowLevelClient가 있었다.
HighLevelClient는 API를 추상화시켜놓아서 SDK 형식으로 사용할 수 있는 기능을 제공했는데,
ES 8버전으로 넘어오면서 이 기능이 deprecated 되었다.
maven repository를 가봐도 HighLevelClient는 21년부터 업데이트가 없다.
때문에 반강제적으로 LowLevelClient를 사용해야했어야 했다.
8버전 부터는 LowLevelClient를 확장한 ElasticsearchClient란 Client를 사용하고 있는데,
이 글에선 LowLevelClient를 기반으로 정리해 보려고 한다.
SpringBoot에서 사용하기
본격적으로 시작해보자
build.gradle
implementation 'org.elasticsearch.client:elasticsearch-rest-client:8.7.1'
LowLevelClient를 사용할 것이라 다른 의존성은 필요 없다.
Configuration
@Configuration
public class RestClient {
@Value("${elastic.hostname}")
private String ELASTICSEARCH_HOSTNAME;
@Bean
public org.elasticsearch.client.RestClient createClient() {
RestClientBuilder builder = org.elasticsearch.client.RestClient.builder(
new HttpHost(ELASTICSEARCH_HOSTNAME, 443, "https"));
return builder.build();
}
}
Elasticsearch는 RestClient라는 자체적으로 개발한? Client를 통해 연결을 한다.
여기서 주의할 점은 https, 443 포트만으로 연결한다는 점과 HOSTNAME에 https:// 를 붙이지 않아야한다는 점이다.
HostName 위치에 aaa.bbb.com 와 같이 써야한다.
그리고 사용자에게 권한을 줬으면 Credential 설정을 해야하는데, 그 내용은 여기에 정리해놨다.
이제 간단한 CRUD를 알아보자.
POST
POST 메서드는 새로운 데이터를 인덱스에 추가하는데 사용된다.
인덱스가 이미 존재하는 경우 문서를 추가하고, 인덱스가 없는 경우에는 새로운 인덱스를 생성하고 문서를 추가한다.
private final RestClient restClient; // 이하 생략
...
String testJson = """
{
"this": "is",
"test": "json!!"
}
""";
Request request = new Request("POST", "/test/_doc/1");
HttpEntity entity = new NStringEntity(testJson, ContentType.APPLICATION_JSON);
request.setEntity(entity);
Response response = restClient.performRequest(request);
String result = EntityUtils.toString(response.getEntity());
System.out.println(result);
아래와 같이 response를 받으면 성공이다.
{"_index":"test","_id":"o2akj4kB3kp-8g5nlE1M","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}
ES cloud의 indices에 가보면 아래와 같이 index가 생성되어 있다.
post 방식으로 3번 데이터를 날렸더니 업데이트 되지 않고 3개의 문서가 생성된 것을 확인 할 수 있다.
GET
인덱스의 특정 문서를 가져오기 위해 사용되며, 해당 문서의 ID를 지정하여 해당 문서를 가져온다.
Request request = new Request("GET", "/test/_doc/1");
HttpEntity entity = new NStringEntity(testJson, ContentType.APPLICATION_JSON);
request.setEntity(entity);
Response response = restClient.performRequest(request);
String result = EntityUtils.toString(response.getEntity());
System.out.println(result);
PUT
인덱스를 생성하거나 이미 존재하는 인덱스를 업데이트할 때 사용한다.
그리고 인덱스의 스키마를 정의하거나 인덱스의 설정을 업데이트할 때에도 사용된다.
(별도의 스키마를 정의하지 않아도 인덱스에 데이터가 바로 입력이 되기 때문에, 따로 예시 코드를 만들진 않음)
String testJson = """
{
"this": "is",
"test": "put put json??"
}
""";
Request request = new Request("PUT", "/test/_doc/1");
HttpEntity entity = new NStringEntity(testJson, ContentType.APPLICATION_JSON);
request.setEntity(entity);
Response response = restClient.performRequest(request);
String result = EntityUtils.toString(response.getEntity());
System.out.println(result);
변화를 알기 힘들긴한데, result가 updated로 출력된다.
{"_index":"test","_id":"1","_version":2,"result":"updated","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":4,"_primary_term":1}
dev 콘솔에서 값을 찍어보면
GET /test/_doc/1
{
"_index": "test",
"_id": "1",
"_version": 3,
"_seq_no": 5,
"_primary_term": 1,
"found": true,
"_source": {
"this": "is",
"test": "put put json??"
}
}
DELETE
인덱스나 문서를 삭제하는데 사용한다.
Request request = new Request("DELETE", "/test/_doc/1");
HttpEntity entity = new NStringEntity(testJson, ContentType.APPLICATION_JSON);
request.setEntity(entity);
Response response = restClient.performRequest(request);
String result = EntityUtils.toString(response.getEntity());
System.out.println(result);
결과는 아래와 같이 날아온다.
{"acknowledged":true}
마지막으로 1번글을 조회해보면
GET /test/_doc/1
{
"error": {
"root_cause": [
{
"type": "index_not_found_exception",
"reason": "no such index [test]",
"resource.type": "index_or_alias",
"resource.id": "test",
"index_uuid": "_na_",
"index": "test"
}
],
"type": "index_not_found_exception",
"reason": "no such index [test]",
"resource.type": "index_or_alias",
"resource.id": "test",
"index_uuid": "_na_",
"index": "test"
},
"status": 404
}
정상적으로 인덱스가 삭제가 되었기 때문에, 에러를 발생시킨다.
마치며
Elasticsearch cloud의 설정 방법과 간단한 CRUD 작업에 대해 정리했다.
Elasticsearch도 본인을 소개할 때 언급했지만, 얘네의 특장점은 단순히 저장하는 기능이 아니라 검색 기능이다.
다음은 몇 가지 누락된 것들과 좀 깊이 있는 내용을 다뤄보려고 한다.
'개발 > Elasticsearch' 카테고리의 다른 글
Elasticsearch _reindex 429 error 해결하기 (0) | 2024.03.08 |
---|---|
Elasticsearch cloud에서 검색(search) 기능 사용하기 - 2 (K-NN search) (0) | 2023.08.24 |
Elasticsearch cloud에서 검색(search) 기능 사용하기 - 1 (cosine similarity search) (0) | 2023.08.17 |
- Total
- Today
- Yesterday
- CloudFront
- springboot
- serverless
- EKS
- cache
- Log
- GIT
- terraform
- Spring
- Kotlin
- MySQL
- OpenAI
- 스프링부트
- ChatGPT
- AWS EC2
- JWT
- java
- 티스토리챌린지
- AWS
- 오블완
- elasticsearch
- OpenFeign
- Elastic cloud
- openAI API
- AOP
- S3
- docker
- lambda
- 후쿠오카
- 람다
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |