Skip to content

Commit 494fc8d

Browse files
committed
feat: 선택영양소 모두 포함 결과만 반환
1 parent 0539ec8 commit 494fc8d

File tree

2 files changed

+28
-1
lines changed

2 files changed

+28
-1
lines changed

src/main/java/com/DecodEat/domain/products/repository/ProductSpecification.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import com.DecodEat.domain.products.entity.RawMaterial.RawMaterialCategory;
99
import jakarta.persistence.criteria.Join;
1010
import jakarta.persistence.criteria.Predicate;
11+
import jakarta.persistence.criteria.Root;
12+
import jakarta.persistence.criteria.Subquery;
1113
import org.springframework.data.jpa.domain.Specification;
1214

1315
import java.util.ArrayList;
@@ -39,4 +41,29 @@ public static Specification<Product> hasRawMaterialCategories(List<RawMaterialCa
3941
return rawMaterialJoin.get("category").in(categories);
4042
};
4143
}
44+
45+
public static Specification<Product> hasAllRawMaterialCategories(List<RawMaterialCategory> categories) {
46+
return (root, query, criteriaBuilder) -> {
47+
48+
// 1. 서브쿼리를 생성합니다. 이 서브쿼리는 조건에 맞는 Product의 ID(Long)를 반환할 것입니다.
49+
Subquery<Long> subquery = query.subquery(Long.class);
50+
Root<Product> subRoot = subquery.from(Product.class); // 서브쿼리에서 Product 테이블을 기준으로 삼습니다.
51+
52+
// 2. 서브쿼리 내에서 필요한 조인을 수행합니다.
53+
Join<Product, ProductRawMaterial> subProductRawMaterialJoin = subRoot.join("ingredients");
54+
Join<ProductRawMaterial, RawMaterial> subRawMaterialJoin = subProductRawMaterialJoin.join("rawMaterial");
55+
56+
// 3. 서브쿼리의 핵심 로직: '상품별로 카테고리를 묶고(GROUP BY), 그 개수가 일치하는지 확인(HAVING)'
57+
subquery.select(subRoot.get("id")) // 조건에 맞는 Product의 ID를 선택
58+
.where(subRawMaterialJoin.get("category").in(categories)) // 먼저 카테고리가 검색 대상에 포함되는 제품만 필터링
59+
.groupBy(subRoot.get("id")) // Product ID 별로 그룹화
60+
.having(criteriaBuilder.equal(
61+
criteriaBuilder.countDistinct(subRawMaterialJoin.get("category")), // 각 제품의 유니크한 카테고리 개수를 세고
62+
categories.size() // 그 개수가 우리가 찾는 카테고리 리스트의 전체 개수와 같은지 확인
63+
));
64+
65+
// 4. 메인 쿼리: Product의 ID가 위 서브쿼리 결과 목록에 포함(IN)되는 제품만 최종 선택합니다.
66+
return root.get("id").in(subquery);
67+
};
68+
}
4269
}

src/main/java/com/DecodEat/domain/products/service/ProductService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public PageResponseDto<ProductSearchResponseDto.ProductPrevDto> searchProducts(S
142142
}
143143

144144
if (categories != null && !categories.isEmpty()) {
145-
spec = spec.and(ProductSpecification.hasRawMaterialCategories(categories));
145+
spec = spec.and(ProductSpecification.hasAllRawMaterialCategories(categories));
146146
}
147147

148148
// Specification과 Pageable을 사용하여 데이터 조회

0 commit comments

Comments
 (0)