Skip to content

Commit

Permalink
Merge pull request #528 from Travel-in-nanaland/fix/#527-searchExperi…
Browse files Browse the repository at this point in the history
…ence

[Fix] 검색 기능 수정
  • Loading branch information
heeeeeseok authored Jan 7, 2025
2 parents 110db41 + 9ee1fd7 commit e95fe5e
Show file tree
Hide file tree
Showing 12 changed files with 332 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ PopularPostPreviewDto findRandomPopularPostPreviewDtoByLanguage(Language languag

PopularPostPreviewDto findPostPreviewDtoByLanguageAndId(Language language, Long postId);

Page<ExperienceSearchDto> findSearchDtoByKeywordsUnion(List<String> keywords, Language language,
Pageable pageable);
Page<ExperienceSearchDto> findSearchDtoByKeywordsUnion(ExperienceType experienceType,
List<String> keywords, Language language, Pageable pageable);

Page<ExperienceSearchDto> findSearchDtoByKeywordsIntersect(List<String> keywords,
Language language, Pageable pageable);
Page<ExperienceSearchDto> findSearchDtoByKeywordsIntersect(ExperienceType experienceType,
List<String> keywords, Language language, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ public ExperienceCompositeDto findCompositeDtoByIdWithPessimisticLock(Long id,
}

@Override
public Page<ExperienceSearchDto> findSearchDtoByKeywordsUnion(List<String> keywords,
Language language, Pageable pageable) {
public Page<ExperienceSearchDto> findSearchDtoByKeywordsUnion(ExperienceType experienceType,
List<String> keywords, Language language, Pageable pageable) {

// experience_id를 가진 게시물의 해시태그가 검색어 키워드 중 몇개를 포함하는지 계산
List<Tuple> keywordMatchQuery = queryFactory
Expand All @@ -123,7 +123,9 @@ public Page<ExperienceSearchDto> findSearchDtoByKeywordsUnion(List<String> keywo
.on(hashtag.post.id.eq(experience.id)
.and(hashtag.language.eq(language)))
.innerJoin(hashtag.keyword, QKeyword.keyword)
.where(QKeyword.keyword.content.toLowerCase().trim().in(keywords))
.where(
experience.experienceType.eq(experienceType),
QKeyword.keyword.content.toLowerCase().trim().in(keywords))
.groupBy(experience.id)
.fetch();

Expand All @@ -146,6 +148,7 @@ public Page<ExperienceSearchDto> findSearchDtoByKeywordsUnion(List<String> keywo
.leftJoin(experience.firstImageFile, imageFile)
.leftJoin(experience.experienceTrans, experienceTrans)
.on(experienceTrans.language.eq(language))
.where(experience.experienceType.eq(experienceType))
.fetch();

// 해시태그 값을 matchedCount에 더해줌
Expand Down Expand Up @@ -176,8 +179,9 @@ public Page<ExperienceSearchDto> findSearchDtoByKeywordsUnion(List<String> keywo
}

@Override
public Page<ExperienceSearchDto> findSearchDtoByKeywordsIntersect(List<String> keywords,
Language language, Pageable pageable) {
public Page<ExperienceSearchDto> findSearchDtoByKeywordsIntersect(
ExperienceType experienceType, List<String> keywords, Language language,
Pageable pageable) {

// experience_id를 가진 게시물의 해시태그가 검색어 키워드 중 몇개를 포함하는지 계산
List<Tuple> keywordMatchQuery = queryFactory
Expand All @@ -187,7 +191,9 @@ public Page<ExperienceSearchDto> findSearchDtoByKeywordsIntersect(List<String> k
.on(hashtag.post.id.eq(experience.id)
.and(hashtag.language.eq(language)))
.innerJoin(hashtag.keyword, QKeyword.keyword)
.where(QKeyword.keyword.content.toLowerCase().trim().in(keywords))
.where(
experience.experienceType.eq(experienceType),
QKeyword.keyword.content.toLowerCase().trim().in(keywords))
.groupBy(experience.id)
.fetch();

Expand All @@ -210,6 +216,7 @@ public Page<ExperienceSearchDto> findSearchDtoByKeywordsIntersect(List<String> k
.leftJoin(experience.firstImageFile, imageFile)
.leftJoin(experience.experienceTrans, experienceTrans)
.on(experienceTrans.language.eq(language))
.where(experience.experienceType.eq(experienceType))
.fetch();

// 해시태그 값을 matchedCount에 더해줌
Expand Down Expand Up @@ -413,34 +420,6 @@ public PopularPostPreviewDto findPostPreviewDtoByLanguageAndId(Language language
.fetchOne();
}

private List<Long> getIdListContainAllHashtags(String keywords, Language language) {
return queryFactory
.select(experience.id)
.from(experience)
.leftJoin(hashtag)
.on(hashtag.post.id.eq(experience.id)
.and(hashtag.category.eq(Category.EXPERIENCE))
.and(hashtag.language.eq(language)))
.where(hashtag.keyword.content.toLowerCase().trim().in(keywords))
.groupBy(experience.id)
.having(experience.id.count().eq(splitKeyword(keywords).stream().count()))
.fetch();
}

private List<Long> getIdListContainAllHashtags(List<String> keywords, Language language) {
return queryFactory
.select(experience.id)
.from(experience)
.leftJoin(hashtag)
.on(hashtag.post.id.eq(experience.id)
.and(hashtag.category.eq(Category.EXPERIENCE))
.and(hashtag.language.eq(language)))
.where(hashtag.keyword.content.toLowerCase().trim().in(keywords))
.groupBy(experience.id)
.having(experience.id.count().eq(keywords.stream().count()))
.fetch();
}

private List<String> splitKeyword(String keyword) {
String[] tokens = keyword.split("\\s+");
List<String> tokenList = new ArrayList<>();
Expand Down Expand Up @@ -469,15 +448,42 @@ private BooleanExpression keywordCondition(List<ExperienceTypeKeyword> keywordFi
}
}

/**
* 공백 제거, 소문자화, '-'와 '_' 제거
*
* @param stringExpression 조건절 컬럼
* @return 정규화된 컬럼
*/
private StringExpression normalizeStringExpression(StringExpression stringExpression) {
return Expressions.stringTemplate(
"replace(replace({0}, '-', ''), '_', '')",
stringExpression.toLowerCase().trim());
}

/**
* 제목, 주소태그, 내용과 일치하는 키워드 개수 카운팅
*
* @param keywords 키워드
* @return 키워드를 포함하는 조건 개수
*/
private Expression<Long> countMatchingWithKeyword(List<String> keywords) {
return Expressions.asNumber(0L)
.add(countMatchingConditionWithKeyword(experienceTrans.title.toLowerCase().trim(), keywords,
0))
.add(countMatchingConditionWithKeyword(experienceTrans.addressTag.toLowerCase().trim(),
.add(countMatchingConditionWithKeyword(normalizeStringExpression(experienceTrans.title),
keywords, 0))
.add(
countMatchingConditionWithKeyword(normalizeStringExpression(experienceTrans.addressTag),
keywords, 0))
.add(countMatchingConditionWithKeyword(experienceTrans.content, keywords, 0));
}

/**
* 조건이 키워드를 포함하는지 검사
*
* @param condition 테이블 컬럼
* @param keywords 유저 키워드 리스트
* @param idx 키워드 인덱스
* @return
*/
private Expression<Integer> countMatchingConditionWithKeyword(StringExpression condition,
List<String> keywords, int idx) {
if (idx == keywords.size()) {
Expand All @@ -490,13 +496,4 @@ private Expression<Integer> countMatchingConditionWithKeyword(StringExpression c
.otherwise(0)
.add(countMatchingConditionWithKeyword(condition, keywords, idx + 1));
}

private BooleanExpression containsAllKeywords(StringExpression condition, List<String> keywords) {
BooleanExpression expression = null;
for (String keyword : keywords) {
BooleanExpression containsKeyword = condition.contains(keyword);
expression = (expression == null) ? containsKeyword : expression.and(containsKeyword);
}
return expression;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,18 @@ private BooleanExpression addressTagCondition(Language language, List<AddressTag
}
}

/**
* 공백 제거, 소문자화, '-'와 '_' 제거
*
* @param stringExpression 조건절 컬럼
* @return 정규화된 컬럼
*/
private StringExpression normalizeStringExpression(StringExpression stringExpression) {
return Expressions.stringTemplate(
"replace(replace({0}, '-', ''), '_', '')",
stringExpression.toLowerCase().trim());
}

/**
* 제목, 주소태그, 내용과 일치하는 키워드 개수 카운팅
*
Expand All @@ -542,9 +554,9 @@ private BooleanExpression addressTagCondition(Language language, List<AddressTag
*/
private Expression<Long> countMatchingWithKeyword(List<String> keywords) {
return Expressions.asNumber(0L)
.add(countMatchingConditionWithKeyword(festivalTrans.title.toLowerCase().trim(), keywords,
0))
.add(countMatchingConditionWithKeyword(festivalTrans.addressTag.toLowerCase().trim(),
.add(countMatchingConditionWithKeyword(normalizeStringExpression(festivalTrans.title),
keywords, 0))
.add(countMatchingConditionWithKeyword(normalizeStringExpression(festivalTrans.addressTag),
keywords, 0))
.add(countMatchingConditionWithKeyword(festivalTrans.content, keywords, 0));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,18 @@ private BooleanExpression addressTagCondition(Language language, List<AddressTag
}
}

/**
* 공백 제거, 소문자화, '-'와 '_' 제거
*
* @param stringExpression 조건절 컬럼
* @return 정규화된 컬럼
*/
private StringExpression normalizeStringExpression(StringExpression stringExpression) {
return Expressions.stringTemplate(
"replace(replace({0}, '-', ''), '_', '')",
stringExpression.toLowerCase().trim());
}

/**
* 제목, 주소태그, 내용과 일치하는 키워드 개수 카운팅
*
Expand All @@ -395,9 +407,9 @@ private BooleanExpression addressTagCondition(Language language, List<AddressTag
*/
private Expression<Long> countMatchingWithKeyword(List<String> keywords) {
return Expressions.asNumber(0L)
.add(countMatchingConditionWithKeyword(marketTrans.title.toLowerCase().trim(), keywords,
0))
.add(countMatchingConditionWithKeyword(marketTrans.addressTag.toLowerCase().trim(),
.add(countMatchingConditionWithKeyword(normalizeStringExpression(marketTrans.title),
keywords, 0))
.add(countMatchingConditionWithKeyword(normalizeStringExpression(marketTrans.addressTag),
keywords, 0))
.add(countMatchingConditionWithKeyword(marketTrans.content, keywords, 0));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,18 @@ private List<String> splitKeyword(String keyword) {
return tokenList;
}

/**
* 공백 제거, 소문자화, '-'와 '_' 제거
*
* @param stringExpression 조건절 컬럼
* @return 정규화된 컬럼
*/
private StringExpression normalizeStringExpression(StringExpression stringExpression) {
return Expressions.stringTemplate(
"replace(replace({0}, '-', ''), '_', '')",
stringExpression.toLowerCase().trim());
}

/**
* 제목, 주소태그, 내용과 일치하는 키워드 개수 카운팅
*
Expand All @@ -393,8 +405,8 @@ private List<String> splitKeyword(String keyword) {
*/
private Expression<Long> getMaxMatchingCountWithKeyword(List<String> keywords) {
return Expressions.asNumber(0L)
.add(countMatchingConditionWithKeyword(nanaTitle.heading.toLowerCase().trim(), keywords,
0))
.add(countMatchingConditionWithKeyword(normalizeStringExpression(nanaTitle.heading),
keywords, 0))
.add(countMatchingConditionWithKeyword(nanaContent.content, keywords, 0))
.max();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,18 @@ private List<String> splitKeyword(String keyword) {
return tokenList;
}

/**
* 공백 제거, 소문자화, '-'와 '_' 제거
*
* @param stringExpression 조건절 컬럼
* @return 정규화된 컬럼
*/
private StringExpression normalizeStringExpression(StringExpression stringExpression) {
return Expressions.stringTemplate(
"replace(replace({0}, '-', ''), '_', '')",
stringExpression.toLowerCase().trim());
}

/**
* 제목, 주소태그, 내용과 일치하는 키워드 개수 카운팅
*
Expand All @@ -447,9 +459,9 @@ private List<String> splitKeyword(String keyword) {
*/
private Expression<Long> countMatchingWithKeyword(List<String> keywords) {
return Expressions.asNumber(0L)
.add(countMatchingConditionWithKeyword(natureTrans.title.toLowerCase().trim(), keywords,
0))
.add(countMatchingConditionWithKeyword(natureTrans.addressTag.toLowerCase().trim(),
.add(countMatchingConditionWithKeyword(normalizeStringExpression(natureTrans.title),
keywords, 0))
.add(countMatchingConditionWithKeyword(normalizeStringExpression(natureTrans.addressTag),
keywords, 0))
.add(countMatchingConditionWithKeyword(natureTrans.content, keywords, 0));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,18 @@ public List<Long> findAllIds() {
.fetch();
}

/**
* 공백 제거, 소문자화, '-'와 '_' 제거
*
* @param stringExpression 조건절 컬럼
* @return 정규화된 컬럼
*/
private StringExpression normalizeStringExpression(StringExpression stringExpression) {
return Expressions.stringTemplate(
"replace(replace({0}, '-', ''), '_', '')",
stringExpression.toLowerCase().trim());
}

/**
* 제목, 주소태그, 내용과 일치하는 키워드 개수 카운팅
*
Expand All @@ -502,10 +514,11 @@ public List<Long> findAllIds() {
*/
private Expression<Long> countMatchingWithKeyword(List<String> keywords) {
return Expressions.asNumber(0L)
.add(countMatchingConditionWithKeyword(restaurantTrans.title.toLowerCase().trim(), keywords,
0))
.add(countMatchingConditionWithKeyword(restaurantTrans.addressTag.toLowerCase().trim(),
.add(countMatchingConditionWithKeyword(normalizeStringExpression(restaurantTrans.title),
keywords, 0))
.add(
countMatchingConditionWithKeyword(normalizeStringExpression(restaurantTrans.addressTag),
keywords, 0))
.add(countMatchingConditionWithKeyword(restaurantTrans.content, keywords, 0));
}

Expand All @@ -515,7 +528,7 @@ private Expression<Long> countMatchingWithKeyword(List<String> keywords) {
* @param condition 테이블 컬럼
* @param keywords 유저 키워드 리스트
* @param idx 키워드 인덱스
* @return
* @return 매칭된 수
*/
private Expression<Integer> countMatchingConditionWithKeyword(StringExpression condition,
List<String> keywords, int idx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static com.jeju.nanaland.global.exception.SuccessCode.SEARCH_SUCCESS;

import com.jeju.nanaland.domain.experience.entity.enums.ExperienceType;
import com.jeju.nanaland.domain.member.dto.MemberResponse.MemberInfoDto;
import com.jeju.nanaland.domain.search.dto.SearchResponse;
import com.jeju.nanaland.domain.search.dto.SearchResponse.AllCategoryDto;
Expand Down Expand Up @@ -95,12 +96,13 @@ public BaseResponse<SearchResponse.ResultDto> searchFestival(
@GetMapping("/experience")
public BaseResponse<SearchResponse.ResultDto> searchExperience(
@AuthMember MemberInfoDto memberInfoDto,
@RequestParam ExperienceType experienceType,
@NotNull String keyword,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "12") int size) {

ResultDto result = searchService.searchExperience(memberInfoDto, keyword, page,
size);
ResultDto result = searchService.searchExperience(memberInfoDto, experienceType, keyword,
page, size);
return BaseResponse.success(SEARCH_SUCCESS, result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ public static class AllCategoryDto {
@Schema(description = "7대자연 조회 결과")
private ResultDto nature;

@Schema(description = "이색체험 조회 결과")
private ResultDto experience;
@Schema(description = "액티비티 조회 결과")
private ResultDto activity;

@Schema(description = "문화예술 조회 결과")
private ResultDto cultureAndArts;

@Schema(description = "전통시장 조회 결과")
private ResultDto market;
Expand Down
Loading

0 comments on commit e95fe5e

Please sign in to comment.