-
Notifications
You must be signed in to change notification settings - Fork 4
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
카페 검색 기능 개발 #443
카페 검색 기능 개발 #443
Changes from all commits
f0d02b2
dd80429
b4e29c1
f86685e
1d74670
d294873
9e22d9f
dfb99f9
5f80dd5
c18154a
00185fa
20c4c88
2fab8e0
806b0f8
c0f46c5
022b301
3ad24e4
e8d3325
595cbba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.project.yozmcafe.config; | ||
|
||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
|
||
import jakarta.persistence.EntityManager; | ||
import jakarta.persistence.PersistenceContext; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class QueryDslConfig { | ||
|
||
@PersistenceContext | ||
private EntityManager entityManager; | ||
|
||
@Bean | ||
public JPAQueryFactory jpaQueryFactory() { | ||
return new JPAQueryFactory(entityManager); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.project.yozmcafe.controller.dto.cafe; | ||
|
||
public record CafeSearchRequest(String cafeName, String menu, String address) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.project.yozmcafe.controller.dto.cafe; | ||
|
||
import com.project.yozmcafe.domain.cafe.Cafe; | ||
|
||
public record CafeSearchResponse(Long id, String name, String address, String image, int likeCount) { | ||
public static CafeSearchResponse from(final Cafe cafe) { | ||
return new CafeSearchResponse( | ||
cafe.getId(), | ||
cafe.getName(), | ||
cafe.getAddress(), | ||
cafe.getRepresentativeImage(), | ||
cafe.getLikeCount() | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.project.yozmcafe.controller.dto.cafe; | ||
|
||
import com.project.yozmcafe.domain.cafe.Cafe; | ||
|
||
public record CafeThumbnailResponse(Long cafeId, String imageUrl) { | ||
|
||
public static CafeThumbnailResponse from(final Cafe cafe) { | ||
return new CafeThumbnailResponse(cafe.getId(), cafe.getRepresentativeImage()); | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.project.yozmcafe.domain.cafe; | ||
|
||
import java.util.List; | ||
|
||
public interface CafeCustomRepository { | ||
|
||
List<Cafe> findAllBy(final String cafeName, final String menu, final String address); | ||
|
||
List<Cafe> findAllBy(final String cafeName, final String address); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.project.yozmcafe.domain.cafe; | ||
|
||
import com.querydsl.core.types.dsl.BooleanExpression; | ||
import com.querydsl.core.types.dsl.StringPath; | ||
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.util.List; | ||
|
||
import static com.project.yozmcafe.domain.cafe.QCafe.cafe; | ||
import static com.project.yozmcafe.domain.menu.QMenu.menu; | ||
import static com.querydsl.core.types.dsl.Expressions.numberTemplate; | ||
import static io.micrometer.common.util.StringUtils.isBlank; | ||
|
||
@Repository | ||
public class CafeCustomRepositoryImpl extends QuerydslRepositorySupport implements CafeCustomRepository { | ||
|
||
private static final double MATCH_THRESHOLD = 0.0; | ||
|
||
public CafeCustomRepositoryImpl() { | ||
super(Cafe.class); | ||
} | ||
|
||
public List<Cafe> findAllBy(final String cafeNameWord, final String menuWord, final String addressWord) { | ||
return from(cafe) | ||
.innerJoin(menu).on(menu.cafe.eq(cafe)) | ||
.where( | ||
contains(cafe.name, cafeNameWord), | ||
contains(menu.name, menuWord), | ||
contains(cafe.address, addressWord)) | ||
.fetch(); | ||
} | ||
|
||
public List<Cafe> findAllBy(final String cafeNameWord, final String addressWord) { | ||
return from(cafe) | ||
.where( | ||
contains(cafe.name, cafeNameWord), | ||
contains(cafe.address, addressWord)) | ||
.fetch(); | ||
} | ||
|
||
private BooleanExpression contains(final StringPath target, final String searchWord) { | ||
if (isBlank(searchWord)) { | ||
return null; | ||
} | ||
|
||
final String formattedSearchWord = "\"" + searchWord + "\""; | ||
return numberTemplate(Double.class, "function('match_against', {0}, {1})", | ||
target, formattedSearchWord) | ||
.gt(MATCH_THRESHOLD); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ | |
import java.util.List; | ||
import java.util.Optional; | ||
|
||
public interface CafeRepository extends JpaRepository<Cafe, Long> { | ||
public interface CafeRepository extends JpaRepository<Cafe, Long>, CafeCustomRepository { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 이런 구조를 생각했었는데 반영 👍 |
||
|
||
Slice<Cafe> findSliceBy(Pageable pageable); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.project.yozmcafe.domain.cafe; | ||
|
||
import org.hibernate.boot.model.FunctionContributions; | ||
import org.hibernate.boot.model.FunctionContributor; | ||
|
||
import static org.hibernate.type.StandardBasicTypes.DOUBLE; | ||
|
||
public class CustomFunctionContributor implements FunctionContributor { | ||
|
||
private static final String FUNCTION_NAME = "match_against"; | ||
private static final String FUNCTION_PATTERN = "match (?1) against (?2 in boolean mode)"; | ||
|
||
Comment on lines
+8
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RepositoryImpl에서 match against 문을 바로 쓰지 않고 함수화하는 이유가 궁금합니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 가능하면 특화구문을 최소한의 의존성으로 사용하고 싶어요!
이런 식으로 match against문을 바로 쓰는 것을 생각하신 게 맞나요? 그렇다고 native query사용을 위해 entityManager를 직접 사용하면 querydsl을 통한 동적쿼리의 의미가 없어지는 것 같고.... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. query dsl은 말 그대로 DSL로 편하게 동적 쿼리를 작성하기 위함이고 native query를 사용하는 것은 피할 수 없다고 생각해요. 근데 만약 여기서 Entity Manager를 사용하면 좀 더 추상객체(Entity Manager)의 의존만으로 처리할 수 있지 않나 해서 남겼어요! native query가 필요할 때마다 native query가 아닌 Query DSL로 하실 것인가요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 Entity Manager로 변경하라는 뜻은 아닙니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. querydsl의 where에 native쿼리가 들어가야 처음 querydsl을 쓸 때 의도했던 대로 Entity Manager를 쓰면서 이를 querydsl에 적용하려면 EntityManager의 쿼리 형식을 booleanExpression으로 바꿔야 하는 데 그게 가능한지 모르겠네요... |
||
@Override | ||
public void contributeFunctions(final FunctionContributions functionContributions) { | ||
functionContributions.getFunctionRegistry() | ||
.registerPattern(FUNCTION_NAME, FUNCTION_PATTERN, | ||
functionContributions.getTypeConfiguration().getBasicTypeRegistry().resolve(DOUBLE)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
com.project.yozmcafe.domain.cafe.CustomFunctionContributor | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 함수를 등록하려면 이 부분이 필요한 건가요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
ALTER TABLE cafe ADD FULLTEXT INDEX cafe_name_idx (name) WITH PARSER NGRAM; | ||
|
||
ALTER TABLE cafe ADD FULLTEXT INDEX address_idx (address) WITH PARSER NGRAM; | ||
|
||
ALTER TABLE menu ADD FULLTEXT INDEX menu_name_idx (name) WITH PARSER NGRAM; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아직 쿼리 dsl을 학습하지 않아서 잘은 모르겠지만 제가 이번에 근로 학습을 하면서 봤던 것은 지금처럼 cafeNameWord, menuWord, addressWord에 따라 메소드 오버로딩을 할 필요 없이 한 메소드를 통해서 진행할 수 있었는데 지금은 불가한 것일까요 ?
왜 오버로딩했는지가 궁금합니다! 단순하게 leftJoin, innerJoin 때문에 분리한건지도요 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오버로딩 했던 이유가 menu검색을 하지 않을 경우에는 cafe엔티티만으로 검색이 가능해서에요!
그래서 분기문으로 menu검색이 없을 때는 불필요한 조인을 하지 않도록 하려 했습니다.
그런데 불필요한 join이라면 db측의 옵티마이저가 알아서 최적화해주지 않을까? 하는 생각도 들어서 50000개 정도의 카페에 메뉴2개씩 저장해서 테스트 해봤어요
알아서 최적화 해주지는 않네요.. join시 메뉴 개수만큼 곱해져서 조회되기에 그만큼의 성능 차이가 납니다.
결론은 leftjoin,inner join이냐의 문제보다는 join이 필요없을 때는 안하는게 게 성능 상 좋아서 분리하려해요!