Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] 노오프셋 페이징 기법으로 변경 #209

Merged
merged 2 commits into from
Oct 2, 2024
Merged
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 @@ -35,19 +35,17 @@ public BaseResponse<AnswerResponse.AnswerId> createAnswer(@AuthMember Member mem
}

@Operation(summary = "질문에 대한 답변 조회 API", description = "특정 질문에 대한 답변리스트를 조회하는 API입니다."
+ "pathVariable으로 questionId를 주세요.")
+ "pathVariable으로 questionId를 주세요.<BR>**첫 번째 조회 시 threshold를 비워 보내고, 이후 조회 시 앞선 조회의 반환값으로 받은 threshold를 보내주세요.**")
@GetMapping("/question/{questionId}")
public BaseResponse<SliceResponse<AnswerInfo>> getAnswerList(
@AuthMember Member member,
@Parameter(description = "질문 식별자")
@PathVariable(value = "questionId") Long questionId,
@Parameter(description = "Pull to Refresh 후 마지막 index")
@RequestParam(required = false) Long threshold,
@Parameter(description = "조회할 페이지 번호<br>0부터 시작")
@RequestParam(defaultValue = "0", required = false) Integer pageNumber,
@Parameter(description = "이전 조회의 마지막 index")
@RequestParam(required = false, name = "threshold") Long lastIndex,
@Parameter(description = "조회할 페이지 크기")
@RequestParam(defaultValue = "1000", required = false) Integer pageSize) {
return BaseResponse.onSuccess(answerService.getAnswerList(member.getId(), questionId, threshold, PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
return BaseResponse.onSuccess(answerService.getAnswerList(member.getId(), questionId, lastIndex, PageRequest.of(0, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
}

@Operation(summary = "답변 수정 API", description = " 답변 수정 API 입니다." +
Expand All @@ -72,29 +70,25 @@ public BaseResponse<AnswerResponse.AnswerLike> toggleAnswerHeart(@AuthMember Mem
return BaseResponse.onSuccess(answerService.toggleAnswerHeart(member, answerId));
}

@Operation(summary = "작성한 답변 조회 API", description = " 작성한 답변 조회 API 입니다.")
@Operation(summary = "작성한 답변 조회 API", description = " 작성한 답변 조회 API 입니다.<BR>**첫 번째 조회 시 threshold를 비워 보내고, 이후 조회 시 앞선 조회의 반환값으로 받은 threshold를 보내주세요.**")
@GetMapping
public BaseResponse<SliceResponse<MemberAnswerInfo>> getMemberAnswer(
@AuthMember Member member,
@Parameter(description = "Pull to Refresh 후 마지막 index")
@RequestParam(required = false) Long threshold,
@Parameter(description = "조회할 페이지 번호<br>0부터 시작")
@RequestParam(defaultValue = "0", required = false) Integer pageNumber,
@Parameter(description = "이전 조회의 마지막 index")
@RequestParam(required = false, name = "threshold") Long lastIndex,
@Parameter(description = "조회할 페이지 크기")
@RequestParam(defaultValue = "1000", required = false) Integer pageSize) {
return BaseResponse.onSuccess(answerService.getMemberAnswer(member, threshold, PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
return BaseResponse.onSuccess(answerService.getMemberAnswer(member, lastIndex, PageRequest.of(0, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
}

@Operation(summary = "좋아한 답변 조회 API", description = " 좋아한 답변 조회 API 입니다.")
@Operation(summary = "좋아한 답변 조회 API", description = " 좋아한 답변 조회 API 입니다.<BR>**첫 번째 조회 시 threshold를 비워 보내고, 이후 조회 시 앞선 조회의 반환값으로 받은 threshold를 보내주세요.**")
@GetMapping("/heart")
public BaseResponse<SliceResponse<MemberAnswerInfo>> getMemberHeartAnswer(
@AuthMember Member member,
@Parameter(description = "Pull to Refresh 후 마지막 index")
@RequestParam(required = false) Long threshold,
@Parameter(description = "조회할 페이지 번호<br>0부터 시작")
@RequestParam(defaultValue = "0", required = false) Integer pageNumber,
@Parameter(description = "이전 조회의 마지막 index")
@RequestParam(required = false, name = "threshold") Long lastIndex,
@Parameter(description = "조회할 페이지 크기")
@RequestParam(defaultValue = "1000", required = false) Integer pageSize) {
return BaseResponse.onSuccess(answerService.getMemberHeartAnswer(member, threshold, PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
return BaseResponse.onSuccess(answerService.getMemberHeartAnswer(member, lastIndex, PageRequest.of(0, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,20 @@
public interface AnswerRepository extends JpaRepository<Answer, Long> {
Optional<Answer> findById(Long answerId);

Slice<Answer> findByIdInAndIdIsLessThanEqual(Set<Long> answerIds, Long lastIndex, Pageable pageable);
@Query("SELECT a FROM Answer a WHERE (a.id < :lastIndex OR :lastIndex IS NULL) AND a.id IN :answerIds")
Slice<Answer> findByIdInAndIdIsLessThan(Set<Long> answerIds, Long lastIndex, Pageable pageable);

boolean existsByQuestionAndMember(Question question, Member member);

@Query("SELECT a AS answer, (r IS NOT NULL) AS isReported " +
"FROM Answer a " +
"LEFT JOIN " +
"Report r ON r.answer = a " +
"WHERE a.id <= :lastIndex AND a.question.id = :questionId")
"WHERE (a.id < :lastIndex OR :lastIndex IS NULL) AND a.question.id = :questionId")
Slice<AnswerInfoInterface> findByQuestion(@Param("questionId") Long questionId, Long lastIndex, Pageable pageable);

Slice<Answer> findByMemberAndIdIsLessThanEqual(@Param("member") Member member, Long lastIndex, Pageable pageable);
@Query("SELECT a FROM Answer a WHERE (a.id < :lastIndex OR :lastIndex IS NULL) AND a.member = :member")
Slice<Answer> findByMemberAndIdIsLessThan(@Param("member") Member member, Long lastIndex, Pageable pageable);

@Query("SELECT COUNT(a) FROM Answer a WHERE a.question.id = :questionId")
Integer getAnswerCountByQuestionId(Long questionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,8 @@ public AnswerLike toggleAnswerHeart(Member loginMember, Long answerId) {

@Override
public SliceResponse<AnswerInfo> getAnswerList(Long memberId, Long questionId, Long lastIndex, Pageable pageable) {
lastIndex = getLastIndex(lastIndex);
Slice<AnswerInfoInterface> answerInfoSliceInterface = answerRepository.findByQuestion(questionId, lastIndex, pageable);
lastIndex = getLastIndexFromAnswerInfoInterface(lastIndex, answerInfoSliceInterface);
lastIndex = getLastIndexFromAnswerInfoInterface(answerInfoSliceInterface);
return SliceResponse.toSliceResponse(answerInfoSliceInterface, answerInfoSliceInterface.getContent().stream().map(
answerInfoDto -> answerMapper.toAnswerInfo(
answerInfoDto.getAnswer(),
Expand All @@ -116,9 +115,8 @@ public SliceResponse<AnswerInfo> getAnswerList(Long memberId, Long questionId, L
// 유저가 작성한 답변 조회
@Override
public SliceResponse<MemberAnswerInfo> getMemberAnswer(Member member, Long lastIndex, Pageable pageable) {
lastIndex = getLastIndex(lastIndex);
Slice<Answer> answerSlice = answerRepository.findByMemberAndIdIsLessThanEqual(member, lastIndex, pageable);
lastIndex = getLastIndexFromAnswer(lastIndex, answerSlice);
Slice<Answer> answerSlice = answerRepository.findByMemberAndIdIsLessThan(member, lastIndex, pageable);
lastIndex = getLastIndexFromAnswer(answerSlice);
return SliceResponse.toSliceResponse(
answerSlice, answerSlice.getContent().stream()
.map(answer -> answerMapper.toMemberAnswerInfo(
Expand All @@ -132,9 +130,8 @@ public SliceResponse<MemberAnswerInfo> getMemberAnswer(Member member, Long lastI
// 유저가 좋아한 답변 조회 //TODO 좋아요니까 좋아요한 순으로 정렬해야할거같은데 Answer의 createAt으로 하고 있음
@Override
public SliceResponse<MemberAnswerInfo> getMemberHeartAnswer(Member member, Long lastIndex, Pageable pageable) {
lastIndex = getLastIndex(lastIndex);
Slice<Answer> answerSlice = answerRepository.findByIdInAndIdIsLessThanEqual(answerHeartRedisRepository.getMemberHeartsAnswer(member.getId()), lastIndex, pageable);
lastIndex = getLastIndexFromAnswer(lastIndex, answerSlice);
Slice<Answer> answerSlice = answerRepository.findByIdInAndIdIsLessThan(answerHeartRedisRepository.getMemberHeartsAnswer(member.getId()), lastIndex, pageable);
lastIndex = getLastIndexFromAnswer(answerSlice);
return SliceResponse.toSliceResponse(answerSlice, answerSlice.getContent().stream()
.map(answer -> answerMapper.toMemberAnswerInfo(
answer,
Expand All @@ -159,20 +156,16 @@ public Answer findAnswer(Long answerId) {
);
}

private Long getLastIndex(Long lastIndex) {
return lastIndex == null ? Long.MAX_VALUE : lastIndex;
private Long getLastIndexFromAnswerInfoInterface(Slice<AnswerInfoInterface> answerInfoSliceInterface) {
if(answerInfoSliceInterface.hasContent())
return answerInfoSliceInterface.stream().map(AnswerInfoInterface::getAnswer).map(Answer::getId).min(Long::compareTo).get();
return -1L;
}

private Long getLastIndexFromAnswerInfoInterface(Long lastIndex, Slice<AnswerInfoInterface> answerInfoSliceInterface) {
if(answerInfoSliceInterface.hasContent() && lastIndex == Long.MAX_VALUE)
return answerInfoSliceInterface.stream().map(AnswerInfoInterface::getAnswer).map(Answer::getId).max(Long::compareTo).get();
return lastIndex;
}

private Long getLastIndexFromAnswer(Long lastIndex, Slice<Answer> answerSlice) {
if (answerSlice.hasContent() && lastIndex == Long.MAX_VALUE)
return answerSlice.stream().map(Answer::getId).max(Long::compareTo).get();
return lastIndex;
private Long getLastIndexFromAnswer(Slice<Answer> answerSlice) {
if (answerSlice.hasContent())
return answerSlice.stream().map(Answer::getId).min(Long::compareTo).get();
return -1L;
}

@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,48 +49,47 @@ private BaseResponse<BoardId> updateBoard(@AuthMember Member member,
return BaseResponse.onSuccess(boardService.updateBoard(member, request.getBoardId(), request.getContent()));
}

@Operation(summary = "카테고리별 게시글 조회 with REDIS API(프론트 사용 X, 성능 테스트 용)", description = "카테고리별 게시글을 조회합니다.")
@Operation(summary = "카테고리별 게시글 조회 with REDIS API(프론트 사용 X, 성능 테스트 용)", description = "카테고리별 게시글을 조회합니다.<BR>**첫 번째 조회 시 threshold를 비워 보내고, 이후 조회 시 앞선 조회의 반환값으로 받은 threshold를 보내주세요.**")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON200", description = "성공"),
})
@GetMapping("/redis")
public BaseResponse<SliceResponse<BoardInfo>> getBoardsByBoardTypeWithRedis(
@AuthMember Member member,
@RequestParam(name = "boardType", required = false) BoardType boardType,
@Parameter(description = "Pull to Refresh 후 마지막 index")
@RequestParam(required = false) Long threshold,
@RequestParam(defaultValue = "0", required = false) Integer pageNumber, @RequestParam(defaultValue = "1000", required = false) Integer pageSize
@Parameter(description = "이전 조회의 마지막 index")
@RequestParam(required = false, name = "threshold") Long lastIndex,
@RequestParam(defaultValue = "1000", required = false) Integer pageSize
) {
return BaseResponse.onSuccess(boardService.getBoardsByBoardTypeWithRedis(member, boardType, threshold, PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
return BaseResponse.onSuccess(boardService.getBoardsByBoardTypeWithRedis(member, boardType, lastIndex, PageRequest.of(0, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
}

@Operation(summary = "카테고리별 게시글 조회", description = "카테고리별 게시글을 조회합니다.")
@Operation(summary = "카테고리별 게시글 조회", description = "카테고리별 게시글을 조회합니다.<BR>**첫 번째 조회 시 threshold를 비워 보내고, 이후 조회 시 앞선 조회의 반환값으로 받은 threshold를 보내주세요.**")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON200", description = "성공"),
})
@GetMapping()
public BaseResponse<SliceResponse<BoardInfo>> getBoardsByBoardType(
@AuthMember Member member,
@RequestParam(name = "boardType", required = false) BoardType boardType,
@Parameter(description = "Pull to Refresh 후 마지막 index")
@RequestParam(required = false) Long threshold,
@RequestParam(defaultValue = "0", required = false) Integer pageNumber, @RequestParam(defaultValue = "1000", required = false) Integer pageSize
@Parameter(description = "이전 조회의 마지막 index")
@RequestParam(required = false, name = "threshold") Long lastIndex,
@RequestParam(defaultValue = "1000", required = false) Integer pageSize
) {
return BaseResponse.onSuccess(boardService.getBoardsByBoardType(member, boardType, threshold, PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
return BaseResponse.onSuccess(boardService.getBoardsByBoardType(member, boardType, lastIndex, PageRequest.of(0, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
}

@Operation(summary = "게시글 검색 API", description = "게시글을 검색합니다. 자유게시판에서만 검색이 가능합니다.")
@Operation(summary = "게시글 검색 API", description = "게시글을 검색합니다. 자유게시판에서만 검색이 가능합니다.<BR>**첫 번째 조회 시 threshold를 비워 보내고, 이후 조회 시 앞선 조회의 반환값으로 받은 threshold를 보내주세요.**")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON200", description = "성공"),
})
@GetMapping("/search")
public BaseResponse<SliceResponse<BoardInfo>> searchBoardsByKeyword(
@AuthMember Member member, @RequestParam(name = "keyword") String keyword,
@Parameter(description = "Pull to Refresh 후 마지막 index")
@RequestParam(required = false) Long threshold,
@RequestParam(defaultValue = "0", required = false) Integer pageNumber,
@Parameter(description = "이전 조회의 마지막 index")
@RequestParam(required = false, name = "threshold") Long lastIndex,
@RequestParam(defaultValue = "1000", required = false) Integer pageSize) {
return BaseResponse.onSuccess(boardService.searchBoardsByKeyword(member, keyword, threshold, PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
return BaseResponse.onSuccess(boardService.searchBoardsByKeyword(member, keyword, lastIndex, PageRequest.of(0, pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ public interface BoardRepository extends JpaRepository<Board, Long> {
"(CASE WHEN b.writer = :member THEN TRUE ELSE FALSE END) AS isMine " +
"FROM Board b " +
"LEFT JOIN BoardHeart bh ON b = bh.board AND bh.member = :member " +
"WHERE (:boardType IS NULL OR b.boardType = :boardType) AND b.id <= :lastIndex")
Slice<BoardInfoInterface> findBoardInfosByMemberAndBoardTypeAndIdIsLessThanEqual(Member member, BoardType boardType, Long lastIndex, Pageable pageable);
"WHERE (:boardType IS NULL OR b.boardType = :boardType) AND (b.id < :lastIndex OR :lastIndex IS NULL)")
Slice<BoardInfoInterface> findBoardInfosByMemberAndBoardTypeAndIdIsLessThan(Member member, BoardType boardType, Long lastIndex, Pageable pageable);

@Query("SELECT DISTINCT b AS board, " +
"(CASE WHEN bh.isLiked = TRUE THEN TRUE ELSE FALSE END) AS isLike, " +
"(CASE WHEN b.writer = :member THEN TRUE ELSE FALSE END) AS isMine " +
"FROM Board b " +
"LEFT JOIN BoardHeart bh ON b = bh.board AND bh.member = :member " +
"WHERE b.id <= :lastIndex AND b.content LIKE %:keyword% AND b.boardType = 0") //FREETYPE = 0
Slice<BoardInfoInterface> findBoardInfosByMemberAndKeywordAndIdIsLessThanEqual(Member member, String keyword, Long lastIndex, Pageable pageable);
"WHERE (b.id < :lastIndex OR :lastIndex IS NULL) AND b.content LIKE %:keyword% AND b.boardType = 0") //FREETYPE = 0
Slice<BoardInfoInterface> findBoardInfosByMemberAndKeywordAndIdIsLessThan(Member member, String keyword, Long lastIndex, Pageable pageable);


@Query("SELECT b AS board, " +
Expand All @@ -40,8 +40,8 @@ public interface BoardRepository extends JpaRepository<Board, Long> {
@Query("SELECT DISTINCT b AS board, " +
"(CASE WHEN b.writer = :member THEN TRUE ELSE FALSE END) AS isMine " +
"FROM Board b " +
"WHERE (:boardType IS NULL OR b.boardType = :boardType) AND b.id <= :lastIndex")
Slice<BoardInfoInterface> findBoardInfosForRedisAndIdIsLessThanEqual(Member member, BoardType boardType, Long lastIndex, Pageable pageable);
"WHERE (:boardType IS NULL OR b.boardType = :boardType) AND (b.id < :lastIndex OR :lastIndex IS NULL)")
Slice<BoardInfoInterface> findBoardInfosForRedisAndIdIsLessThan(Member member, BoardType boardType, Long lastIndex, Pageable pageable);

@Query("SELECT COUNT(b) FROM Board b")
Integer getBoardCount();
Expand Down
Loading
Loading