Skip to content
Closed
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 @@ -30,6 +30,8 @@ public record ExploreStandardCriteria(
Integer size,
LocalDateTime popularityBucketAt) {

public static final String NO_POPULARITY_BUCKET = "NONE";

public static ExploreStandardCriteria of(
ExploreStandardRequest request, Long userId, long seed, LocalDateTime popularityBucketAt) {
return new ExploreStandardCriteria(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.math.BigDecimal;
Expand Down Expand Up @@ -354,7 +355,12 @@ public KeysetPageResponse<List<AlcoholDetailItem>> getStandardExplore(
Map<String, String> extra =
switch (criteria.sortType()) {
case RANDOM -> Map.of("seed", String.valueOf(criteria.seed()));
case POPULAR -> Map.of("bucketAt", criteria.popularityBucketAt().toString());
case POPULAR ->
Map.of(
"bucketAt",
criteria.popularityBucketAt() == null
? ExploreStandardCriteria.NO_POPULARITY_BUCKET
: criteria.popularityBucketAt().toString());
default -> Map.of();
};
return cursorCodec.encode(context, keys, extra);
Expand Down Expand Up @@ -466,11 +472,13 @@ private List<ExploreSeekKey> fetchCandidateIds(ExploreStandardCriteria criteria,

private List<ExploreSeekKey> fetchPopularityCandidates(
ExploreStandardCriteria criteria, CursorClaims claims, int fetchSize) {
if (criteria.popularityBucketAt() == null) {
return List.of();
}

NumberExpression<BigDecimal> score = alcoholPopularitySnapshot.popularityScore;
NumberExpression<BigDecimal> score =
alcoholPopularitySnapshot.popularityScore.coalesce(BigDecimal.ZERO);
// 첫 페이지에서 스냅샷이 없었다면 이후에도 점수 조인을 차단한다.
BooleanExpression bucketCondition =
criteria.popularityBucketAt() == null
? Expressions.FALSE
: alcoholPopularitySnapshot.bucketAt.eq(criteria.popularityBucketAt());
var query =
queryFactory
.select(alcohol.id, score)
Expand All @@ -479,15 +487,15 @@ private List<ExploreSeekKey> fetchPopularityCandidates(
.on(alcohol.region.id.eq(region.id))
.join(distillery)
.on(alcohol.distillery.id.eq(distillery.id))
.join(alcoholPopularitySnapshot)
.leftJoin(alcoholPopularitySnapshot)
.on(
alcoholPopularitySnapshot
.alcoholId
.eq(alcohol.id)
.and(
alcoholPopularitySnapshot.bucketGranularity.eq(
app.bottlenote.alcohols.constant.BucketGranularity.HOUR))
.and(alcoholPopularitySnapshot.bucketAt.eq(criteria.popularityBucketAt())));
.and(bucketCondition));
if (criteria.hasRatingRange()) {
query = query.leftJoin(rating).on(rating.id.alcoholId.eq(alcohol.id));
}
Expand All @@ -504,7 +512,7 @@ private List<ExploreSeekKey> fetchPopularityCandidates(
supporter.eqCurationId(criteria.curationId()),
supporter.isNotDeleted(),
popularitySeek(claims, criteria.sortOrder(), score))
.groupBy(alcohol.id, score)
.groupBy(alcohol.id, alcoholPopularitySnapshot.popularityScore)
.having(ratingInRange(criteria.ratingFrom(), criteria.ratingTo()))
.orderBy(scoreOrder, alcohol.id.asc())
.limit(fetchSize)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,13 @@ private LocalDateTime resolvePopularityBucketAt(ExploreStandardRequest request,
return null;
}
if (request.cursor() != null) {
return CursorKeys.requireExtraTime(
var claims =
cursorCodec.verify(
request.cursor(), ExploreStandardCriteria.of(request, userId, 0L, null).context()),
"bucketAt");
request.cursor(), ExploreStandardCriteria.of(request, userId, 0L, null).context());
if (ExploreStandardCriteria.NO_POPULARITY_BUCKET.equals(claims.extra().get("bucketAt"))) {
return null;
}
return CursorKeys.requireExtraTime(claims, "bucketAt");
}
return alcoholPopularitySnapshotRepository
.findLatestBucketAt(BucketGranularity.HOUR)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ private AlcoholExploreApiDocs() {}
description =
"""
인기순, 별점순, 리뷰순, 찜순, 무작위 중 원하는 기준으로 위스키를 둘러봅니다.
동일한 검색 조건에서는 정렬 방식과 관계없이 검색 대상이 같습니다.
인기 점수가 없는 위스키도 포함하며, 정렬할 때는 0점으로 취급합니다.
인기순의 점수 기준은 첫 페이지에서 고정되며, 점수가 같으면 ID 오름차순으로 정렬합니다.

ratingFrom/ratingTo는 목록에 표시되는 0.5 단위 반올림 집계 평점의 포함 하한/상한입니다.
한쪽 경계만 보내면 이상/이하로 조회하며, 둘 다 생략하면 별점 조건을 적용하지 않습니다.
Expand Down
Loading
Loading