Skip to content

Commit

Permalink
Merge pull request #28 from team-acode/feat/search
Browse files Browse the repository at this point in the history
feat: 검색
  • Loading branch information
yoonsseo authored Jan 20, 2024
2 parents 9c4a699 + 3916b6e commit 5ea91f7
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@
public class PageableResponse<T> {
// TODO global로 옮겨야하나 고민

private List<T> data;

private int totalPages;
private long totalElements;

private List<T> data;

public PageableResponse(List<T> data, int totalPages, long totalElements){
this.data = data;
this.totalPages = totalPages;
this.totalElements = totalElements;
}

public PageableResponse(Page<T> data) {
this.totalElements = data.getTotalElements();
this.totalPages = data.getTotalPages();
this.data = data.getContent();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public Page<DisplayFragrance> searchByIngredient(String ingredientName, Pageable

@Override
public List<ExtractFamily> extractFamilies(List<Long> fragranceIdList){
return queryFactory.select(new QExtractFamily(
return queryFactory.select(new QExtractFamily(
family.korName,
family.engName,
family.summary,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package server.acode.domain.family.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import server.acode.domain.family.dto.SimilarFragranceOrCond;
import server.acode.domain.fragrance.dto.request.SearchCond;
import server.acode.domain.fragrance.dto.response.ExtractFragrance;
import server.acode.domain.fragrance.dto.response.FamilyCountDto;
import server.acode.domain.fragrance.dto.response.FragranceInfo;
Expand All @@ -23,4 +26,6 @@ public interface FragranceFamilyRepositoryCustom {
List<FamilyCountDto> countFamily(List<Long> fragranceIdList);

List<ExtractFragrance> extractFragrance(List<Long> familyIdList);

Page<FragranceInfo> searchFragrance(SearchCond cond, String additionalFamily, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package server.acode.domain.family.repository;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Repository;
import server.acode.domain.family.dto.SimilarFragranceOrCond;
import server.acode.domain.family.entity.QFragranceFamily;
import server.acode.domain.fragrance.dto.request.SearchCond;
import server.acode.domain.fragrance.dto.response.*;
import server.acode.domain.fragrance.entity.QFragrance;

import java.util.List;

import static org.springframework.util.StringUtils.*;
import static server.acode.domain.family.entity.QFamily.*;
import static server.acode.domain.family.entity.QFragranceFamily.*;
import static server.acode.domain.fragrance.entity.QFragrance.fragrance;

Expand Down Expand Up @@ -84,8 +90,8 @@ public List<FragranceInfo> searchSimilarFragranceOr(SimilarFragranceOrCond cond)
.join(fragranceFamily.fragrance, fragrance)
.where(
fragrance.id.notIn(cond.getSelectedFragranceIdList())
.and(fragranceFamily.family.id.eq(cond.getFamilyId1())
.or(fragranceFamily.family.id.eq(cond.getFamilyId2())))
.and(fragranceFamily.family.id.eq(cond.getFamilyId1())
.or(fragranceFamily.family.id.eq(cond.getFamilyId2())))
)
.limit(cond.getCount())
.orderBy(fragrance.view.desc())
Expand Down Expand Up @@ -144,11 +150,11 @@ public List<FamilyCountDto> countFamily(List<Long> fragranceIdList) {
public List<ExtractFragrance> extractFragrance(List<Long> familyIdList) {
return queryFactory
.selectDistinct(new QExtractFragrance(
fragrance.id,
fragrance.name,
fragrance.brand.korName,
fragranceFamily.family.korName,
fragrance.thumbnail
fragrance.id,
fragrance.name,
fragrance.brand.korName,
fragranceFamily.family.korName,
fragrance.thumbnail
)
)
.from(fragranceFamily)
Expand All @@ -157,4 +163,74 @@ public List<ExtractFragrance> extractFragrance(List<Long> familyIdList) {
}


@Override
public Page<FragranceInfo> searchFragrance(SearchCond cond, String additionalFamily, Pageable pageable) {
List<FragranceInfo> contents = queryFactory
.select(new QFragranceInfo(
fragrance.id.as("fragranceId"),
fragrance.thumbnail,
fragrance.name.as("fragranceName"),
fragrance.brand.korName
)).distinct()
.from(fragranceFamily)
.where(
fragranceNameContains(cond.getSearch())
.or(korBrandNameContains(cond.getSearch()))
.or(engBrandNameContains(cond.getSearch())),
familyNameEq(cond.getFamily()),
additionalFamilyNameEq(additionalFamily)
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();

JPAQuery<Long> countQuery = queryFactory
.select(fragrance.count())
.from(fragranceFamily)
.where(
fragranceNameContains(cond.getSearch())
.or(korBrandNameContains(cond.getSearch()))
.or(engBrandNameContains(cond.getSearch())),
familyNameEq(cond.getFamily()),
additionalFamilyNameEq(additionalFamily)
);

return PageableExecutionUtils.getPage(contents, pageable, countQuery::fetchOne);
}

private BooleanExpression fragranceNameContains(String search) {
return hasText(search)
? fragrance.name.containsIgnoreCase(search)
: null;
}

private BooleanExpression korBrandNameContains(String search) {
return hasText(search)
? fragrance.brand.korName.containsIgnoreCase(search)
: null;
}

private BooleanExpression engBrandNameContains(String search) {
return hasText(search)
? fragrance.brand.engName.containsIgnoreCase(search)
: null;
}

private BooleanExpression familyNameEq(String familyName) {
return hasText(familyName)
? family.korName.eq(familyName)
: null;
}

private BooleanExpression additionalFamilyNameEq(String additionalFamily) {
QFragranceFamily fragranceFamilySub = new QFragranceFamily("fragranceFamilySub");

return hasText(additionalFamily)
? fragrance.id.in(
JPAExpressions.select(fragranceFamilySub.fragrance.id)
.from(fragranceFamilySub)
.where(fragranceFamilySub.family.korName.eq(additionalFamily)))
: null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package server.acode.domain.fragrance.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
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;
import server.acode.domain.family.dto.response.PageableResponse;
import server.acode.domain.fragrance.dto.request.SearchCond;
import server.acode.domain.fragrance.dto.response.BrandInfo;
import server.acode.domain.fragrance.dto.response.FragranceInfo;
import server.acode.domain.fragrance.dto.response.SearchBrandResponse;
import server.acode.domain.fragrance.service.SearchService;
import server.acode.global.common.PageRequest;

@RestController
@RequestMapping("/api/v1/search")
@RequiredArgsConstructor
@Tag(name = "Search", description = "검색 API")
public class SearchController {
private final SearchService searchService;

@Operation(summary = "브랜드 검색",
description = "search 검색어: 필수\n\n" +
"검색어 없는 경우 `400 SEARCH_NOT_FOUND`\n\n" +
"페이지는 파라미터 없을 시 기본 page = 1, size = 10입니다")
@GetMapping("/brand")
public PageableResponse<BrandInfo> searchBrand(@RequestParam("search") String search, PageRequest pageRequest) {
return searchService.searchBrand(search, pageRequest);
}

@Operation(summary = "향수 검색",
description = "검색어 외에는 필요한 값만 파라미터에 넣으면 됩니다 \n\n" +
"search 검색어: 필수\n\n" +
" 검색어 없는 경우 `400 SEARCH_NOT_FOUND`\n\n" +
"family 계열: 한글로 넣어주세요 ex. `플로럴`\n\n" +
" * 계열 두 개 검색 시에는 두 계열 사이 공백 한 칸 (url 상으로는 %20) 넣어주세요 ex. `플로럴 프루티` \n\n" +
"페이지는 파라미터 없을 시 기본 page = 1, size = 10입니다")
@GetMapping("/fragrance")
public PageableResponse<FragranceInfo> searchFragrance(SearchCond cond, PageRequest pageRequest) {
return searchService.searchFragrance(cond, pageRequest);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package server.acode.domain.fragrance.dto.request;

import lombok.Data;

@Data
public class SearchCond {
private String search;
private String family;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package server.acode.domain.fragrance.dto.response;

import com.querydsl.core.annotations.QueryProjection;
import lombok.Data;

@Data
public class BrandInfo {
private Long brandId;
private String korName;
private String roundImg;

@QueryProjection
public BrandInfo(Long brandId, String korName, String roundImg) {
this.brandId = brandId;
this.korName = korName;
this.roundImg = roundImg;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package server.acode.domain.fragrance.dto.response;

import com.querydsl.core.annotations.QueryProjection;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;


@Getter
@NoArgsConstructor
public class SearchBrandResponse {
private List<BrandInfo> brandList;

public SearchBrandResponse(List<BrandInfo> brandList) {
this.brandList = brandList;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package server.acode.domain.fragrance.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import server.acode.domain.fragrance.entity.Brand;

public interface BrandRepository extends JpaRepository<Brand, Long> {
@Repository
public interface BrandRepository extends JpaRepository<Brand, Long>, BrandRepositoryCustom {
public Brand findByKorName(String korName);

boolean existsByKorName(String brandName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package server.acode.domain.fragrance.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import server.acode.domain.fragrance.dto.response.BrandInfo;

@Repository
public interface BrandRepositoryCustom {
Page<BrandInfo> searchBrand(String search, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package server.acode.domain.fragrance.repository;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Repository;
import server.acode.domain.fragrance.dto.response.BrandInfo;
import server.acode.domain.fragrance.dto.response.QBrandInfo;

import java.util.List;

import static server.acode.domain.fragrance.entity.QBrand.*;

@Repository
public class BrandRepositoryImpl implements BrandRepositoryCustom {
private final JPAQueryFactory queryFactory;

public BrandRepositoryImpl(EntityManager em) {
this.queryFactory = new JPAQueryFactory(em);
}


@Override
public Page<BrandInfo> searchBrand(String search, Pageable pageable) {
List<BrandInfo> contents = queryFactory
.select(new QBrandInfo(
brand.id.as("brandId"),
brand.korName,
brand.roundImg
))
.from(brand)
.where(
brandNameContains(search)
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();

JPAQuery<Long> countQuery = queryFactory
.select(brand.count())
.from(brand)
.where(
brandNameContains(search)
);

return PageableExecutionUtils.getPage(contents, pageable, countQuery::fetchOne);
}

private BooleanExpression brandNameContains(String search) {
return brand.korName.containsIgnoreCase(search)
.or(brand.engName.containsIgnoreCase(search));
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package server.acode.domain.fragrance.repository;

import org.springframework.stereotype.Repository;
import server.acode.domain.fragrance.dto.request.KeywordCond;
import server.acode.domain.fragrance.dto.response.ExtractFamily;

import java.util.List;

Expand All @@ -21,4 +19,4 @@ public interface FragranceRepositoryCustom {
List<Long> extractByStyleOr(String style1, String style2, List<Long> fragranceIdList);

// List<Long> extractByStyle(String style, List<Long> fragranceIdList);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import server.acode.domain.fragrance.dto.response.ExtractFamily;
import server.acode.domain.fragrance.dto.response.QExtractFamily;
import server.acode.domain.fragrance.entity.Concentration;

import java.util.List;
Expand Down
Loading

0 comments on commit 5ea91f7

Please sign in to comment.