QueryDsl

order by 조건 설정 - 현재 기준 n일 미만 내 작성된 게시글 상위에 정렬

- 구현해야 하는 조건 : 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을 변수로 추출함으로써 훨씬 간결해짐을 확인할 수 있다.