- 구현해야 하는 조건 : Request Parameter의 sortBy 값에 따라 0일 경우 작성일 기준 최신순 정렬 / 1일 경우 인기순으로 정렬하되, 현재 날짜 기준 일주일 미만의 게시글은 상위에, 일주일 이후의 게시글은 하위에 우선적으로 정렬한다.
위 조건에 따라서, 기본적인 JPA 기능만으로는 동적 쿼리를 작성하기 힘들다고 판단해 QueryDSL을 통해 구현하였다.
sortBy = 0일 경우 작성일 기준 최신순 정렬에 해당하는 로직은 작성일 기준 내림차순 정렬만 수행하면 쉽게 구현할 수 있다.
그러나 sortBy=1일 경우 현재 날짜를 기준으로 7일 미만의 게시글을 상위, 이후 게시글은 하위에 정렬해야 하는 부분에서 어려움이 있었다.
최초에 구상해 본 QueryDSL의 orderBy 로직은 아래와 같다.
public class ReportRepositoryImpl implements ReportRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final EntityManager em;
public ReportRepositoryImpl(EntityManager em) {
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public List<Report> findAllByQuery(String query, Integer isTag, Integer category, Integer sortBy) {
return queryFactory
.selectFrom(report)
...
.orderBy(sortBy == 0 ? report.createdAt.desc() :
report.createdAt
.after(LocalDate.now().minusDays(7).atTime(LocalTime.MIN))
.desc()) // boolean 값에 대한 desc 정렬 (true first)
.fetch();
}
게시글(report)의 작성일(createdAt)이 현재 날짜 -7일의 00:00 기준으로 이후라면 true, 이전이라면 false를 반환한다.
order by의 boolean 정렬 기준은 false first 이므로, 내림차순 정렬을 통해 true(작성 일주일 미만)를 우선적으로 정렬되도록 하였다.
그러나, 본 코드를 동작시켰을 때 해당 에러가 발생했다.
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node:
QueryDSL을 통해 boolean 결과에 대한 정렬이 올바르게 되지 않음을 확인하고,
boolean 결과를 true -> 1 및 false -> 0 과 같이 Integer 값으로 변환하여 이를 내림차순 정렬하는 로직으로 변경하여 해결했다.
변경된 코드는 아래와 같다.
public class ReportRepositoryImpl implements ReportRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final EntityManager em;
public ReportRepositoryImpl(EntityManager em) {
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public List<Report> findAllByQuery(String query, Integer isTag, Integer category, Integer sortBy) {
NumberExpression<Integer> date = new CaseBuilder()
.when(report.createdAt.after(LocalDate.now().minusDays(7).atTime(LocalTime.MIN)))
.then(1)
.otherwise(0); // boolean 값에 대한 Integer 변환
return queryFactory
.selectFrom(report)
...
.orderBy(sortBy == 0 ? report.createdAt.desc() : date.desc())
// 변환한 Integer 값에 대한 내림차순 정렬
.fetch();
}
CaseBuilder를 통해 NumberExpression<Integer>를 구현한다.
when 메서드를 통해 기존과 같이 게시글(report)의 작성일(createdAt)이 현재 날짜 -7일의 00:00 기준으로 이후라면 true, 이전이라면 false를 반환하도록 하고, then 과 otherwise 메서드를 통해 when 메서드의 결과가 true일 경우 1, false일 경우 0으로 변환하도록 NumberExpression을 구현했다.
orderBy 에서는 단순히 date.desc() 를 통해 결과값(1 or 0)에 대해 내림차순 정렬을 수행함으로써
현재 날짜 기준 일주일 미만 게시글은 상위, 일주일 이후 게시글은 하위에 정렬되도록 할 수 있었다.
오류도 정상적으로 해결하고, 정렬도 올바르게 수행될 뿐 아니라
orderBy 쪽 코드도 NumberExpression을 변수로 추출함으로써 훨씬 간결해짐을 확인할 수 있다.
'QueryDsl' 카테고리의 다른 글
QueryDsl에서 결과를 가져오는 메서드, fetchFirst()와 fetchOne()의 차이 (0) | 2023.09.20 |
---|---|
QueryDsl에서 in절에 empty list, null이 올 때 어떻게 처리될까? (0) | 2023.09.17 |
No-Offset 적용을 통한 무한 스크롤 방식의 페이징 쿼리 성능 개선 (0) | 2023.05.17 |
exists 메서드 성능 개선 - count vs exists (0) | 2023.05.01 |