Skip to content

Commit

Permalink
refactor: 스와이프 최초 진입시 쿼리 최적화
Browse files Browse the repository at this point in the history
  • Loading branch information
seokhwan-an committed Sep 16, 2023
1 parent 118894a commit dc74a0f
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package shook.shook.song.application;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
Expand All @@ -20,11 +16,17 @@
import shook.shook.song.application.killingpart.dto.HighLikedSongResponse;
import shook.shook.song.domain.Song;
import shook.shook.song.domain.SongTitle;
import shook.shook.song.domain.killingpart.repository.KillingPartLikeRepository;
import shook.shook.song.domain.killingpart.repository.KillingPartRepository;
import shook.shook.song.domain.repository.SongRepository;
import shook.shook.song.domain.repository.dto.SongTotalLikeCountDto;
import shook.shook.song.exception.SongException;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
Expand All @@ -36,6 +38,7 @@ public class SongService {
private final SongRepository songRepository;
private final KillingPartRepository killingPartRepository;
private final MemberRepository memberRepository;
private final KillingPartLikeRepository killingPartLikeRepository;
private final SongDataExcelReader songDataExcelReader;

@Transactional
Expand Down Expand Up @@ -80,8 +83,21 @@ public SongSwipeResponse findSongByIdForFirstSwipe(
) {
final Song currentSong = findSongById(songId);

final List<Song> beforeSongs = findBeforeSongs(currentSong);
final List<Song> afterSongs = findAfterSongs(currentSong);
final List<Song> songs = songRepository.findAllWithKillingParts();
songs.sort(Comparator.comparing(Song::getTotalLikeCount, Comparator.reverseOrder())
.thenComparing(Song::getId, Comparator.reverseOrder()));

final int currentSongIndex = songs.indexOf(currentSong);
final List<Song> beforeSongs = songs.subList(Math.max(0, currentSongIndex - 10), currentSongIndex);

if (currentSongIndex == songs.size() - 1) {
return convertToSongSwipeResponse(memberInfo, currentSong, beforeSongs, Collections.emptyList());
}

final List<Song> afterSongs = songs.subList(
Math.min(currentSongIndex + 1, songs.size() - 1),
Math.min(songs.size(), currentSongIndex + 11)
);

return convertToSongSwipeResponse(memberInfo, currentSong, beforeSongs, afterSongs);
}
Expand Down Expand Up @@ -120,8 +136,9 @@ private SongSwipeResponse convertToSongSwipeResponse(
}

final Member member = findMemberById(memberInfo.getMemberId());
final List<Long> killingPartIdsByMember = killingPartLikeRepository.findKillingPartIdsByMember(member);

return SongSwipeResponse.of(member, currentSong, beforeSongs, afterSongs);
return SongSwipeResponse.of(killingPartIdsByMember, currentSong, beforeSongs, afterSongs);
}

