Skip to content

Commit

Permalink
[refac] 검색 리팩토링 및 최근 검색어 삭제 기능 추가 (#62)
Browse files Browse the repository at this point in the history
* [feat] 최근 검색어 삭제 기능 추가 #59

* [refac] 최근 검색어 불러오기 response 변경 #59

* [chore] spotless 적용 #61

* [refac] 검색어 검색 http method 변경 #61

* [feat] 강제 데이터 주입 기능 추가 #61

* [refac] 연관 검색어 기능 로직 수정 및 코드 정리 #61

* [refac] 연관 검색어 range 수정 #61

* [refac] 점색어 자동완성 get 으로 변환 #61
  • Loading branch information
wjdtkdgns authored Jul 26, 2023
1 parent d1f7327 commit bed7795
Show file tree
Hide file tree
Showing 16 changed files with 205 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
import allchive.server.api.search.model.dto.request.SearchRequest;
import allchive.server.api.search.model.dto.response.SearchListResponse;
import allchive.server.api.search.model.dto.response.SearchResponse;
import allchive.server.api.search.model.dto.response.SearchVoListResponse;
import allchive.server.api.search.model.enums.ArchivingType;
import allchive.server.api.search.service.GetLatestSearchListUseCase;
import allchive.server.api.search.service.GetRelativeSearchListUseCase;
import allchive.server.api.search.service.SearchArchivingUseCase;
import allchive.server.api.search.service.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
Expand All @@ -26,25 +25,39 @@ public class SearchController {
private final SearchArchivingUseCase searchArchivingUseCase;
private final GetLatestSearchListUseCase getLatestSearchListUseCase;
private final GetRelativeSearchListUseCase getRelativeSearchListUseCase;
private final DeleteLatestSearchUseCase deleteLatestSearchUseCase;
private final RenewalTitleDataUseCase renewalTitleDataUseCase;

@Operation(summary = "검색어를 검색합니다.")
@PostMapping
@GetMapping
public SearchResponse searchArchiving(
@ParameterObject @PageableDefault(size = 10) Pageable pageable,
@RequestParam("type") ArchivingType type,
@RequestBody SearchRequest request) {
return searchArchivingUseCase.execute(pageable, type, request);
@RequestParam("word") String word) {
return searchArchivingUseCase.execute(pageable, type, word);
}

@Operation(summary = "최근 검색어 목록을 가져옵니다.", description = "5개만 드릴게요")
@GetMapping(value = "/latest")
public SearchListResponse getLatestSearchList() {
public SearchVoListResponse getLatestSearchList() {
return getLatestSearchListUseCase.execute();
}

@Operation(summary = "최근 검색어를 삭제합니다.")
@DeleteMapping(value = "/latest/{latestId}")
public void deleteLatestSearch(@PathVariable("latestId") Long latestId) {
deleteLatestSearchUseCase.execute(latestId);
}

@Operation(summary = "검색어 자동 완성")
@PostMapping(value = "/relation")
public SearchListResponse getRelativeSearchList(@RequestBody SearchRequest request) {
return getRelativeSearchListUseCase.execute(request);
@GetMapping(value = "/relation")
public SearchListResponse getRelativeSearchList(@RequestParam("word") String word) {
return getRelativeSearchListUseCase.execute(word);
}

@Operation(summary = "자동 완성 데이터 강제 리뉴얼", deprecated = true)
@GetMapping(value = "/relation/force")
public void forceRenewalTitleData() {
renewalTitleDataUseCase.executeForce();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package allchive.server.api.search.model.dto.response;


import allchive.server.api.search.model.vo.LatestSearchVo;
import java.util.List;
import lombok.Builder;
import lombok.Getter;

@Getter
public class SearchVoListResponse {
private List<LatestSearchVo> keywords;

@Builder
private SearchVoListResponse(List<LatestSearchVo> keywords) {
this.keywords = keywords;
}

public static SearchVoListResponse from(List<LatestSearchVo> keywords) {
return SearchVoListResponse.builder().keywords(keywords).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package allchive.server.api.search.model.vo;


import allchive.server.domain.domains.search.domain.LatestSearch;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Getter;

@Getter
public class LatestSearchVo {
@Schema(description = "최근 검색 내용")
private String word;

@Schema(description = "최근 검색 내용 고유번호")
private Long latestSearchId;

@Builder
private LatestSearchVo(String word, Long latestSearchId) {
this.word = word;
this.latestSearchId = latestSearchId;
}

public static LatestSearchVo from(LatestSearch latestSearch) {
return LatestSearchVo.builder()
.word(latestSearch.getKeyword())
.latestSearchId(latestSearch.getId())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package allchive.server.api.search.service;


import allchive.server.api.config.security.SecurityUtil;
import allchive.server.core.annotation.UseCase;
import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor;
import allchive.server.domain.domains.search.validator.LatestSearchValidator;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

@UseCase
@RequiredArgsConstructor
public class DeleteLatestSearchUseCase {
private final LatestSearchValidator latestSearchValidator;
private final LatestSearchAdaptor latestSearchAdaptor;

@Transactional
public void execute(Long latestSearchId) {
Long userId = SecurityUtil.getCurrentUserId();
validateExecution(latestSearchId, userId);
latestSearchAdaptor.deleteByIdAndUserId(latestSearchId, userId);
}

private void validateExecution(Long latestSearchId, Long userId) {
latestSearchValidator.validateExistByIdAndUserId(latestSearchId, userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@


import allchive.server.api.config.security.SecurityUtil;
import allchive.server.api.search.model.dto.response.SearchListResponse;
import allchive.server.api.search.model.dto.response.SearchVoListResponse;
import allchive.server.api.search.model.vo.LatestSearchVo;
import allchive.server.core.annotation.UseCase;
import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor;
import allchive.server.domain.domains.search.domain.LatestSearch;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -16,12 +16,12 @@ public class GetLatestSearchListUseCase {
private final LatestSearchAdaptor latestSearchAdaptor;

@Transactional(readOnly = true)
public SearchListResponse execute() {
public SearchVoListResponse execute() {
Long userId = SecurityUtil.getCurrentUserId();
List<String> keywords =
List<LatestSearchVo> keywords =
latestSearchAdaptor.findAllByUserIdOrderByCreatedAt(userId).stream()
.map(LatestSearch::getKeyword)
.map(LatestSearchVo::from)
.toList();
return SearchListResponse.from(keywords);
return SearchVoListResponse.from(keywords);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ public class GetRelativeSearchListUseCase {
private final RedisTemplate<String, String> redisTemplate;

@Transactional
public SearchListResponse execute(SearchRequest request) {
public SearchListResponse execute(String word) {
ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
List<String> autoCompleteList = new ArrayList<>();
Long rank = zSetOperations.rank(SEARCH_KEY, request.getKeyword());
Long rank = zSetOperations.rank(SEARCH_KEY, word);
if (rank != null) {
Set<String> rangeList = zSetOperations.range(SEARCH_KEY, rank, rank + 1000);
autoCompleteList = getAutoCompleteList(rangeList, request.getKeyword());
autoCompleteList = getAutoCompleteList(rangeList, word);
}
return SearchListResponse.from(autoCompleteList);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@ public class RenewalTitleDataUseCase {
private final ArchivingAdaptor archivingAdaptor;
private final RedisTemplate<String, String> redisTemplate;

private static final int TIME_LIMIT = 1;

@Scheduled(cron = "0 0 3 * * *")
@Transactional(readOnly = true)
public void executeSchedule() {
log.info("renewal title scheduler on");
renewalData();
log.info("renewal title scheduler off");
}

@Transactional(readOnly = true)
public void executeForce() {
renewalData();
}

private void renewalData() {
redisTemplate.delete(SEARCH_KEY);
Set<Archiving> archivings =
new HashSet<>(archivingAdaptor.findAllByPublicStatus(Boolean.TRUE));
Expand All @@ -35,15 +43,14 @@ public void executeSchedule() {
redisTemplate
.opsForZSet()
.add(SEARCH_KEY, archiving.getTitle().trim() + ASTERISK, 0);
for (int index = 1; index < archiving.getTitle().length(); index++) {
for (int index = 0; index <= archiving.getTitle().length(); index++) {
redisTemplate
.opsForZSet()
.add(
SEARCH_KEY,
archiving.getTitle().trim().substring(0, index - 1),
archiving.getTitle().trim().substring(0, index),
0);
}
});
log.info("renewal title scheduler off");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import allchive.server.api.archiving.model.dto.response.ArchivingResponse;
import allchive.server.api.common.slice.SliceResponse;
import allchive.server.api.config.security.SecurityUtil;
import allchive.server.api.search.model.dto.request.SearchRequest;
import allchive.server.api.search.model.dto.response.SearchResponse;
import allchive.server.api.search.model.enums.ArchivingType;
import allchive.server.core.annotation.UseCase;
Expand Down Expand Up @@ -32,23 +31,23 @@ public class SearchArchivingUseCase {
private final LatestSearchDomainService latestSearchDomainService;

@Transactional
public SearchResponse execute(Pageable pageable, ArchivingType type, SearchRequest request) {
public SearchResponse execute(Pageable pageable, ArchivingType type, String word) {
Long userId = SecurityUtil.getCurrentUserId();
SliceResponse<ArchivingResponse> my = null;
SliceResponse<ArchivingResponse> community = null;
renewalLatestSearch(userId, request.getKeyword());
renewalLatestSearch(userId, word);
switch (type) {
case ALL -> {
my = getMyArchivings(userId, request.getKeyword(), pageable);
community = getCommunityArchivings(userId, request.getKeyword(), pageable);
my = getMyArchivings(userId, word, pageable);
community = getCommunityArchivings(userId, word, pageable);
return SearchResponse.forAll(my, community);
}
case MY -> {
my = getMyArchivings(userId, request.getKeyword(), pageable);
my = getMyArchivings(userId, word, pageable);
return SearchResponse.forMy(my);
}
case COMMUNITY -> {
community = getCommunityArchivings(userId, request.getKeyword(), pageable);
community = getCommunityArchivings(userId, word, pageable);
return SearchResponse.forCommunity(community);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,17 @@ public class GetUserProfileResponse {
@Schema(defaultValue = "0", description = "공개 아카이브 개수")
private int publicArchivingCount;


@Schema(defaultValue = "0", description = "모든 아카이브 개수")
private int archivingCount;

@Builder
public GetUserProfileResponse(String nickname, String imgUrl, int linkCount, int imgCount, int publicArchivingCount, int archivingCount) {
public GetUserProfileResponse(
String nickname,
String imgUrl,
int linkCount,
int imgCount,
int publicArchivingCount,
int archivingCount) {
this.nickname = nickname;
this.imgUrl = imgUrl;
this.linkCount = linkCount;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public GetUserProfileResponse toGetUserProfileResponse(
imgCount += archiving.getImgCnt();
publicArchivingCount += archiving.getPublicStatus() ? 1 : 0;
}
return GetUserProfileResponse.of(user, linkCount, imgCount, publicArchivingCount, archivingList.size());
return GetUserProfileResponse.of(
user, linkCount, imgCount, publicArchivingCount, archivingList.size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor;
import allchive.server.domain.domains.archiving.domain.Archiving;
import allchive.server.domain.domains.archiving.domain.enums.Category;
import java.util.List;

import allchive.server.domain.domains.content.domain.enums.ContentType;
import java.util.List;
import lombok.RequiredArgsConstructor;

@DomainService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import allchive.server.core.annotation.Adaptor;
import allchive.server.domain.domains.search.domain.LatestSearch;
import allchive.server.domain.domains.search.exception.exceptions.LatestSearchNotFoundException;
import allchive.server.domain.domains.search.repository.LatestSearchRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,4 +28,14 @@ public void save(LatestSearch newSearch) {
public void deleteAllByUserId(Long userId) {
latestSearchRepository.deleteAllByUserId(userId);
}

public LatestSearch findByIdAndUserId(Long latestSearchId, Long userId) {
return latestSearchRepository
.findByIdAndUserId(latestSearchId, userId)
.orElseThrow(() -> LatestSearchNotFoundException.EXCEPTION);
}

public void deleteByIdAndUserId(Long latestSearchId, Long userId) {
latestSearchRepository.deleteByIdAndUserId(latestSearchId, userId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package allchive.server.domain.domains.search.exception;

import static allchive.server.core.consts.AllchiveConst.*;

import allchive.server.core.dto.ErrorReason;
import allchive.server.core.error.BaseErrorCode;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum SearchErrorCode implements BaseErrorCode {
LATEST_SEARCH_NOT_FOUND(NOT_FOUND, "LATESTSEARCH_404_1", "최근 검색어를 찾을 수 없습니다.");
private int status;
private String code;
private String reason;

@Override
public ErrorReason getErrorReason() {
return ErrorReason.of(status, code, reason);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package allchive.server.domain.domains.search.exception.exceptions;


import allchive.server.core.error.BaseErrorException;
import allchive.server.domain.domains.search.exception.SearchErrorCode;

public class LatestSearchNotFoundException extends BaseErrorException {

public static final BaseErrorException EXCEPTION = new LatestSearchNotFoundException();

private LatestSearchNotFoundException() {
super(SearchErrorCode.LATEST_SEARCH_NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@

import allchive.server.domain.domains.search.domain.LatestSearch;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface LatestSearchRepository extends JpaRepository<LatestSearch, Long> {
List<LatestSearch> findAllByUserIdOrderByCreatedAt(Long userId);

void deleteAllByUserId(Long userId);

Optional<LatestSearch> findByIdAndUserId(Long latestSearchId, Long userId);

void deleteByIdAndUserId(Long latestSearchId, Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package allchive.server.domain.domains.search.validator;


import allchive.server.core.annotation.Validator;
import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor;
import lombok.RequiredArgsConstructor;

@Validator
@RequiredArgsConstructor
public class LatestSearchValidator {
private final LatestSearchAdaptor latestSearchAdaptor;

public void validateExistByIdAndUserId(Long latestSearchId, Long userId) {
latestSearchAdaptor.findByIdAndUserId(latestSearchId, userId);
}
}

0 comments on commit bed7795

Please sign in to comment.