Lv. 3 QueryDSL
조건
- 검색 조건은 다음과 같아요.
- 검색 키워드로 일정의 제목을 검색할 수 있어요.
- 일정의 생성일 범위로 검색할 수 있어요.
- 담당자의 닉네임으로도 검색이 가능해요.
- 닉네임은 부분적으로 일치해도 검색이 가능해요.
- 다음의 내용을 포함해서 검색 결과를 반환해주세요.
- 일정에 대한 모든 정보가 아닌, 제목만 넣어주세요.
- 해당 일정의 담당자 수를 넣어주세요.
- 해당 일정의 총 댓글 개수를 넣어주세요.
- 검색 결과는 페이징 처리되어 반환되도록 합니다.
1. 컨트롤러 작성
@GetMapping("/todos/searchFilters")
public ResponseEntity<Page<TodoSummaryResponse>> searchTodosWithFilters(
@RequestParam(required = false) String keyword,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)LocalDate startDate,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)LocalDate endDate,
@RequestParam(required = false) String nickname,
@PageableDefault(size = 10, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable
) {
Page<TodoSummaryResponse> result = todoService.searchTodosWithFilters(keyword, nickname, startDate, endDate, pageable);
return ResponseEntity.ok(result);
}
required = false 사용하여 빈값도 받음
2. 응답 DTO 작성
public record TodoSummaryResponse (
String title,
Long managerCount,
Long commentCount
) {
@QueryProjection
public TodoSummaryResponse(String title, Long managerCount, Long commentCount) {
this.title = title;
this.managerCount = managerCount;
this.commentCount = commentCount;
}
}
3. Repository 작성
Page<TodoSummaryResponse> searchTodosWithFilters(String keyword, String nickname, LocalDate startDate, LocalDate endDate, Pageable pageable);
@Override
public Page<TodoSummaryResponse> searchTodosWithFilters(String keyword, String nickname, LocalDate startDate,
LocalDate endDate, Pageable pageable) {
QTodo todo = QTodo.todo;
QUser user = QUser.user;
QManager manager = QManager.manager;
QComment comment = QComment.comment;
List<TodoSummaryResponse> content = queryFactory
.select(Projections.constructor(
TodoSummaryResponse.class,
todo.title,
manager.countDistinct(),
comment.count()
))
.from(todo)
.leftJoin(todo.managers, manager)
.leftJoin(manager.user, user)
.leftJoin(todo.comments, comment)
.where(
titleContains(keyword),
nicknameContains(nickname),
createdBetween(startDate, endDate)
)
.groupBy(todo.id)
.orderBy(todo.createdAt.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
Long count = queryFactory
.select(todo.countDistinct())
.from(todo)
.leftJoin(todo.managers, manager)
.leftJoin(manager.user, user)
.where(
titleContains(keyword),
nicknameContains(nickname),
createdBetween(startDate, endDate)
)
.fetchOne();
return new PageImpl<>(content, pageable, count);
}
private BooleanExpression titleContains(String keyword) {
return StringUtils.hasText(keyword) ? QTodo.todo.title.contains(keyword) : null;
}
private BooleanExpression nicknameContains(String nickname) {
return StringUtils.hasText(nickname) ? QManager.manager.user.nickname.contains(nickname) : null;
}
private BooleanExpression createdBetween(LocalDate start, LocalDate end) {
if (start != null && end != null) {
return QTodo.todo.createdAt.between(start.atStartOfDay(), end.plusDays(1).atStartOfDay());
}
return null;
}
4. 서비스 작성
public Page<TodoSummaryResponse> searchTodosWithFilters(String keyword, String nickname, LocalDate startDate, LocalDate endDate, Pageable pageable) {
return todoQueryRepository.searchTodosWithFilters(keyword, nickname, startDate, endDate, pageable);
}