Skip to content

Commit

Permalink
Merge pull request #72 from final-idea-rush/develop
Browse files Browse the repository at this point in the history
카테고리 카운트쿼리 Redis로 대체 및 Full-text 인덱스 적용
  • Loading branch information
Domae-back-end committed Sep 5, 2023
2 parents 747f010 + 12e10f7 commit f07f705
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ public record IdeaListResponse(
Long BidWinPrice
) {


}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.bid.idearush.domain.idea.repository;

import com.bid.idearush.domain.idea.model.entity.Idea;
import com.bid.idearush.domain.idea.type.Category;
import jakarta.persistence.LockModeType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Modifying;
Expand Down Expand Up @@ -34,4 +37,6 @@ public interface IdeaRepository extends
@Query("UPDATE Idea m SET m.auctionStatus = 'ONGOING' WHERE m.auctionStatus = 'PREPARE' and m.auctionStartTime <= :currentTime")
void updatePrepareToOngoing(@Param("currentTime") LocalDateTime currentTime);

Long countByCategory(Category category);

}
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
package com.bid.idearush.domain.idea.repository;

import com.bid.idearush.domain.idea.model.entity.Idea;
import com.bid.idearush.domain.idea.model.reponse.IdeaListResponse;
import com.bid.idearush.domain.idea.model.reponse.IdeaOneResponse;
import com.bid.idearush.domain.idea.type.Category;
import io.lettuce.core.dynamic.annotation.Param;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;

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

public interface IdeaRepositoryCustom {

Page<IdeaListResponse> findIdeaAll(Pageable pageable, long count);
Page<IdeaListResponse> findCategoryAndTitleAll(Category category, String keyword, Pageable pageable, long count);

Page<IdeaListResponse> findCategory(Pageable pageable, Category category, long count);

Page<IdeaListResponse> findTitle(Pageable pageable, String keyword);

Optional<IdeaOneResponse> findIdeaOne(Long ideaId);


}
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,30 @@
import com.bid.idearush.domain.idea.model.entity.QIdea;
import com.bid.idearush.domain.idea.model.reponse.IdeaListResponse;
import com.bid.idearush.domain.idea.model.reponse.IdeaOneResponse;
import com.bid.idearush.domain.idea.type.AuctionStatus;
import com.bid.idearush.domain.idea.type.Category;
import com.bid.idearush.domain.user.model.entity.QUsers;
import com.bid.idearush.global.type.ServerIpAddress;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.JPQLQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.springframework.util.StringUtils.isEmpty;

Expand All @@ -31,10 +38,12 @@ public class IdeaRepositoryCustomImpl extends QuerydslRepositorySupport implemen
private QUsers qUsers = QUsers.users;
private QBid qBid = QBid.bid;
private JPAQueryFactory queryFactory;
private EntityManager entityManager;

