개발놀이터

Elasticsearch 스프링에서 사용하기 본문

CS 지식/데이터베이스

Elasticsearch 스프링에서 사용하기

마늘냄새폴폴 2023. 5. 26. 05:42

이번 포스팅에선 이제 제가 공부하는 스프링에서 사용해봐야죠. 우리는 앞선 포스팅에서 Elassticsearch의 전반적인 내용과 CRUD를 해봤습니다. 

 

스프링에선 Elasticsearch를 어떻게 사용하고있을까요? 

 

한번 알아보죠!

 

스프링에서 Elasticsearch 사용하기

	// elasticsearch
	implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
	implementation 'org.elasticsearch.client:elasticsearch-rest-high-level-client'

우선 의존성을 추가해줍니다. 

 

ElasticsearchConfig.java

package com.hello.capston.elasticsearch.config;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ElasticsearchConfig {

    @Value("${spring.data.elasticsearch.host}")
    private String host;

    @Value("${spring.data.elasticsearch.port}")
    private int port;

    @Bean(destroyMethod = "close")
    public RestHighLevelClient client() {
        RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
        RestHighLevelClient client = new RestHighLevelClient(builder);

        return client;
    }
}

Elasticsearch에서 사용될 Configuration 클래스입니다. 

 

Elasticsearch에선 high-level 과 low-level을 지원하는데 둘의 차이는 low-level을 사용하기 쉽게 추상화한 것이 바로 high-level입니다. 둘의 성능적인 차이는 잘 모르겠지만 조금 변태같은 취미가 있으신 분들은 low-level을 추천드립니다. 

 

또한 저는 spring-data-elasticsearch를 사용하지 않았습니다. 사용방법은 spring-data-jpa와 비슷하다고 합니다. 

 

ElasticItem.java

package com.hello.capston.elasticsearch.entity;

import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Document(indexName = "item")
@Getter
@NoArgsConstructor
public class ElasticItem {

    @Id
    private String id;

    @Field(type = FieldType.Text)
    private String view_name;

    @Field(type = FieldType.Text)
    private String item_url;

    @Field(type = FieldType.Text)
    private String item_name;

    @Field(type = FieldType.Integer)
    private int price;

    @PersistenceConstructor
    public ElasticItem(String view_name, String item_url, String item_name, int price) {
        this.view_name = view_name;
        this.item_url = item_url;
        this.item_name = item_name;
        this.price = price;
    }
}

이 Document 클래스가 은근히 중요합니다. 클래스 레벨에선 @Document 를 이용해 해당 클래스가 Elasticsearch의 Document와 대응되는 클래스라는 것을 명시합니다. 

 

그리고 각각의 필드들은 실제 Elasticsearch에 저장된 필드명을 적어야하고

 

가장 중요한 @PersistenceConstructor 를 이용해 실제 Elasticsearch의 객체와 우리 @Document 클래스를 매핑해줍니다. 

 

주의해야하는 점은 생성자에 파라미터로 들어간 내용으로 매핑하기 때문에 맘대로 바꾸시면 안됩니다. Elasticsearch에 있는 필드명과 동일하게 작성해주셔야 에러가 안뜹니다. 

 

 

package com.hello.capston.elasticsearch.service;

import com.hello.capston.elasticsearch.entity.ElasticItem;
import lombok.RequiredArgsConstructor;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ElasticsearchService {

    private final ElasticsearchRestTemplate template;

    public List<ElasticItem> search(String name) {
        QueryBuilder queryBuilder = QueryBuilders.matchQuery("viewName", name);
        SearchHits<ElasticItem> searchHits = template.search(new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .withPageable(PageRequest.of(0, 10))
                .build(), ElasticItem.class);

        List<ElasticItem> collect = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());

        return collect;
    }
}

직접적으로 검색을 도와줄 Service클래스입니다. ElasticsearchRestTemplate을 이용해 쿼리문을 만들어서 HTTP로 쏴주는 역할을 합니다. 

 

실제로 디버깅을 해보시면 쿼리문을 자동으로 생성해주는 역할을 하고 있다는 것을 알 수 있습니다. 

 

이제 이걸 그대로 컨트롤러에서 가져다 쓰면 됩니다. 

 

컨트롤러에서 사용하는건 너무 간단해서 포스팅에 작성하지는 않겠습니다. 

 

 

마치며

이렇게 스프링에서 Elasticsearch를 사용하는 방법에 대해서 알아봤습니다. 

 

스프링에서 사용하는 방법은 그렇게 어렵진 않았지만 이 방법을 찾기까지 수많은 삽질이 있었습니다. 뭐 별에별 방법들이 있었는데 저는 이 방법이 가장 깔끔하고 간단하면서 유지보수 하기도 쉬운 것 같습니다. 

 

여기까지 긴 글 읽어주셔서 감사합니다. 오늘도 즐거운 하루 되세요~