📌보면 좋을 정보: https://tecoble.techcourse.co.kr/post/2021-10-19-elasticsearch/
일래스틱서치는 루씬 기반의 검색 엔진이다. HTTP 웹 인터페이스와 스키마에서 자유로운 JSON 문서와 함께 분산 멀티테넌트 지원 전문 검색 엔진을 제공한다. 일래스틱서치는 자바로 개발되어 있으며 아파치 라이선스 조항에 의거하여 오픈 소스로 출시되어 있다.
📌 출처: https://ko.wikipedia.org/wiki/%EC%9D%BC%EB%9E%98%EC%8A%A4%ED%8B%B1%EC%84%9C%EC%B9%98
엘라스틱 서치와 관계형 데이터베이스의 상관관계
📌출처: https://www.slideshare.net/deview/2d1elasticsearch
- 도커를 사용한 엘라스틱서치 다운로드 : docker pull docker.elastic.co/elasticsearch/elasticsearch:8.5.3
- 실행: docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:8.5.3
본인의 프로젝트 구성
호환가능한 버전이 다 다르니 이 부분을 유의하며 진행할 것
https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#new-features.5-0-0
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
@Configuration
public class ElasticConfig extends ElasticsearchConfiguration {
@Value("${spring.elastic.url}")
private String elasticUrl;
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder()
.connectedTo(elasticUrl)
.build();
}
}
logging.level.org.springframework.data.elasticsearch.client.WIRE=TRACE
https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference
package com.yaloostore.shop.product.documents;
import com.yaloostore.shop.helper.Indices;
import lombok.*;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Setting;
import java.time.LocalDateTime;
/**
* 엘라스틱 서치에 사용되는 인덱스 입니다.(관계형 디비 table - 엘라스틱서치 index)
* */
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
@Setting(settingPath = "/static/elastic-settings.json")
@Document(indexName = Indices.PRODUCTS_INDEX)
public class SearchProduct {
@Id
@Field(name = "product_id", type = FieldType.Long)
private Long productId;
@Field(name = "productName", type = FieldType.Text)
private String productName;
@Field(name = "product_id", type = FieldType.Long)
private Long stock;
@Field(name = "product_created_at", type =FieldType.Date)
private LocalDateTime productCreatedAt;
@Field(type=FieldType.Text)
private String description;
@Field(name = "thumbnail_url",type=FieldType.Text)
private String thumbnailUrl;
@Field(name = "fixed_price", type=FieldType.Long)
private Long fixedPrice;
@Field(name = "raw_price", type=FieldType.Long)
private Long rawPrice;
@Field(name = "is_selled", type=FieldType.Boolean)
private Boolean isSelled;
@Field(name = "is_deleted", type=FieldType.Boolean)
private Boolean isDeleted;
@Field(name ="is_expose", type=FieldType.Boolean)
private Boolean isExpose;
@Field(name = "discount_percent", type=FieldType.Long)
private Long discountPercent;
}
@Document
해당 클래스가 인덱스(=Document)클래스임을 명시@Id
Entity에서 작업한 것처럼 pk를 지정하는 작업과 동일하게 진행@Field
name, type , pattern등을 지정 가능@Setting
https://esbook.kimjmin.net/07-settings-and-mappings/7.1-settings@Mapping
https://esbook.kimjmin.net/07-settings-and-mappings/7.2-mappings package com.yaloostore.shop.product.repository.elasticSearch;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import com.yaloostore.shop.product.documents.SearchProduct;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.Optional;
/**
* 엘라스틱 서치를 사용해서 상품 검색을 위한 레포지토리입니다.
* */
public interface ElasticCommonProductRepository extends ElasticsearchRepository<SearchProduct, Long> {
Page<SearchProduct> findByProductName(Pageable pageable, String productName);
Optional<SearchProduct> findById(Long productId);
}
package com.yaloostore.shop.product.service.elasticSearch;
import com.yaloostore.shop.common.dto.PaginationResponseDto;
import com.yaloostore.shop.product.dto.response.SearchProductResponseDto;
import com.yaloostore.shop.product.dto.transfer.SearchProductTransfer;
import com.yaloostore.shop.product.documents.SearchProduct;
import com.yaloostore.shop.product.repository.elasticSearch.ElasticCommonProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ElasticProductServiceImpl implements ElasticProductService {
private final ElasticCommonProductRepository elasticCommonProductRepository;
/**
* {@inheritDoc}
* */
@Override
public Page<SearchProductResponseDto> searchProductByProductName(Pageable pageable, String productName) {
Page<SearchProduct> searchProducts = elasticCommonProductRepository.findByProductName(pageable,productName);
List<SearchProductResponseDto> response = searchProducts.stream()
.map(SearchProductTransfer::fromEntity).collect(Collectors.toList());
//cotent pageable, total
return new PageImpl<>(response, pageable, searchProducts.getTotalElements());
}
}
package com.yaloostore.shop.product.controller.elasticSearch;
import com.yalooStore.common_utils.dto.ResponseDto;
import com.yaloostore.shop.common.dto.PaginationResponseDto;
import com.yaloostore.shop.product.dto.response.SearchProductResponseDto;
import com.yaloostore.shop.product.service.elasticSearch.ElasticProductService;
import jakarta.validation.constraints.Size;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 엘라스틱 서치를 사용해서 상품 검색을 하는 컨트롤러입니다.
* */
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/service/products/search")
public class ElasticProductRestController {
private final ElasticProductService elasticProductService;
@GetMapping(params = "productName")
public ResponseEntity<ResponseDto> searchProductByProductName(@RequestParam @Size(max = 30) String productName,
@PageableDefault Pageable pageable){
Page<SearchProductResponseDto> response = elasticProductService.searchProductByProductName(pageable, productName);
return ResponseEntity.ok(
ResponseDto.builder()
.success(true)
.status(HttpStatus.OK)
.data(response.getContent())
.build()
);
}
}