public IdeaRepositoryCustomImpl(JPAQueryFactory jpaQueryFactory) {
public IdeaRepositoryCustomImpl(EntityManager entityManager) {
super(Idea.class);
this.queryFactory = jpaQueryFactory;
this.entityManager = entityManager;
this.queryFactory = new JPAQueryFactory(entityManager);
}

@Override
Expand Down Expand Up @@ -85,7 +94,7 @@ public Page<IdeaListResponse> findIdeaAll(Pageable pageable, long count) {
}

@Override
public Page<IdeaListResponse> findCategoryAndTitleAll(Category category, String keyword, Pageable pageable, long count) {
public Page<IdeaListResponse> findCategory(Pageable pageable, Category category, long count) {
List<IdeaListResponse> results = queryFactory.select(Projections.constructor(IdeaListResponse.class,
qIdea.id,
qUsers.nickname.as("writer"),
Expand All @@ -96,10 +105,7 @@ public Page<IdeaListResponse> findCategoryAndTitleAll(Category category, String
qIdea.minimumStartingPrice,
qIdea.bidWinPrice
))
.where(
ideaTitleContains(keyword),
ideaCategoryEq(category)
)
.where(ideaCategoryEq(category))
.from(qIdea)
.innerJoin(qIdea.users, qUsers)
.orderBy(qIdea.createdAt.desc())
Expand All @@ -110,10 +116,41 @@ public Page<IdeaListResponse> findCategoryAndTitleAll(Category category, String
return new PageImpl<>(results, pageable, count);
}

@Override
public Page<IdeaListResponse> findTitle(Pageable pageable, String keyword) {

String sql = "SELECT * FROM idea WHERE to_tsvector('english', title) @@ to_tsquery('english', ?1) ORDER BY created_at DESC OFFSET ?2 ROWS FETCH FIRST ?3 ROWS ONLY";
Query nativeQuery = entityManager.createNativeQuery(sql, Idea.class);
nativeQuery.setParameter(1, keyword+":*");
nativeQuery.setParameter(2, pageable.getOffset());
nativeQuery.setParameter(3, pageable.getPageSize());
List<Idea> responses = nativeQuery.getResultList();

return new PageImpl<>(responses.stream()
.map(idea -> new IdeaListResponse(
idea.getId(),
idea.getUsers().getNickname(),
idea.getTitle(),
idea.getContent(),
idea.getImageName(),
idea.getAuctionStatus(),
idea.getMinimumStartingPrice(),
idea.getBidWinPrice()
))
.collect(Collectors.toList()),
pageable, 0);

}

private BooleanExpression ideaTitleContains(String keyword) {
return isEmpty(keyword) ? null : qIdea.title.contains(keyword);
}

private BooleanExpression test(String keyword) {
return Expressions.booleanTemplate("function('full_text_search', {0}, {1})",
qIdea.title, keyword);
}

private BooleanExpression ideaCategoryEq(Category category) {
return isEmpty(category) ? null : qIdea.category.eq(category);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,15 @@
import com.bid.idearush.global.util.RedisUtil;
import com.bid.idearush.global.util.S3Service;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.bid.idearush.global.type.ServerIpAddress.IMAGE_BASE_PATH;

Expand Down Expand Up @@ -61,13 +59,27 @@ public Page<IdeaListResponse> findAllIdea(String keyword, Category category, Int
Sort sort = Sort.by(Sort.Direction.ASC, "createdAt");
Pageable pageable = PageRequest.of(page, 10, sort);

long count = getCount();
long count = getIdeaCount();

if (!StringUtils.hasText(keyword) && Objects.isNull(category)) {
findList = ideaRepository.findIdeaAll(pageable, count);
} else if (!Objects.isNull(category)) {
findList = ideaRepository.findCategory(pageable, category, getCategoryCount(category));
} else {
findList = ideaRepository.findCategoryAndTitleAll(category, keyword, pageable, count);
// findList = ideaRepository.findByKeyword(keyword, pageable)
// .map(idea -> new IdeaListResponse(
// idea.getId(),
// idea.getUsers().getNickname(),
// idea.getTitle(),
// idea.getContent(),
// idea.getImageName(), // 혹시 이미지 URL 조합이 필요하다면 여기에 추가
// idea.getAuctionStatus(),
// idea.getMinimumStartingPrice(),
// idea.getBidWinPrice()
// ));
findList = ideaRepository.findTitle(pageable,keyword);
}

return findList;
}

Expand Down Expand Up @@ -102,7 +114,6 @@ public void createIdea(IdeaRequest ideaRequest, MultipartFile image, Long userId
}
imageName = image.getOriginalFilename();
}
redisUtil.setIdeaCount(getCount() + 1);

Idea newIdea = ideaRequest.toIdea(user, imageName);
ideaRepository.save(newIdea);
Expand All @@ -112,6 +123,8 @@ public void createIdea(IdeaRequest ideaRequest, MultipartFile image, Long userId
newIdea.changeImage(uploadPath + "/" + imageName);
s3Service.upload(uploadPath, imageName, image);
}
redisUtil.addIdeaCount();
redisUtil.addCategoryCount(ideaRequest.category());
}

public void deleteIdea(Long userId, Long ideaId) {
Expand All @@ -120,9 +133,10 @@ public void deleteIdea(Long userId, Long ideaId) {

validateUser(userId, idea);

redisUtil.setIdeaCount(getCount() - 1);
s3Service.delete(filePath);
ideaRepository.delete(idea);
redisUtil.minIdeaCount();
redisUtil.minCategoryCount(idea.getCategory());
}

private boolean isMultipartFile(MultipartFile multipartFile) {
Expand Down Expand Up @@ -153,7 +167,7 @@ private void validateUser(Long userId, Idea idea) {
}
}

private Long getCount() {
private Long getIdeaCount() {
Long count = redisUtil.getIdeaCount();
if (Objects.isNull(count)) {
count = ideaRepository.count();
Expand All @@ -162,4 +176,16 @@ private Long getCount() {
return count;
}

private Long getCategoryCount(Category category) {
Long count = redisUtil.getCategoryCount(category);
System.out.println("redis : " + count);
if (Objects.isNull(count)) {
count = ideaRepository.countByCategory(category);
System.out.println("category : " + category);
System.out.println("category : " + count);
redisUtil.setCategoryCount(category, count);
}
return count;
}

}
19 changes: 0 additions & 19 deletions src/main/java/com/bid/idearush/global/config/QuerydslConfig.java

This file was deleted.

33 changes: 28 additions & 5 deletions src/main/java/com/bid/idearush/global/util/RedisUtil.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.bid.idearush.global.util;

import com.bid.idearush.domain.idea.repository.IdeaRepository;
import com.bid.idearush.domain.idea.type.Category;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
Expand All @@ -14,15 +15,37 @@ public class RedisUtil {
private final RedisTemplate redisTemplate;

public Long getIdeaCount() {
Object object = redisTemplate.opsForValue().get("count");
if(Objects.isNull(object)){
Object redisIdea = redisTemplate.opsForValue().get("ideaCount");
if(Objects.isNull(redisIdea)){
return null;
}
return Long.parseLong(redisTemplate.opsForValue().get("count").toString());
return Long.parseLong(redisIdea.toString());
}

public void setIdeaCount(long count) {
redisTemplate.opsForValue().set("count", count);
redisTemplate.opsForValue().set("ideaCount", count);
}
public void addIdeaCount() {
redisTemplate.opsForValue().set("ideaCount", getIdeaCount()+1);
}
public void minIdeaCount() {
redisTemplate.opsForValue().set("ideaCount", getIdeaCount()-1);
}

public Long getCategoryCount(Category category) {
Object redisCategory = redisTemplate.opsForValue().get(category.toString());
if(Objects.isNull(redisCategory)){
return null;
}
return Long.parseLong(redisCategory.toString());
}
public void setCategoryCount(Category category, Long count) {
redisTemplate.opsForValue().set(category.toString(), count);
}
public void addCategoryCount(Category category) {
redisTemplate.opsForValue().set(category.toString(), getCategoryCount(category)+1);
}
public void minCategoryCount(Category category) {
redisTemplate.opsForValue().set(category.toString(), getCategoryCount(category)-1);
}

}

0 comments on commit f07f705

Please sign in to comment.