Skip to content

Commit 3bb4d14

Browse files
authored
Merge pull request #328 from prgrms-web-devcourse-final-project/refactor/recruitment-post/search-fulltext
feat: 모집글 검색 FullText Index 적용
2 parents 9e32929 + 0fe5996 commit 3bb4d14

File tree

4 files changed

+77
-8
lines changed

4 files changed

+77
-8
lines changed

src/main/java/grep/neogulcoder/domain/recruitment/post/repository/RecruitmentPostQueryRepository.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.querydsl.core.BooleanBuilder;
44
import com.querydsl.core.types.dsl.BooleanExpression;
5+
import com.querydsl.core.types.dsl.Expressions;
56
import com.querydsl.jpa.impl.JPAQueryFactory;
67
import grep.neogulcoder.domain.recruitment.post.RecruitmentPost;
78
import grep.neogulcoder.domain.recruitment.post.controller.dto.response.QRecruitmentPostWithStudyInfo;
@@ -71,22 +72,22 @@ public Page<RecruitmentPost> search(Pageable pageable, Category category, StudyT
7172

7273
equalsStudyCategory(category),
7374
equalsStudyType(studyType),
74-
likeContent(keyword)
75+
likeContentFullText(keyword)
7576
)
7677
.offset(pageable.getOffset())
7778
.limit(pageable.getPageSize())
7879
.orderBy(recruitmentPost.createdDate.desc())
7980
.fetch();
8081

81-
Long count = queryFactory.select(recruitmentPost.count())
82+
Long count = queryFactory.select(recruitmentPost.id.count())
8283
.from(recruitmentPost)
8384
.join(study).on(recruitmentPost.studyId.eq(study.id))
8485
.where(
8586
recruitmentPost.activated.isTrue(),
8687

8788
equalsStudyCategory(category),
8889
equalsStudyType(studyType),
89-
likeContent(keyword)
90+
likeContentFullText(keyword)
9091
)
9192
.fetchOne();
9293

@@ -103,15 +104,15 @@ public Page<RecruitmentPost> search(Pageable pageable, Category category, StudyT
103104

104105
equalsStudyCategory(category),
105106
equalsStudyType(studyType),
106-
likeContent(keyword)
107+
likeContentFullText(keyword)
107108
)
108109
.offset(pageable.getOffset())
109110
.limit(pageable.getPageSize())
110111
.orderBy(recruitmentPost.createdDate.desc())
111112
.fetch();
112113

113114

114-
Long count = queryFactory.select(recruitmentPost.count())
115+
Long count = queryFactory.select(recruitmentPost.id.count())
115116
.from(recruitmentPost)
116117
.join(study).on(recruitmentPost.studyId.eq(study.id))
117118
.where(
@@ -120,7 +121,7 @@ public Page<RecruitmentPost> search(Pageable pageable, Category category, StudyT
120121

121122
equalsStudyCategory(category),
122123
equalsStudyType(studyType),
123-
likeContent(keyword)
124+
likeContentFullText(keyword)
124125
)
125126
.fetchOne();
126127

@@ -145,8 +146,19 @@ private BooleanBuilder equalsStudyCategory(Category category) {
145146
return nullSafeBuilder(() -> study.category.eq(category));
146147
}
147148

148-
private BooleanBuilder likeContent(String content) {
149-
return nullSafeBuilder(() -> recruitmentPost.content.contains(content).or(recruitmentPost.subject.contains(content)));
149+
private BooleanBuilder likeContentFullText(String content) {
150+
return nullSafeBuilder(() -> {
151+
if (content == null || content.isBlank()) {
152+
return null;
153+
}
154+
155+
return Expressions.booleanTemplate(
156+
"function('match', {0}, {1}, {2}) > 0",
157+
recruitmentPost.content,
158+
recruitmentPost.subject,
159+
content
160+
);
161+
});
150162
}
151163

152164
private BooleanBuilder nullSafeBuilder(Supplier<BooleanExpression> supplier) {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package grep.neogulcoder.global.config.mysql;
2+
3+
import org.hibernate.dialect.function.StandardSQLFunction;
4+
import org.hibernate.query.ReturnableType;
5+
import org.hibernate.sql.ast.SqlAstTranslator;
6+
import org.hibernate.sql.ast.spi.SqlAppender;
7+
import org.hibernate.sql.ast.tree.SqlAstNode;
8+
import org.hibernate.type.BasicTypeReference;
9+
import org.hibernate.type.SqlTypes;
10+
11+
import java.util.List;
12+
13+
public class FullTextFunction extends StandardSQLFunction {
14+
15+
private static final BasicTypeReference<Double> RETURN_TYPE = new BasicTypeReference<>("double", Double.class, SqlTypes.DOUBLE);
16+
17+
public FullTextFunction(String name) {
18+
super(name, true, RETURN_TYPE);
19+
}
20+
21+
@Override
22+
public void render(SqlAppender sqlAppender, List<? extends SqlAstNode> sqlAstArguments, ReturnableType<?> returnType, SqlAstTranslator<?> translator) {
23+
if (sqlAstArguments.size() != 3) {
24+
throw new IllegalArgumentException("requires exactly 3 arguments");
25+
}
26+
27+
sqlAppender.append("MATCH(");
28+
sqlAstArguments.get(0).accept(translator); // 첫번째 인자: 검색 대상 컬럼
29+
sqlAppender.append(", ");
30+
sqlAstArguments.get(1).accept(translator); // 두번째 인자: 또 다른 컬럼
31+
sqlAppender.append(") AGAINST (");
32+
sqlAstArguments.get(2).accept(translator); // 세번째 인자: 검색 키워드
33+
sqlAppender.append(" IN NATURAL LANGUAGE MODE)");
34+
}
35+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package grep.neogulcoder.global.config.mysql;
2+
3+
import org.hibernate.boot.model.FunctionContributions;
4+
import org.hibernate.boot.model.FunctionContributor;
5+
import org.hibernate.type.StandardBasicTypes;
6+
7+
public class MySqlContributor implements FunctionContributor {
8+
9+
public static final String FUNCTION_NAME = "match";
10+
public static final String FUNCTION_PATTERN = "match (?1, ?2) against (?3 IN NATURAL LANGUAGE MODE)";
11+
12+
@Override
13+
public void contributeFunctions(FunctionContributions functionContributions) {
14+
functionContributions.getFunctionRegistry().registerPattern(
15+
FUNCTION_NAME, FUNCTION_PATTERN,
16+
functionContributions.getTypeConfiguration()
17+
.getBasicTypeRegistry()
18+
.resolve(StandardBasicTypes.DOUBLE)
19+
);
20+
}
21+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
grep.neogulcoder.global.config.mysql.MySqlContributor

0 commit comments

Comments
 (0)