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] 검색 기능 수정 #528

Merged
merged 21 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
51bd9a7
[#495] feat: s3 url 가져오기
jyajoo Nov 8, 2024
8be0f14
[#495] feat: 리뷰 생성 시 리뷰이미지 저장 수정
jyajoo Nov 8, 2024
88b4351
[#495] feat: 리뷰 수정 시 리뷰이미지 저장 및 삭제 기능 수정
jyajoo Nov 8, 2024
166176d
[#495] del: multipart form 설정 삭제
jyajoo Nov 8, 2024
27500d9
Merge branch 'develop' into feat/#495-review
jyajoo Nov 18, 2024
e7d6cd1
[#495] feat: 이미지 리사이징 값 추가
jyajoo Nov 27, 2024
1c69d07
[#495] feat: application/json 형태로 수정
jyajoo Nov 27, 2024
5d9843c
[#527] fix: 이색체험 검색 타입 별로 분리
heeeeeseok Dec 1, 2024
373319c
Merge pull request #522 from Travel-in-nanaland/feat/#495-review
jyajoo Dec 1, 2024
c416619
[#495] feat: 정보수정제안, 신고하기 이미지 처리 수정
jyajoo Dec 4, 2024
50ca29c
[#495] feat: 파일키 유효성 확인
jyajoo Dec 5, 2024
f5cd78d
[#495] test: report 테스트 수정
jyajoo Dec 5, 2024
ab116fc
[#495] feat: column명 수정
jyajoo Dec 5, 2024
d1257a5
Merge pull request #529 from Travel-in-nanaland/feat/#495-report
jyajoo Dec 5, 2024
7adc953
[#495] feat: 회원 가입 수정
jyajoo Dec 9, 2024
71255c1
[#495] feat: valid 설정 추가
jyajoo Dec 9, 2024
767b1d3
Merge pull request #530 from Travel-in-nanaland/feat/#495-join
jyajoo Dec 11, 2024
d50a058
Merge branch 'fix/#527-searchExperience' of https://github.com/Travel…
heeeeeseok Dec 16, 2024
d9e0c5b
[#527] fix: 검색 키워드 조합 추가
heeeeeseok Dec 16, 2024
a88c89d
[#527] fix: 검색 키워드 조합 추가
heeeeeseok Dec 16, 2024
9ee1fd7
[#527] fix: searchServiceTest 수정
heeeeeseok Dec 16, 2024
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 @@ -8,6 +8,8 @@
import com.jeju.nanaland.domain.common.repository.ImageFileRepository;
import com.jeju.nanaland.domain.member.service.ProfileImageService;
import com.jeju.nanaland.global.exception.ServerErrorException;
import com.jeju.nanaland.global.file.data.FileCategory;
import com.jeju.nanaland.global.file.service.FileUploadService;
import com.jeju.nanaland.global.image_upload.S3ImageService;
import com.jeju.nanaland.global.image_upload.dto.S3ImageDto;
import java.io.File;
Expand All @@ -34,6 +36,7 @@ public class ImageFileService {
private String MEMBER_PROFILE_DIRECTORY;
private final S3ImageService s3ImageService;
private final ImageFileRepository imageFileRepository;
private final FileUploadService fileUploadService;


public ImageFile saveS3ImageFile(S3ImageDto s3ImageDto) {
Expand Down Expand Up @@ -87,4 +90,9 @@ public void uploadMemberProfileImage(Long memberId, File file) {
CompletableFuture.failedFuture(new ServerErrorException(SERVER_ERROR.getMessage()));
}
}

public ImageFile getAndSaveImageFile(String fileKey) {
S3ImageDto s3ImageDto = fileUploadService.getCloudImageUrls(fileKey);
return saveS3ImageFile(s3ImageDto);
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
package com.jeju.nanaland.domain.common.service;

import static com.jeju.nanaland.global.exception.ErrorCode.*;

import com.jeju.nanaland.domain.common.entity.VideoFile;
import com.jeju.nanaland.domain.common.repository.VideoFileRepository;
import com.jeju.nanaland.global.exception.ServerErrorException;
import com.jeju.nanaland.global.image_upload.S3VideoService;
import com.jeju.nanaland.global.file.service.FileUploadService;
import com.jeju.nanaland.global.image_upload.dto.S3VideoDto;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@Service
@RequiredArgsConstructor
public class VideoFileService {

private final S3VideoService s3VideoService;
private final VideoFileRepository videoFileRepository;
private final FileService fileService;
private final FileUploadService fileUploadService;

public VideoFile saveS3VideoFile(S3VideoDto s3VideoDto) {
VideoFile videoFile = VideoFile.builder()
Expand All @@ -32,15 +24,8 @@ public VideoFile saveS3VideoFile(S3VideoDto s3VideoDto) {
}

// S3에 저장될 경로 지정
public VideoFile uploadAndSaveVideoFile(File file, String directory) {
try {
MultipartFile multipartFile = fileService.convertFileToMultipartFile(file);
CompletableFuture<S3VideoDto> futureVideoDto = s3VideoService.uploadVideoToS3(multipartFile, directory);
S3VideoDto s3VideoDto = futureVideoDto.join();
return saveS3VideoFile(s3VideoDto);
} catch (IOException e) {
log.error("파일 업로드 오류: {}", e.getMessage());
throw new ServerErrorException(FILE_FAIL_ERROR.getMessage());
}
public VideoFile getAndSaveVideoFile(String fileKey) {
S3VideoDto s3VideoDto = fileUploadService.getCloudVideoUrls(fileKey);
return saveS3VideoFile(s3VideoDto);
}
}
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 @@ -42,17 +42,14 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@Slf4j
@RestController
Expand All @@ -75,12 +72,11 @@ public class MemberController {
@ApiResponse(responseCode = "409", description = "이미 가입된 계정이 있는 경우, 닉네임이 중복되는 경우", content = @Content),
@ApiResponse(responseCode = "500", description = "서버측 에러", content = @Content)
})
@PostMapping(value = "/join",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = "/join")
public BaseResponse<JwtDto> join(
@RequestPart(value = "reqDto") @Valid MemberRequest.JoinDto joinDto,
@RequestPart(required = false) MultipartFile multipartFile) {
JwtDto jwtDto = memberLoginService.join(joinDto, multipartFile);
@RequestBody @Valid MemberRequest.JoinDto joinDto,
@RequestParam(required = false) String fileKey) {
JwtDto jwtDto = memberLoginService.join(joinDto, fileKey);
return BaseResponse.success(JOIN_SUCCESS, jwtDto);
}

Expand Down Expand Up @@ -220,6 +216,7 @@ public BaseResponse<Null> withdrawal(
description = "유저 닉네임, 설명, 프로필 사진 수정")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400", description = "파일키 형식이 맞지 않는 등 입력값이 올바르지 않은 경우", content = @Content),
@ApiResponse(responseCode = "401", description = "accessToken이 유효하지 않은 경우", content = @Content),
@ApiResponse(responseCode = "500", description = "이미지 업로드에 실패한 경우", content = @Content)
})
Expand Down
Loading
Loading