From 4882f8dd58f64e9f1361b565abbfa0ba093d2359 Mon Sep 17 00:00:00 2001 From: wjdtkdgns Date: Sun, 12 May 2024 18:13:22 +0900 Subject: [PATCH] =?UTF-8?q?[refac]=20=EC=97=B0=EA=B4=80=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=96=B4=20=EB=A6=AC=EB=89=B4=EC=96=BC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/common/util/StringParamUtil.java | 14 -- .../server/api/common/util/StringUtil.java | 30 +++ .../service/GetRelativeSearchListUseCase.java | 4 +- .../service/RenewalSearchDataUseCase.java | 182 +++++++++++++++--- .../service/SearchArchivingUseCase.java | 4 +- .../server/core/consts/AllchiveConst.java | 13 +- .../archiving/adaptor/ArchivingAdaptor.java | 5 + .../repository/ArchivingCustomRepository.java | 2 + .../ArchivingCustomRepositoryImpl.java | 18 ++ .../domains/content/adaptor/TagAdaptor.java | 6 + .../repository/TagCustomRepository.java | 4 + .../repository/TagCustomRepositoryImpl.java | 14 ++ .../src/main/resources/application-domain.yml | 2 +- 13 files changed, 246 insertions(+), 52 deletions(-) delete mode 100644 Api/src/main/java/allchive/server/api/common/util/StringParamUtil.java create mode 100644 Api/src/main/java/allchive/server/api/common/util/StringUtil.java diff --git a/Api/src/main/java/allchive/server/api/common/util/StringParamUtil.java b/Api/src/main/java/allchive/server/api/common/util/StringParamUtil.java deleted file mode 100644 index f4203056..00000000 --- a/Api/src/main/java/allchive/server/api/common/util/StringParamUtil.java +++ /dev/null @@ -1,14 +0,0 @@ -package allchive.server.api.common.util; - - -import allchive.server.core.error.exception.EmptyParamValueException; -import org.springframework.stereotype.Component; - -@Component -public class StringParamUtil { - public static void checkEmptyString(String param) { - if (param.equals("")) { - throw EmptyParamValueException.EXCEPTION; - } - } -} diff --git a/Api/src/main/java/allchive/server/api/common/util/StringUtil.java b/Api/src/main/java/allchive/server/api/common/util/StringUtil.java new file mode 100644 index 00000000..d44056d2 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/common/util/StringUtil.java @@ -0,0 +1,30 @@ +package allchive.server.api.common.util; + + +import allchive.server.core.error.exception.EmptyParamValueException; +import org.springframework.stereotype.Component; + +import static allchive.server.core.consts.AllchiveConst.HANGUL_BASE; +import static allchive.server.core.consts.AllchiveConst.KOREAN_ALPHA; + +@Component +public class StringUtil { + public static void checkEmptyString(String param) { + if (param.equals("")) { + throw EmptyParamValueException.EXCEPTION; + } + } + + public static char extractKoreanInitial(char ch) { + int index = (ch - HANGUL_BASE) / (21 * 28); // 21개의 중성, 28개의 종성 + return KOREAN_ALPHA[index]; + } + + public static boolean isEnglish(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); + } + + public static boolean isKorean(char ch) { + return (ch >= '\uAC00' && ch <= '\uD7A3'); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java b/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java index fbae0ea6..5ab717e9 100644 --- a/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java +++ b/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java @@ -3,7 +3,7 @@ import static allchive.server.core.consts.AllchiveConst.ASTERISK; import static allchive.server.core.consts.AllchiveConst.SEARCH_KEY; -import allchive.server.api.common.util.StringParamUtil; +import allchive.server.api.common.util.StringUtil; import allchive.server.api.search.model.dto.response.SearchListResponse; import allchive.server.core.annotation.UseCase; import java.util.ArrayList; @@ -33,7 +33,7 @@ public SearchListResponse execute(String word) { } private void validateExecution(String word) { - StringParamUtil.checkEmptyString(word); + StringUtil.checkEmptyString(word); } private List getAutoCompleteList(Set rangeList, String keyword) { diff --git a/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java b/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java index 64dc256e..9a327d39 100644 --- a/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java +++ b/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java @@ -1,17 +1,21 @@ package allchive.server.api.search.service; -import static allchive.server.core.consts.AllchiveConst.ASTERISK; -import static allchive.server.core.consts.AllchiveConst.SEARCH_KEY; +import static allchive.server.core.consts.AllchiveConst.*; +import allchive.server.api.common.util.StringUtil; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Tag; -import java.util.HashSet; -import java.util.Set; + +import java.util.*; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.transaction.annotation.Transactional; @@ -38,40 +42,156 @@ public void executeForce() { } private void renewalData() { - redisTemplate.delete(SEARCH_KEY); + long startTime = System.currentTimeMillis(); + delete(); renewalArchiving(); renewalTag(); + long stopTime = System.currentTimeMillis(); + log.info("{}", stopTime - startTime); } - private void renewalTag() { - Set tags = new HashSet<>(tagAdaptor.findAll()); - tags.forEach( - tag -> { - redisTemplate.opsForZSet().add(SEARCH_KEY, tag.getName().trim() + ASTERISK, 0); - for (int index = 0; index <= tag.getName().length(); index++) { - redisTemplate - .opsForZSet() - .add(SEARCH_KEY, tag.getName().trim().substring(0, index), 0); + private void delete() { + redisTemplate.executePipelined((RedisCallback) + redisConnection -> { + for (char c = 'A'; c <= 'Z'; c++) { + String key = SEARCH_KEY + c; + redisConnection.del(key.getBytes()); + } + + for (int index = 0; index < 19; index++) { + String key = SEARCH_KEY + KOREAN_ALPHA[index]; + redisConnection.del(key.getBytes()); + } + + for (int index = 0; index < 9; index++) { + String key = SEARCH_KEY + index; + redisConnection.del(key.getBytes()); } + + redisConnection.del((SEARCH_KEY + '?').getBytes()); + return null; }); } + private void renewalTag() { + int pageNum = 0; + while (true) { + PageRequest pageRequest = PageRequest.of(pageNum, BULK_SIZE); + Slice slicedTags = tagAdaptor.querySliceTag(pageRequest); + Set tags = new HashSet<>(slicedTags.getContent()); + redisTemplate.executePipelined( + (RedisCallback) + redisConnection -> { + tags.forEach( + tag -> { + String key; + if (StringUtil.isKorean( + tag.getName().trim().charAt(0))) { + key = + SEARCH_KEY + + StringUtil.extractKoreanInitial( + tag.getName() + .trim() + .charAt(0)); + } else { + key = + SEARCH_KEY + + tag.getName() + .trim() + .toUpperCase() + .charAt(0); + } + redisConnection + .zSetCommands() + .zAdd( + key.getBytes(), + 0, + (tag.getName().trim() + ASTERISK) + .getBytes()); + for (int index = 0; + index <= tag.getName().length(); + index++) { + redisConnection + .zSetCommands() + .zAdd( + key.getBytes(), + 0, + tag.getName() + .trim() + .substring(0, index) + .getBytes()); + } + }); + return null; + }); + + if (!slicedTags.hasNext()) { + break; + } + pageNum++; + } + } + private void renewalArchiving() { - Set archivings = - new HashSet<>(archivingAdaptor.findAllByPublicStatus(Boolean.TRUE)); - archivings.forEach( - archiving -> { - redisTemplate - .opsForZSet() - .add(SEARCH_KEY, archiving.getTitle().trim() + ASTERISK, 0); - for (int index = 0; index <= archiving.getTitle().length(); index++) { - redisTemplate - .opsForZSet() - .add( - SEARCH_KEY, - archiving.getTitle().trim().substring(0, index), - 0); - } - }); + int pageNum = 0; + while (true) { + PageRequest pageRequest = PageRequest.of(pageNum, BULK_SIZE); + Slice slicedArchivings = + archivingAdaptor.querySliceArchivingByPublicStatus(pageRequest, true); + Set archivings = new HashSet<>(slicedArchivings.getContent()); + redisTemplate.executePipelined( + (RedisCallback) + redisConnection -> { + archivings.forEach( + archiving -> { + String key; + if (StringUtil.isKorean( + archiving.getTitle().trim().charAt(0))) { + key = + SEARCH_KEY + + StringUtil.extractKoreanInitial( + archiving + .getTitle() + .trim() + .charAt(0)); + } else { + key = + SEARCH_KEY + + archiving + .getTitle() + .trim() + .toUpperCase() + .charAt(0); + } + redisConnection + .zSetCommands() + .zAdd( + key.getBytes(), + 0, + (archiving.getTitle().trim() + ASTERISK) + .getBytes()); + for (int index = 0; + index <= archiving.getTitle().length(); + index++) { + redisConnection + .zSetCommands() + .zAdd( + key.getBytes(), + 0, + archiving + .getTitle() + .trim() + .substring(0, index) + .getBytes()); + } + }); + return null; + }); + + if (!slicedArchivings.hasNext()) { + break; + } + pageNum++; + } } -} +} \ No newline at end of file diff --git a/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java b/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java index 168ca036..c78320e9 100644 --- a/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java @@ -3,7 +3,7 @@ import allchive.server.api.archiving.model.dto.response.ArchivingResponse; import allchive.server.api.common.page.PageResponse; -import allchive.server.api.common.util.StringParamUtil; +import allchive.server.api.common.util.StringUtil; import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.search.model.dto.response.SearchResponse; import allchive.server.api.search.model.enums.ArchivingType; @@ -65,7 +65,7 @@ public SearchResponse execute(Pageable pageable, ArchivingType type, String word } private void validateExecution(String word) { - StringParamUtil.checkEmptyString(word); + StringUtil.checkEmptyString(word); } private Set getTagArchivingIds(Long userId, String word) { diff --git a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java index ee91f75a..55ce8ab3 100644 --- a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java +++ b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java @@ -1,5 +1,7 @@ package allchive.server.core.consts; +import java.util.List; + public class AllchiveConst { public static final String AUTH_HEADER = "Authorization"; public static final String BEARER = "Bearer "; @@ -35,11 +37,18 @@ public class AllchiveConst { "https://all-chive-bucket.s3.ap-northeast-2.amazonaws.com/"; public static final String[] SwaggerPatterns = { - "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs", + "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs", }; - public static final String SEARCH_KEY = "ARCHIVING_TITLE"; + public static final String SEARCH_KEY = "SEARCH_KEY_"; public static final String ASTERISK = "*"; + public static final int BULK_SIZE = 100; + public static final char[] KOREAN_ALPHA = { + 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', + 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' + }; + // 한글 유니코드에서 '가'의 코드 포인트 + public static final int HANGUL_BASE = 0xAC00; public static final int PLUS_ONE = 1; public static final int MINUS_ONE = -1; diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java index 53705302..cf417d78 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java @@ -104,4 +104,9 @@ public List queryArchivingOrderByScrapCntLimit5ExceptBlockList( List blockList) { return archivingRepository.queryArchivingOrderByScrapCntLimit5ExceptBlockList(blockList); } + + public Slice querySliceArchivingByPublicStatus( + Pageable pageable, Boolean publicStatus) { + return archivingRepository.querySliceArchivingByPublicStatus(pageable, publicStatus); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java index 68e33120..40712e01 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java @@ -34,4 +34,6 @@ Page querySliceArchivingByKeywordExceptBlockOrderByTagArchvingIds( Long userId); List queryArchivingOrderByScrapCntLimit5ExceptBlockList(List blockList); + + Slice querySliceArchivingByPublicStatus(Pageable pageable, Boolean publicStatus); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java index 58a1bb47..1e8c6beb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java @@ -160,6 +160,20 @@ public List queryArchivingOrderByScrapCntLimit5ExceptBlockList( .fetch(); } + @Override + public Slice querySliceArchivingByPublicStatus( + Pageable pageable, Boolean publicStatus) { + List archivings = + queryFactory + .select(archiving) + .from(archiving) + .where(publicStatusEq(publicStatus), deleteStatusFalse()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + PLUS_ONE) + .fetch(); + return SliceUtil.toSlice(archivings, pageable); + } + private BooleanExpression userIdNotIn(List blockList) { return archiving.userId.notIn(blockList); } @@ -168,6 +182,10 @@ private BooleanExpression publicStatusTrue() { return archiving.publicStatus.eq(Boolean.TRUE); } + private BooleanExpression publicStatusEq(Boolean publicStatus) { + return archiving.publicStatus.eq(publicStatus); + } + private BooleanExpression categoryEq(Category category) { if (category.equals(Category.ALL)) { return null; diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java index 0b65c5be..d8270235 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java @@ -7,6 +7,8 @@ import allchive.server.domain.domains.content.repository.TagRepository; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; @Adaptor @RequiredArgsConstructor @@ -52,4 +54,8 @@ public List queryTagByUserIdContainName(Long userId, String word) { public List findAll() { return tagRepository.findAll(); } + + public Slice querySliceTag(Pageable pageable) { + return tagRepository.querySliceTag(pageable); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java index 8975dcee..f46b53c3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java @@ -3,6 +3,8 @@ import allchive.server.domain.domains.content.domain.Tag; import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; public interface TagCustomRepository { List queryTagByUserIdOrderByUsedAt(Long userId); @@ -10,4 +12,6 @@ public interface TagCustomRepository { List queryTagByTagIdIn(List tagIds); List queryTagByUserIdContainName(Long userId, String name); + + Slice querySliceTag(Pageable pageable); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java index a8f5b9ad..37aff9d7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java @@ -2,6 +2,7 @@ import static allchive.server.domain.domains.content.domain.QTag.tag; +import allchive.server.domain.common.util.SliceUtil; import allchive.server.domain.domains.content.domain.Tag; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; @@ -9,6 +10,8 @@ import java.time.LocalDateTime; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; @RequiredArgsConstructor public class TagCustomRepositoryImpl implements TagCustomRepository { @@ -33,6 +36,17 @@ public List queryTagByUserIdContainName(Long userId, String name) { return queryFactory.selectFrom(tag).where(userIdEq(userId), titleContain(name)).fetch(); } + @Override + public Slice querySliceTag(Pageable pageable) { + List tags = + queryFactory + .selectFrom(tag) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(tags, pageable); + } + private BooleanExpression tagUserIdEq(Long userId) { return tag.userId.eq(userId); } diff --git a/Domain/src/main/resources/application-domain.yml b/Domain/src/main/resources/application-domain.yml index 8335a2e4..0727ce71 100644 --- a/Domain/src/main/resources/application-domain.yml +++ b/Domain/src/main/resources/application-domain.yml @@ -21,7 +21,7 @@ spring: format_sql: true default_batch_fetch_size: 100 hibernate: - ddl-auto: update + ddl-auto: none defer-datasource-initialization: true open-in-view: false database-platform: org.hibernate.dialect.MySQL5InnoDBDialect