Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] feat: 상품 검색 기능 구현 #445

Merged
merged 6 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
import com.funeat.product.dto.ProductsInCategoryResponse;
import com.funeat.product.dto.RankingProductDto;
import com.funeat.product.dto.RankingProductsResponse;
import com.funeat.product.dto.SearchProductDto;
import com.funeat.product.dto.SearchProductResultDto;
import com.funeat.product.dto.SearchProductResultsResponse;
import com.funeat.product.dto.SearchProductsResponse;
import com.funeat.product.exception.CategoryException.CategoryNotFoundException;
import com.funeat.product.exception.ProductException.ProductNotFoundException;
import com.funeat.product.persistence.CategoryRepository;
Expand Down Expand Up @@ -93,4 +97,26 @@ public RankingProductsResponse getTop3Products() {

return RankingProductsResponse.toResponse(rankingProductDtos);
}

public SearchProductsResponse searchProducts(final String query, final Pageable pageable) {
final Page<Product> products = productRepository.findAllByNameContaining(query, pageable);

final PageDto pageDto = PageDto.toDto(products);
final List<SearchProductDto> productDtos = products.stream()
.map(SearchProductDto::toDto)
.collect(Collectors.toList());

return SearchProductsResponse.toResponse(pageDto, productDtos);
}

public SearchProductResultsResponse getSearchResults(final String query, final Pageable pageable) {
final Page<ProductReviewCountDto> products = productRepository.findAllWithReviewCountByNameContaining(query, pageable);

final PageDto pageDto = PageDto.toDto(products);
final List<SearchProductResultDto> resultDtos = products.stream()
.map(it -> SearchProductResultDto.toDto(it.getProduct(), it.getReviewCount()))
.collect(Collectors.toList());

return SearchProductResultsResponse.toResponse(pageDto, resultDtos);
}
}
32 changes: 32 additions & 0 deletions backend/src/main/java/com/funeat/product/dto/SearchProductDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.funeat.product.dto;

import com.funeat.product.domain.Product;

