📦 Database/QueryDSL
간단한 조건 검색 (필터링, 상세검색) 기능 만들기 (Kotlin, QueryDSL)
신건우
2023. 6. 25. 18:44
검색 기능 만들기
QueryDSL의 Boolean Builder와 OrderSpecifier를 활용한 검색기능을 만들어 보았습니다.
요구사항
- 필터링: 와인 종류, 알코올 도수 범위, 와인의 가격 범위, 와인의 스타일, 와인의 등급, 지역
- 정렬: 와인 이름, 알코올 도수, 산도, 바디감, 단맛, 타닌, 와인의 점수, 와인의 가격
- 검색: 와인 이름
class CustomWineRepositoryImpl(
@Autowired private val queryFactory: JPAQueryFactory
) : QuerydslRepositorySupport(Wine::class.java), CustomWineRepository {
override fun findSingleWineByCondition(filter: FilterDTO.Filter?, sort: FilterDTO.Sort?, name: String?): Tuple? {
val builder = BooleanBuilder()
// Type 필터링
if (!filter?.type.isNullOrEmpty()) builder.and(wine.type.eq(filter?.type))
// Alcohol 범위 필터링
if (filter?.minAlcohol != null && filter.maxAlcohol != null && filter.minAlcohol!! >= 0.0 && filter.maxAlcohol!! <= 20.0) {
builder.and(wine.alcohol.between(filter.minAlcohol, filter.maxAlcohol))
} else if (filter?.minAlcohol != null) {
builder.and(wine.alcohol.goe(filter.minAlcohol))
} else if (filter?.maxAlcohol != null) {
builder.and(wine.alcohol.loe(filter.maxAlcohol))
}
// Price 범위 필터링
if (filter?.minPrice != null && filter.maxPrice != null && filter.minPrice!! >= 0 && filter.maxPrice!! <= 1000000) {
builder.and(wine.price.between(filter.minPrice, filter.maxPrice))
} else if (filter?.minPrice != null) {
builder.and(wine.price.goe(filter.minPrice))
} else if (filter?.maxPrice != null) {
builder.and(wine.price.loe(filter.maxPrice))
}
// Style 필터링
if (!filter?.style.isNullOrEmpty()) {
builder.and(wine.style.eq(filter?.style))
}
// Grade 필터링
if (!filter?.grade.isNullOrEmpty()) {
builder.and(wine.grade.eq(filter?.grade))
}
// Region 필터링
if (!filter?.region.isNullOrEmpty()) {
builder.and(wine.region.name.eq(filter?.region))
}
// 정렬 조건
val orderByExpressions = mutableListOf<com.querydsl.core.types.OrderSpecifier<*>>()
when {
!sort?.name.isNullOrEmpty() -> orderByExpressions.add(wine.name.desc())
sort?.alcohol != null -> orderByExpressions.add(wine.alcohol.desc())
sort?.acidity != null -> orderByExpressions.add(wine.acidity.desc())
sort?.body != null -> orderByExpressions.add(wine.body.desc())
sort?.sweetness != null -> orderByExpressions.add(wine.sweetness.desc())
sort?.tannin != null -> orderByExpressions.add(wine.tannin.desc())
sort?.score != null -> orderByExpressions.add(wine.score.desc())
sort?.price != null -> orderByExpressions.add(wine.price.desc())
}
// Name 검색어
if (!name.isNullOrEmpty()) {
builder.and(wine.name.containsIgnoreCase(name))
}
return queryFactory
.select(
wine.type,
wine.name,
wine.alcohol,
wine.acidity,
wine.body,
wine.sweetness,
wine.tannin,
wine.price,
wine.style,
wine.grade,
wine.importer.name,
wine.wineryName,
wine.region.name,
wine.region.parentName,
wine.aroma,
wine.pairing,
wine.wineGrape
)
.from(wine)
.innerJoin(wine.aroma).fetchJoin()
.innerJoin(wine.wineGrape).fetchJoin()
.innerJoin(wine.pairing).fetchJoin()
.innerJoin(wine.region).fetchJoin()
.innerJoin(wine.importer).fetchJoin()
.where(builder)
.orderBy(*orderByExpressions.toTypedArray())
.fetchOne()
}
override fun findMultiWineByCondition(filter: FilterDTO.Filter?, sort: FilterDTO.Sort?, name: String?): MutableList<Tuple>? {
val builder = BooleanBuilder()
// Type 필터링
if (!filter?.type.isNullOrEmpty()) builder.and(wine.type.eq(filter?.type))
// Alcohol 범위 필터링
if (filter?.minAlcohol != null && filter.maxAlcohol != null && filter.minAlcohol!! >= 0.0 && filter.maxAlcohol!! <= 20.0) {
builder.and(wine.alcohol.between(filter.minAlcohol, filter.maxAlcohol))
} else if (filter?.minAlcohol != null) {
builder.and(wine.alcohol.goe(filter.minAlcohol))
} else if (filter?.maxAlcohol != null) {
builder.and(wine.alcohol.loe(filter.maxAlcohol))
}
// Price 범위 필터링
if (filter?.minPrice != null && filter.maxPrice != null && filter.minPrice!! >= 0 && filter.maxPrice!! <= 1000000) {
builder.and(wine.price.between(filter.minPrice, filter.maxPrice))
} else if (filter?.minPrice != null) {
builder.and(wine.price.goe(filter.minPrice))
} else if (filter?.maxPrice != null) {
builder.and(wine.price.loe(filter.maxPrice))
}
// Style 필터링
if (!filter?.style.isNullOrEmpty()) {
builder.and(wine.style.eq(filter?.style))
}
// Grade 필터링
if (!filter?.grade.isNullOrEmpty()) {
builder.and(wine.grade.eq(filter?.grade))
}
// Region 필터링
if (!filter?.region.isNullOrEmpty()) {
builder.and(wine.region.name.eq(filter?.region))
}
// 정렬 조건
val orderByExpressions = mutableListOf<com.querydsl.core.types.OrderSpecifier<*>>()
when {
!sort?.name.isNullOrEmpty() -> orderByExpressions.add(wine.name.desc())
sort?.alcohol != null -> orderByExpressions.add(wine.alcohol.desc())
sort?.acidity != null -> orderByExpressions.add(wine.acidity.desc())
sort?.body != null -> orderByExpressions.add(wine.body.desc())
sort?.sweetness != null -> orderByExpressions.add(wine.sweetness.desc())
sort?.tannin != null -> orderByExpressions.add(wine.tannin.desc())
sort?.score != null -> orderByExpressions.add(wine.score.desc())
sort?.price != null -> orderByExpressions.add(wine.price.desc())
}
// Name 검색어
if (!name.isNullOrEmpty()) {
builder.and(wine.name.containsIgnoreCase(name))
}
return queryFactory
.select(
wine.type,
wine.name,
wine.region.name
)
.from(wine)
.innerJoin(wine.region).fetchJoin()
.where(builder)
.orderBy(*orderByExpressions.toTypedArray())
.fetch()
}
}