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

커뮤니티 전체&추천 여행 목록 조회 쿼리 개선 #709

Merged
merged 9 commits into from
Oct 13, 2023
14 changes: 0 additions & 14 deletions backend/src/main/java/hanglog/community/domain/BaseTripInfo.java

This file was deleted.

12 changes: 12 additions & 0 deletions backend/src/main/java/hanglog/community/domain/LikeInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package hanglog.community.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class LikeInfo {

private final long likeCount;
private final boolean isLike;
}
8 changes: 0 additions & 8 deletions backend/src/main/java/hanglog/community/domain/TripInfo.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package hanglog.community.domain.repository;

import hanglog.community.dto.LikeElement;
import hanglog.community.domain.Likes;
import hanglog.community.domain.TripInfo;
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;
Expand All @@ -15,23 +14,13 @@ public interface LikeRepository extends JpaRepository<Likes, Long> {
void deleteByMemberIdAndTripId(final Long memberId, final Long tripId);

Long countLikesByTripId(final Long tripId);

@Query("""
SELECT l.tripId,
COUNT(l.memberId) AS like_count,
EXISTS(SELECT 1 FROM Likes l_1 WHERE l_1.memberId = :memberId AND l_1.tripId = l.tripId) AS is_like
FROM Likes l
WHERE l.tripId in :tripIds
GROUP BY l.tripId
""")
List<Object[]> countByMemberIdAndTripId(@Param("memberId") Long memberId, @Param("tripIds") List<Long> tripIds);

@Query("""
SELECT COUNT(l.memberId) AS like_count,
EXISTS(SELECT 1 FROM Likes l_1 WHERE l_1.memberId = :memberId AND l_1.tripId = l.tripId) AS is_like
SELECT new hanglog.community.dto.LikeElement
(l.tripId, COUNT(l.memberId), EXISTS(SELECT 1 FROM Likes l_1 WHERE l_1.memberId = :memberId AND l_1.tripId = l.tripId))
FROM Likes l
WHERE l.tripId = :tripId
WHERE l.tripId in :tripIds
GROUP BY l.tripId
""")
Optional<TripInfo> countByMemberIdAndTripId(@Param("memberId") Long memberId, @Param("tripId") Long tripId);
""")
List<LikeElement> findLikeCountAndIsLikeByTripIds(@Param("memberId") final Long memberId, @Param("tripIds") final List<Long> tripIds);
}
13 changes: 13 additions & 0 deletions backend/src/main/java/hanglog/community/dto/CityElement.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package hanglog.community.dto;

import hanglog.city.domain.City;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class CityElement {

private final Long tripId;
private final City city;
}
24 changes: 24 additions & 0 deletions backend/src/main/java/hanglog/community/dto/CityElements.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package hanglog.community.dto;

import hanglog.city.domain.City;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;

@AllArgsConstructor
public class CityElements {

private final List<CityElement> cityElements;

public static Map<Long, List<City>> toCityMap(final List<CityElement> cityElements) {
final Map<Long, List<City>> map = new HashMap<>();
for (final CityElement cityElement : cityElements) {
final Long tripId = cityElement.getTripId();
final City city = cityElement.getCity();
map.computeIfAbsent(tripId, k -> new ArrayList<>()).add(city);
}
return map;
}
}
13 changes: 13 additions & 0 deletions backend/src/main/java/hanglog/community/dto/LikeElement.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package hanglog.community.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class LikeElement {

private final Long tripId;
private final long likeCount;
private final boolean isLike;
}
22 changes: 22 additions & 0 deletions backend/src/main/java/hanglog/community/dto/LikeElements.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package hanglog.community.dto;

import hanglog.community.domain.LikeInfo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;

@AllArgsConstructor
public class LikeElements {

private final List<LikeElement> likeElements;

public static Map<Long, LikeInfo> toLikeMap(final List<LikeElement> likeElements) {
final Map<Long, LikeInfo> map = new HashMap<>();
for (final LikeElement likeElement : likeElements) {
final LikeInfo likeInfo = new LikeInfo(likeElement.getLikeCount(), likeElement.isLike());
map.put(likeElement.getTripId(), likeInfo);
}
return map;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public ResponseEntity<CommunityTripListResponse> getTrips(
@Auth final Accessor accessor,
@PageableDefault(sort = "publishedTrip.id", direction = DESC) final Pageable pageable
) {
final CommunityTripListResponse communityTripListResponse = communityService.getTripsByPage(accessor, pageable);
final CommunityTripListResponse communityTripListResponse = communityService.getCommunityTripsByPage(accessor, pageable);
return ResponseEntity.ok().body(communityTripListResponse);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
import hanglog.community.domain.recommendstrategy.RecommendStrategies;
import hanglog.community.domain.recommendstrategy.RecommendStrategy;
import hanglog.community.domain.repository.LikeRepository;
import hanglog.community.dto.CityElement;
import hanglog.community.dto.CityElements;
import hanglog.community.domain.LikeInfo;
import hanglog.community.dto.LikeElement;
import hanglog.community.dto.LikeElements;
import hanglog.community.dto.response.CommunityTripListResponse;
import hanglog.community.dto.response.CommunityTripResponse;
import hanglog.community.dto.response.RecommendTripListResponse;
Expand All @@ -21,6 +26,7 @@
import hanglog.trip.dto.response.TripDetailResponse;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
Expand All @@ -39,53 +45,64 @@ public class CommunityService {
private final RecommendStrategies recommendStrategies;
private final PublishedTripRepository publishedTripRepository;

public CommunityTripListResponse getTripsByPage(final Accessor accessor, final Pageable pageable) {
public CommunityTripListResponse getCommunityTripsByPage(final Accessor accessor, final Pageable pageable) {
final List<Trip> trips = tripRepository.findPublishedTripByPageable(pageable.previousOrFirst());
final List<CommunityTripResponse> communityTripResponse = trips.stream()
.map(trip -> getTripResponse(accessor, trip))
.toList();
final List<CommunityTripResponse> communityTripResponses = getCommunityTripResponses(accessor, trips);
final Long lastPageIndex = getLastPageIndex(pageable.getPageSize());
return new CommunityTripListResponse(communityTripResponses, lastPageIndex);
}

return new CommunityTripListResponse(communityTripResponse, lastPageIndex);
public RecommendTripListResponse getRecommendTrips(final Accessor accessor) {
final RecommendStrategy recommendStrategy = recommendStrategies.mapByRecommendType(LIKE);
final Pageable pageable = Pageable.ofSize(RECOMMEND_AMOUNT);
final List<Trip> trips = recommendStrategy.recommend(pageable);
final List<CommunityTripResponse> communityTripResponses = getCommunityTripResponses(accessor, trips);
return new RecommendTripListResponse(recommendStrategy.getTitle(), communityTripResponses);
}

private CommunityTripResponse getTripResponse(final Accessor accessor, final Trip trip) {
final List<City> cities = getCitiesByTripId(trip.getId());
final Long likeCount = likeRepository.countLikesByTripId(trip.getId());
if (accessor.isMember()) {
final boolean isLike = likeRepository.existsByMemberIdAndTripId(accessor.getMemberId(), trip.getId());
return CommunityTripResponse.of(trip, cities, isLike, likeCount);
private List<CommunityTripResponse> getCommunityTripResponses(final Accessor accessor, final List<Trip> trips) {
final List<Long> tripIds = trips.stream().map(Trip::getId).toList();

final List<CityElement> cityElements = tripCityRepository.findTripIdAndCitiesByTripIds(tripIds);
final Map<Long, List<City>> citiesByTrip = CityElements.toCityMap(cityElements);

final List<LikeElement> likeElements = likeRepository.findLikeCountAndIsLikeByTripIds(accessor.getMemberId(), tripIds);
final Map<Long, LikeInfo> likeInfoByTrip = LikeElements.toLikeMap(likeElements);

return trips.stream()
.map(trip -> CommunityTripResponse.of(
trip,
citiesByTrip.get(trip.getId()),
isLike(likeInfoByTrip, trip.getId()),
getLikeCount(likeInfoByTrip, trip.getId())
)).toList();
}

private boolean isLike(final Map<Long, LikeInfo> likeInfoByTrip, final Long tripId) {
final LikeInfo likeInfo = likeInfoByTrip.get(tripId);
if (likeInfo == null) {
return false;
}
return CommunityTripResponse.of(trip, cities, false, likeCount);
return likeInfo.isLike();
}

private List<City> getCitiesByTripId(final Long tripId) {
return tripCityRepository.findByTripId(tripId).stream()
.map(TripCity::getCity)
.toList();
private Long getLikeCount(final Map<Long, LikeInfo> likeInfoByTrip, final Long tripId) {
final LikeInfo likeInfo = likeInfoByTrip.get(tripId);
if (likeInfo == null) {
return 0L;
}
return likeInfo.getLikeCount();
}

private Long getLastPageIndex(final int pageSize) {
final Long totalTripCount = tripRepository.countTripByPublishedStatus(PUBLISHED);
final Long lastPageIndex = totalTripCount / pageSize;
final long lastPageIndex = totalTripCount / pageSize;
if (totalTripCount % pageSize == 0) {
return lastPageIndex;
}
return lastPageIndex + 1;
}

public RecommendTripListResponse getRecommendTrips(final Accessor accessor) {
final RecommendStrategy recommendStrategy = recommendStrategies.mapByRecommendType(LIKE);
final Pageable pageable = Pageable.ofSize(RECOMMEND_AMOUNT);
final List<Trip> trips = recommendStrategy.recommend(pageable);

final List<CommunityTripResponse> communityTripResponses = trips.stream()
.map(trip -> getTripResponse(accessor, trip))
.toList();

return new RecommendTripListResponse(recommendStrategy.getTitle(), communityTripResponses);
}

public TripDetailResponse getTripDetail(final Accessor accessor, final Long tripId) {
final Trip trip = tripRepository.findById(tripId)
.orElseThrow(() -> new BadRequestException(NOT_FOUND_TRIP_ID));
Expand Down Expand Up @@ -115,4 +132,10 @@ public TripDetailResponse getTripDetail(final Accessor accessor, final Long trip
publishedDate
);
}

private List<City> getCitiesByTripId(final Long tripId) {
return tripCityRepository.findByTripId(tripId).stream()
.map(TripCity::getCity)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package hanglog.trip.domain.repository;

import hanglog.community.dto.CityElement;
import hanglog.trip.domain.TripCity;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -18,4 +19,11 @@ public interface TripCityRepository extends JpaRepository<TripCity, Long> {
WHERE tripCity.trip.id = :tripId
""")
void deleteAllByTripId(@Param("tripId") final Long tripId);