public class SearchProductDto {

private final Long id;
private final String name;
private final String categoryType;

public SearchProductDto(final Long id, final String name, final String categoryType) {
this.id = id;
this.name = name;
this.categoryType = categoryType;
}

public static SearchProductDto toDto(final Product product) {
return new SearchProductDto(product.getId(), product.getName(), product.getCategory().getType().getName());
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getCategoryType() {
return categoryType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.funeat.product.dto;

import com.funeat.product.domain.Product;

public class SearchProductResultDto {

private final Long id;
private final String name;
private final Long price;
private final String image;
private final Double averageRating;
private final Long reviewCount;
private final String categoryType;

public SearchProductResultDto(final Long id, final String name, final Long price, final String image,
final Double averageRating, final Long reviewCount, final String categoryType) {
this.id = id;
this.name = name;
this.price = price;
this.image = image;
this.averageRating = averageRating;
this.reviewCount = reviewCount;
this.categoryType = categoryType;
}

public static SearchProductResultDto toDto(final Product product, final Long reviewCount) {
return new SearchProductResultDto(product.getId(), product.getName(), product.getPrice(), product.getImage(),
product.getAverageRating(), reviewCount, product.getCategory().getType().getName());
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public Long getPrice() {
return price;
}

public String getImage() {
return image;
}

public Double getAverageRating() {
return averageRating;
}

public Long getReviewCount() {
return reviewCount;
}

public String getCategoryType() {
return categoryType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.funeat.product.dto;

import com.funeat.common.dto.PageDto;
import java.util.List;

public class SearchProductResultsResponse {

private final PageDto page;
private final List<SearchProductResultDto> products;

public SearchProductResultsResponse(final PageDto page, final List<SearchProductResultDto> products) {
this.page = page;
this.products = products;
}

public static SearchProductResultsResponse toResponse(final PageDto page,
final List<SearchProductResultDto> products) {
return new SearchProductResultsResponse(page, products);
}

public PageDto getPage() {
return page;
}

public List<SearchProductResultDto> getProducts() {
return products;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.funeat.product.dto;

import com.funeat.common.dto.PageDto;
import java.util.List;

public class SearchProductsResponse {

private final PageDto page;
private final List<SearchProductDto> products;

public SearchProductsResponse(final PageDto page, final List<SearchProductDto> products) {
this.page = page;
this.products = products;
}

public static SearchProductsResponse toResponse(final PageDto page, final List<SearchProductDto> products) {
return new SearchProductsResponse(page, products);
}

public PageDto getPage() {
return page;
}

public List<SearchProductDto> getProducts() {
return products;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,20 @@ public interface ProductRepository extends JpaRepository<Product, Long> {
+ "WHERE p.averageRating > 3.0 "
+ "GROUP BY p.id")
List<ProductReviewCountDto> findAllByAverageRatingGreaterThan3();

@Query("SELECT p FROM Product p "
+ "WHERE p.name LIKE CONCAT('%', :name, '%') "
+ "ORDER BY CASE "
+ "WHEN p.name LIKE CONCAT(:name, '%') THEN 1 "
+ "ELSE 2 END")
Page<Product> findAllByNameContaining(@Param("name") final String name, final Pageable pageable);

@Query("SELECT new com.funeat.product.dto.ProductReviewCountDto(p, COUNT(r.id)) FROM Product p "
+ "LEFT JOIN Review r ON r.product.id = p.id "
+ "WHERE p.name LIKE CONCAT('%', :name, '%') "
+ "GROUP BY p.id "
+ "ORDER BY CASE "
+ "WHEN p.name LIKE CONCAT(:name, '%') THEN 1 "
+ "ELSE 2 END")
Page<ProductReviewCountDto> findAllWithReviewCountByNameContaining(@Param("name") final String name, final Pageable pageable);
Comment on lines +39 to +54
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍👍👍👍👍👍

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
import com.funeat.product.dto.ProductResponse;
import com.funeat.product.dto.ProductsInCategoryResponse;
import com.funeat.product.dto.RankingProductsResponse;
import com.funeat.product.dto.SearchProductResultsResponse;
import com.funeat.product.dto.SearchProductsResponse;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
Expand Down Expand Up @@ -40,4 +44,20 @@ public ResponseEntity<RankingProductsResponse> getRankingProducts() {
final RankingProductsResponse response = productService.getTop3Products();
return ResponseEntity.ok(response);
}

@GetMapping("/search/products")
public ResponseEntity<SearchProductsResponse> searchProducts(@RequestParam final String query,
@PageableDefault final Pageable pageable) {
final PageRequest pageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize());
final SearchProductsResponse response = productService.searchProducts(query, pageRequest);
return ResponseEntity.ok(response);
}

@GetMapping("/search/products/results")
public ResponseEntity<SearchProductResultsResponse> getSearchResults(@RequestParam final String query,
@PageableDefault final Pageable pageable) {
final PageRequest pageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize());
final SearchProductResultsResponse response = productService.getSearchResults(query, pageRequest);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.funeat.product.dto.ProductResponse;
import com.funeat.product.dto.ProductsInCategoryResponse;
import com.funeat.product.dto.RankingProductsResponse;
import com.funeat.product.dto.SearchProductResultsResponse;
import com.funeat.product.dto.SearchProductsResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -11,6 +13,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;

@Tag(name = "01.Product", description = "상품 기능")
public interface ProductController {
Expand Down Expand Up @@ -40,4 +43,22 @@ ResponseEntity<ProductsInCategoryResponse> getAllProductsInCategory(
)
@GetMapping
ResponseEntity<RankingProductsResponse> getRankingProducts();

@Operation(summary = "상품 자동 완성 검색", description = "공통 상품 및 PB 상품들의 이름 중에 해당하는 문자열이 포함되어 있는지 검색한다.")
@ApiResponse(
responseCode = "200",
description = "상품 검색 성공."
)
@GetMapping
ResponseEntity<SearchProductsResponse> searchProducts(@RequestParam final String query,
@PageableDefault final Pageable pageable);

@Operation(summary = "상품 검색 결과 조회", description = "문자열을 받아 상품을 검색하고 검색 결과들을 조회한다.")
@ApiResponse(
responseCode = "200",
description = "상품 검색 결과 조회 성공."
)
@GetMapping
ResponseEntity<SearchProductResultsResponse> getSearchResults(@RequestParam final String query,
@PageableDefault final Pageable pageable);
}
Loading