private Member findMemberById(final Long memberId) {
Expand Down Expand Up @@ -158,9 +175,9 @@ private List<SongResponse> convertToSongResponses(
}

final Member member = findMemberById(memberInfo.getMemberId());

final List<Long> killingPartIdsByMember = killingPartLikeRepository.findKillingPartIdsByMember(member);
return songs.stream()
.map(song -> SongResponse.of(song, member))
.map(song -> SongResponse.of(song, killingPartIdsByMember))
.toList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import shook.shook.member.domain.Member;
import shook.shook.song.domain.Song;
import shook.shook.song.domain.killingpart.KillingPart;

Expand Down Expand Up @@ -41,7 +40,7 @@ public static KillingPartResponse of(
final Song song,
final KillingPart killingPart,
final int rank,
final Member member
final boolean isLikedByMember
) {
return new KillingPartResponse(
killingPart.getId(),
Expand All @@ -51,7 +50,7 @@ public static KillingPartResponse of(
killingPart.getEndSecond(),
song.getPartVideoUrl(killingPart),
killingPart.getLength(),
killingPart.isLikedByMember(member)
isLikedByMember
);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package shook.shook.song.application.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import shook.shook.member.domain.Member;
import shook.shook.song.domain.Song;
import shook.shook.song.domain.killingpart.KillingPart;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Schema(description = "노래 응답")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
Expand All @@ -37,29 +38,35 @@ public class SongResponse {
@Schema(description = "킬링파트 3개")
private final List<KillingPartResponse> killingParts;

public static SongResponse of(final Song song, final Member member) {
public static SongResponse of(final Song song, final List<Long> likedKillingPartIds) {
return new SongResponse(
song.getId(),
song.getTitle(),
song.getSinger(),
song.getLength(),
song.getVideoId(),
song.getAlbumCoverUrl(),
toKillingPartResponses(song, member)
toKillingPartResponses(song, likedKillingPartIds)
);
}

public static SongResponse fromUnauthorizedUser(final Song song) {
return SongResponse.of(song, null);
return SongResponse.of(song, Collections.emptyList());
}

private static List<KillingPartResponse> toKillingPartResponses(final Song song,
final Member member) {
private static List<KillingPartResponse> toKillingPartResponses(final Song song, final List<Long> likedKillingPartIds) {
final List<KillingPart> songKillingParts = song.getLikeCountSortedKillingParts();

return IntStream.range(0, songKillingParts.size())
.mapToObj(index ->
KillingPartResponse.of(song, songKillingParts.get(index), index + 1, member))
.mapToObj(index -> {
final KillingPart killingPart = songKillingParts.get(index);
return KillingPartResponse.of(song, killingPart, index + 1, isLikedKillingPart(killingPart, likedKillingPartIds));
}
)
.collect(Collectors.toList());
}

private static boolean isLikedKillingPart(final KillingPart killingPart, final List<Long> likedPartIds) {
return likedPartIds.contains(killingPart.getId());
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package shook.shook.song.application.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import shook.shook.member.domain.Member;
import shook.shook.song.domain.Song;

import java.util.List;

@Schema(description = "첫 스와이프 시, 현재 노래와 이전, 이후 노래 리스트 조회 응답")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
Expand All @@ -23,17 +23,17 @@ public class SongSwipeResponse {
private final List<SongResponse> nextSongs;

public static SongSwipeResponse of(
final Member member,
final List<Long> likedKillingPartIds,
final Song currentSong,
final List<Song> prevSongs,
final List<Song> nextSongs
) {
final SongResponse currentResponse = SongResponse.of(currentSong, member);
final SongResponse currentResponse = SongResponse.of(currentSong, likedKillingPartIds);
final List<SongResponse> prevResponses = prevSongs.stream()
.map(song -> SongResponse.of(song, member))
.map(song -> SongResponse.of(song, likedKillingPartIds))
.toList();
final List<SongResponse> nextResponses = nextSongs.stream()
.map(song -> SongResponse.of(song, member))
.map(song -> SongResponse.of(song, likedKillingPartIds))
.toList();

return new SongSwipeResponse(prevResponses, currentResponse, nextResponses);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@ public List<KillingPart> getKillingPartsSortedByLikeCount() {
.thenComparing(KillingPart::getStartSecond))
.toList();
}

public int getKillingPartsTotalLikeCount() {
return killingParts.stream()
.reduce(0, (sum, killingPart) -> sum + killingPart.getLikeCount(), Integer::sum);
}
}
4 changes: 4 additions & 0 deletions backend/src/main/java/shook/shook/song/domain/Song.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ public List<KillingPart> getLikeCountSortedKillingParts() {
return killingParts.getKillingPartsSortedByLikeCount();
}

public int getTotalLikeCount() {
return killingParts.getKillingPartsTotalLikeCount();
}

@Override
public boolean equals(final Object o) {
if (this == o) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
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.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import shook.shook.member.domain.Member;
import shook.shook.song.domain.killingpart.KillingPart;
Expand All @@ -15,4 +17,7 @@ Optional<KillingPartLike> findByKillingPartAndMember(final KillingPart killingPa
final Member member);

List<KillingPartLike> findAllByMemberAndIsDeleted(final Member member, final boolean isDeleted);

@Query("SELECT kpl.killingPart.id FROM KillingPartLike kpl WHERE kpl.member = :member")
List<Long> findKillingPartIdsByMember(final @Param("member") Member member);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package shook.shook.song.domain.repository;

import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
Expand All @@ -10,6 +9,8 @@
import shook.shook.song.domain.SongTitle;
import shook.shook.song.domain.repository.dto.SongTotalLikeCountDto;

import java.util.List;

@Repository
public interface SongRepository extends JpaRepository<Song, Long> {

Expand All @@ -18,6 +19,13 @@ public interface SongRepository extends JpaRepository<Song, Long> {
+ "GROUP BY s.id")
List<SongTotalLikeCountDto> findAllWithTotalLikeCount();

@Query("SELECT s AS song "
+ "FROM Song s "
+ "LEFT JOIN FETCH s.killingParts.killingParts kp "
+ "GROUP BY s.id, kp.id")
List<Song> findAllWithKillingParts();

// TODO 좋아요 수가 적은 노래 가져오는 것도 join을 통해 song 과 KillingPart 가
@Query("SELECT s FROM Song s "
+ "LEFT JOIN s.killingParts.killingParts kp "
+ "GROUP BY s.id "
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package shook.shook.song.application;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertAll;

import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand All @@ -29,6 +24,12 @@
import shook.shook.song.exception.SongException;
import shook.shook.support.UsingJpaTest;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertAll;

@Sql("classpath:/schema-test.sql")
class SongServiceTest extends UsingJpaTest {

Expand All @@ -51,6 +52,7 @@ public void setUp() {
songRepository,
killingPartRepository,
memberRepository,
likeRepository,
new SongDataExcelReader(" ", " ", " ")
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,21 @@ void findAllByMemberAndDeleted() {
.comparingOnlyFields("id")
.isEqualTo(List.of(killingPartLike));
}

@Test
@DisplayName("멤버가 좋아요한 킬링파트 id 가져오기")
void getKillingPartIdThatMemberLike() {
// given
final KillingPartLike killingPartLike = new KillingPartLike(SAVED_KILLING_PART,
SAVED_MEMBER);

killingPartLikeRepository.save(killingPartLike);
saveAndClearEntityManager();

// when
final List<Long> killingPartIdsByMemberId = killingPartLikeRepository.findKillingPartIdsByMember(SAVED_MEMBER);

// then
assertThat(killingPartIdsByMemberId).containsExactly(SAVED_KILLING_PART.getId());
}
}

0 comments on commit dc74a0f

Please sign in to comment.