@Query("""
SELECT new hanglog.community.dto.CityElement (tc.trip.id, tc.city)
FROM TripCity tc
WHERE tc.trip.id IN :tripIds
""")
List<CityElement> findTripIdAndCitiesByTripIds(@Param("tripIds") final List<Long> tripIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,23 @@ public interface TripRepository extends JpaRepository<Trip, Long> {
@Query("SELECT trip FROM Trip trip LEFT JOIN FETCH trip.sharedTrip WHERE trip.member.id = :memberId")
List<Trip> findAllByMemberId(@Param("memberId") final Long memberId);

@Query("SELECT trip FROM Trip trip "
+ "LEFT JOIN PublishedTrip publishedTrip ON publishedTrip.trip = trip "
+ "WHERE trip.publishedStatus = 'PUBLISHED'")
@Query("""
SELECT trip FROM Trip trip
LEFT JOIN PublishedTrip publishedTrip ON publishedTrip.trip = trip
LEFT JOIN FETCH trip.sharedTrip sharedTrip
LEFT JOIN FETCH trip.member member
WHERE trip.publishedStatus = 'PUBLISHED'
""")
List<Trip> findPublishedTripByPageable(final Pageable pageable);

@Query("SELECT trip FROM Trip trip "
+ "LEFT JOIN Likes likes ON likes.tripId = trip.id "
+ "WHERE trip.publishedStatus = 'PUBLISHED' "
+ "GROUP BY trip.id "
+ "ORDER BY COUNT(likes.tripId) DESC")
@Query("""
SELECT trip FROM Trip trip
LEFT JOIN Likes likes ON likes.tripId = trip.id
LEFT JOIN FETCH trip.sharedTrip sharedTrip
WHERE trip.publishedStatus = 'PUBLISHED'
GROUP BY trip.id
ORDER BY COUNT(likes.tripId) DESC
""")
List<Trip> findTripsOrderByLikesCount(final Pageable pageable);

Long countTripByPublishedStatus(final PublishedStatusType publishedStatusType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private ResultActions performGetExpense(final int tripId) throws Exception {
@Test
void getTripsByPage() throws Exception {
// given
when(communityService.getTripsByPage(any(), any()))
when(communityService.getCommunityTripsByPage(any(), any()))
.thenReturn(new CommunityTripListResponse(
List.of(CommunityTripResponse.of(LONDON_TRIP, CITIES, true, 1L)),
1L
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void setTrips() {
void getTripsByPage() {
// when
final Pageable pageable = PageRequest.of(1, 10, DESC, "publishedTrip.id");
final CommunityTripListResponse response = communityService.getTripsByPage(Accessor.member(1L), pageable);
final CommunityTripListResponse response = communityService.getCommunityTripsByPage(Accessor.member(1L), pageable);
final List<CommunityTripResponse> tripResponses = response.getTrips();

// then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import hanglog.category.domain.Category;
import hanglog.category.domain.repository.CategoryRepository;
import hanglog.category.fixture.CategoryFixture;
import hanglog.expense.domain.repository.ExpenseRepository;
import hanglog.global.exception.BadRequestException;
import hanglog.image.domain.repository.ImageRepository;
import hanglog.trip.domain.DayLog;
Expand Down Expand Up @@ -53,6 +54,9 @@ class ItemServiceTest {
@Mock
private ImageRepository imageRepository;

@Mock
private ExpenseRepository expenseRepository;

@DisplayName("새롭게 생성한 여행 아이템의 id를 반환한다.")
@Test
void save() {
Expand Down Expand Up @@ -246,7 +250,7 @@ void delete() {
itemService.delete(itemForDelete.getId());

// then
verify(itemRepository).delete(any());
verify(itemRepository).deleteById(any());
Comment on lines -249 to +253
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

여기 건들지도 않았는데 테스트 터짐! 이게 말.이.됩.니.까?
근데 이전 PR에선 안터졌음. 근데 터지는게 맞긴함
왜 안터졌지 ..? 뭔가 놓치고 있나 .. ? 근데 아무리 봐도 잘 모르겠다 ~

쨌든 테스트 터진 문제는 아래와 같습니다.
ExpenseRepository 참조하는데 모킹이 안되어있었고,
delete() -> deleteById()로 로직 바뀌었는데 verify 검증 부분은 안바꿔줘서 발생한 문제

}

@DisplayName("모든 여행 아이템의 Response를 반환한다.")
Expand Down