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

검색 쿼리 개선 #703

Merged
merged 11 commits into from
Sep 27, 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 @@ -13,6 +13,22 @@ public interface TemplateTagJpaRepository extends TemplateTagRepository, JpaRepo

List<TemplateTag> findAllByTemplate(Template template);

@Query("""
SELECT tt, t
FROM TemplateTag tt
JOIN FETCH tt.tag t
WHERE tt.id.templateId = :templateId
""")
List<TemplateTag> findAllByTemplateId(Long templateId);

@Query("""
SELECT tt, t
FROM TemplateTag tt
JOIN FETCH tt.tag t
WHERE tt.id.templateId in :templateIds
""")
List<TemplateTag> findAllByTemplateIdsIn(List<Long> templateIds);

@Query("""
SELECT DISTINCT tt.id.tagId
FROM TemplateTag tt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ public interface TemplateTagRepository {

List<TemplateTag> findAllByTemplate(Template template);

List<TemplateTag> findAllByTemplateId(Long templateId);

List<Long> findDistinctByTemplateIn(List<Long> templateIds);

List<TemplateTag> findAllByTemplateIdsIn(List<Long> templateIds);

TemplateTag save(TemplateTag templateTag);

<S extends TemplateTag> List<S> saveAll(Iterable<S> entities);
Expand Down
11 changes: 11 additions & 0 deletions backend/src/main/java/codezap/tag/service/TagService.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ public List<Tag> findAllByTemplate(Template template) {
.toList();
}

public List<Tag> findAllByTemplateId(Long templateId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용되지 않는 메소드인 것 같네요.

return templateTagRepository.findAllByTemplateId(templateId).stream()
.map(TemplateTag::getTag)
.toList();
}
Comment on lines +55 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에 Tag 를 받도록 변경하면 좋을 것 같아욧 ~


public List<TemplateTag> getAllTemplateTagsByTemplates(List<Template> templates) {
List<Long> templateIds = templates.stream().map(Template::getId).toList();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
List<Long> templateIds = templates.stream().map(Template::getId).toList();
List<Long> templateIds = templates.stream()
.map(Template::getId)
.toList();

return templateTagRepository.findAllByTemplateIdsIn(templateIds);
}

public FindAllTagsResponse findAllByMemberId(Long memberId) {
List<Template> templates = templateRepository.findByMemberId(memberId);
List<Long> templateIds = templates.stream().map(Template::getId).toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ public TemplateTag(Template template, Tag tag) {
this.template = template;
this.tag = tag;
}

public boolean hasTemplate(Template template) {
return this.getTemplate().equals(template);
}
}
4 changes: 4 additions & 0 deletions backend/src/main/java/codezap/template/domain/Thumbnail.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,8 @@ public Thumbnail(Template template, SourceCode sourceCode) {
public void updateThumbnail(SourceCode sourceCode) {
this.sourceCode = sourceCode;
}

public boolean hasTemplate(Template template) {
return this.template.equals(template);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
Expand Down Expand Up @@ -37,6 +38,11 @@ public Predicate toPredicate(Root<Template> root, CriteriaQuery<?> query, Criter
addTagPredicate(predicates, criteriaBuilder, root, query);
addKeywordPredicate(predicates, criteriaBuilder, root, query);

if (query.getResultType().equals(Template.class)) {
root.fetch("category", JoinType.LEFT);
root.fetch("member", JoinType.LEFT);
Comment on lines +42 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추후 상수화 해요 ~

}

return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package codezap.template.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.http.HttpStatus;

import codezap.global.exception.CodeZapException;
Expand All @@ -19,7 +21,21 @@ default Thumbnail fetchByTemplate(Template template) {
"식별자가 " + template.getId() + "인 템플릿에 해당하는 썸네일이 없습니다."));
}

@Query("""
SELECT t, sc
FROM Thumbnail t
join fetch t.sourceCode sc
WHERE t.template = :template
""")
Optional<Thumbnail> findByTemplate(Template template);

@Query("""
SELECT t, sc
FROM Thumbnail t
join fetch t.sourceCode sc
WHERE t.template.id IN :templateIds
""")
List<Thumbnail> findAllByTemplateIn(List<Long> templateIds);

void deleteByTemplateId(Long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface ThumbnailRepository {

List<Thumbnail> findAll();

List<Thumbnail> findAllByTemplateIn(List<Long> templateIds);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
List<Thumbnail> findAllByTemplateIn(List<Long> templateIds);
List<Thumbnail> findAllByTemplateIdsIn(List<Long> templateIds);


Thumbnail save(Thumbnail thumbnail);

void deleteByTemplateId(Long id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ public Thumbnail getByTemplate(Template template) {
return thumbnailRepository.fetchByTemplate(template);
}

public List<Thumbnail> getAllByTemplates(List<Template> templates) {
List<Long> templateIds = templates.stream()
.map(Template::getId)
Comment on lines +30 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추후 id만 받도록 코드 개선하면 커버링 인덱스를 이용할 수 있을 것 같아요 ~

.toList();

return thumbnailRepository.findAllByTemplateIn(templateIds);
}

public ExploreTemplatesResponse findAll() {
return ExploreTemplatesResponse.from(thumbnailRepository.findAll());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import codezap.tag.service.TagService;
import codezap.template.domain.SourceCode;
import codezap.template.domain.Template;
import codezap.template.domain.TemplateTag;
import codezap.template.domain.Thumbnail;
import codezap.template.dto.request.CreateTemplateRequest;
import codezap.template.dto.request.UpdateTemplateRequest;
Expand Down Expand Up @@ -91,19 +92,43 @@ public FindAllTemplatesResponse findAllByWithMember(
}

private FindAllTemplatesResponse makeResponse(Page<Template> page, LikedChecker likedChecker) {
List<FindAllTemplateItemResponse> findAllTemplateByResponse = page.stream()
.map(template -> FindAllTemplateItemResponse.of(
template,
tagService.findAllByTemplate(template),
thumbnailService.getByTemplate(template).getSourceCode(),
likedChecker.isLiked(template)))
.toList();
List<Template> templates = page.getContent();
List<FindAllTemplateItemResponse> findAllTemplateByResponse = getFindAllTemplateItemResponses(templates, likedChecker);

return new FindAllTemplatesResponse(
page.getTotalPages(),
page.getTotalElements(),
findAllTemplateByResponse);
}

private List<FindAllTemplateItemResponse> getFindAllTemplateItemResponses(List<Template> templates, LikedChecker likedChecker) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private List<FindAllTemplateItemResponse> getFindAllTemplateItemResponses(List<Template> templates, LikedChecker likedChecker) {
private List<FindAllTemplateItemResponse> findAllTemplateItemResponses(List<Template> templates, LikedChecker likedChecker) {

List<TemplateTag> allTemplateTagsByTemplates = tagService.getAllTemplateTagsByTemplates(templates);
List<Thumbnail> allThumbnailsByTemplates = thumbnailService.getAllByTemplates(templates);

return templates.stream()
.map(template -> FindAllTemplateItemResponse.of(
template,
getTagByTemplate(allTemplateTagsByTemplates, template),
getThumbnailSourceCodeByTemplate(allThumbnailsByTemplates, template),
likedChecker.isLiked(template)))
.toList();
}

private List<Tag> getTagByTemplate(List<TemplateTag> templateTags, Template template) {
return templateTags.stream()
.filter(templateTag -> templateTag.hasTemplate(template))
.map(TemplateTag::getTag)
.toList();
}

private SourceCode getThumbnailSourceCodeByTemplate(List<Thumbnail> thumbnails, Template template) {
return thumbnails.stream()
.filter(thumbnail -> thumbnail.hasTemplate(template))
.findFirst()
.map(Thumbnail::getSourceCode)
.orElseGet(() -> thumbnailService.getByTemplate(template).getSourceCode());
}

@Transactional
public void update(Member member, Long templateId, UpdateTemplateRequest updateTemplateRequest) {
Category category = categoryService.fetchById(updateTemplateRequest.categoryId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,53 @@ void findAllByTemplateTest() {
.doesNotContain(new TemplateTag(template, tag3));
}


@Test
@DisplayName("Template Id 을 이용한 TemplateTag 목록 조회 성공")
void findAllByTemplateIdTest() {
//given
Template template = templateRepository.save(createNthTemplate(member, category, 1));

Tag tag1 = tagRepository.save(new Tag("tag1"));
Tag tag2 = tagRepository.save(new Tag("tag2"));
Tag tag3 = tagRepository.save(new Tag("tag3"));

TemplateTag templateTag1 = templateTagRepository.save(new TemplateTag(template, tag1));
TemplateTag templateTag2 = templateTagRepository.save(new TemplateTag(template, tag2));

//when
List<TemplateTag> templateTags = templateTagRepository.findAllByTemplateId(template.getId());

//then
assertThat(templateTags).containsExactly(templateTag1, templateTag2)
.doesNotContain(new TemplateTag(template, tag3));
}

@Test
@DisplayName("Template Id 목록 중 하나라도 일치하는 TemplateTag 목록 조회 성공")
void findAllByTemplateIdsInTest() {
//given
Template template1 = templateRepository.save(createNthTemplate(member, category, 1));
Template template2 = templateRepository.save(createNthTemplate(member, category, 1));

Tag tag1 = tagRepository.save(new Tag("tag1"));
Tag tag2 = tagRepository.save(new Tag("tag2"));
Tag tag3 = tagRepository.save(new Tag("tag3"));

TemplateTag templateTag1 = templateTagRepository.save(new TemplateTag(template1, tag1));

TemplateTag templateTag2 = templateTagRepository.save(new TemplateTag(template2, tag2));

//when
List<TemplateTag> templateTags = templateTagRepository.findAllByTemplateIdsIn(
List.of(template1.getId(), template2.getId())
);

//then
assertThat(templateTags).containsExactly(templateTag1, templateTag2)
.doesNotContain(new TemplateTag(template1, tag3));
}

@Nested
@DisplayName("템플릿 id 목록이 사용하는 모든 태그 목록을 조회")
class FindDistinctByTemplateIn {
Expand Down
86 changes: 84 additions & 2 deletions backend/src/test/java/codezap/tag/service/TagServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,88 @@ void findAllByTemplate_WhenNotExistTemplate() {
}
}

@Nested
@DisplayName("템플릿 Id로 태그 조회")
class FindAllByTemplateId {

@Test
@DisplayName("성공: 템플릿 Id에 해당하는 태그 목록 반환")
void findAllByTemplate() {
// given
Template template = createSavedTemplate();
Tag tag1 = tagRepository.save(new Tag("tag1"));
Tag tag2 = tagRepository.save(new Tag("tag2"));
TemplateTag templateTag1 = templateTagRepository.save(new TemplateTag(template, tag1));
TemplateTag templateTag2 = templateTagRepository.save(new TemplateTag(template, tag2));

// when & then
assertThat(sut.findAllByTemplateId(template.getId()))
.containsExactly(templateTag1.getTag(), templateTag2.getTag());
}

@Test
@DisplayName("성공: 템플릿에 해당하는 태그가 없는 경우 빈 목록 반환")
void findAllByTemplate_WhenNotExistTemplateTag() {
// given
Template template = createSavedTemplate();
tagRepository.save(new Tag("tag1"));
tagRepository.save(new Tag("tag2"));

// when & then
assertThat(sut.findAllByTemplateId(template.getId())).isEmpty();
}

@Test
@Disabled("현재 InvalidDataAccessApiUsageException 발생하므로 조회 직전에 검증 처리가 필요")
@DisplayName("실패: 존재하지 않는 템플릿으로 태그 조회")
void findAllByTemplate_WhenNotExistTemplate() {
// given
Member member = memberRepository.save(MemberFixture.getFirstMember());
Category category = categoryRepository.save(CategoryFixture.getFirstCategory());
Template unSavedTemplate = TemplateFixture.get(member, category);
tagRepository.save(new Tag("tag1"));
tagRepository.save(new Tag("tag2"));

// when & then
assertThatThrownBy(() -> sut.findAllByTemplateId(unSavedTemplate.getId()))
.isInstanceOf(CodeZapException.class)
.hasMessage("템플릿이 존재하지 않아 태그를 조회할 수 없습니다.");
}
}

@Nested
@DisplayName("템플릿 목록으로 템플릿 태그 조회")
class GetAllTemplateTagsByTemplates {

@Test
@DisplayName("성공: 템플릿 목록에 하나라도 해당하는 템플릿 태그 목록 반환")
void getAllTemplateTagsByTemplates() {
// given
Template template = createSavedTemplate();
Template secondTemplate = createSecondTemplate();
Tag tag1 = tagRepository.save(new Tag("tag1"));
Tag tag2 = tagRepository.save(new Tag("tag2"));
TemplateTag templateTag1 = templateTagRepository.save(new TemplateTag(template, tag1));
TemplateTag templateTag2 = templateTagRepository.save(new TemplateTag(secondTemplate, tag2));

// when & then
assertThat(sut.getAllTemplateTagsByTemplates(List.of(template, secondTemplate)))
.containsExactly(templateTag1, templateTag2);
}

@Test
@DisplayName("성공: 템플릿 목록에 전혀 해당하는 템플릿 태그 빈 목록 반환")
void getAllTemplateTagsByTemplates_WhenNotExistTemplateTag() {
// given
Template template = createSavedTemplate();
tagRepository.save(new Tag("tag1"));
tagRepository.save(new Tag("tag2"));

// when & then
assertThat(sut.getAllTemplateTagsByTemplates(List.of(template))).isEmpty();
}
}

@Nested
@DisplayName("사용자 ID로 모든 태그 조회")
class FindAllByMemberId {
Expand Down Expand Up @@ -200,8 +282,8 @@ void findAllByTemplates() {

// when & then
List<Tag> actual = new ArrayList<>();
actual.addAll(sut.findAllByTemplate(template1));
actual.addAll(sut.findAllByTemplate(template2));
actual.addAll(sut.findAllByTemplateId(template1.getId()));
actual.addAll(sut.findAllByTemplateId(template2.getId()));
assertThat(actual).isEqualTo(List.of(tag1, tag2));
}

Expand Down
Loading