Skip to content

Commit

Permalink
feat: apply builder pattern for dynamic query build
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukinhasssss committed May 26, 2024
1 parent 55b9244 commit d469972
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.lukinhasssss.catalogo.infrastructure.video

import co.elastic.clients.elasticsearch._types.FieldValue
import co.elastic.clients.elasticsearch._types.query_dsl.Query
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders
import com.lukinhasssss.catalogo.domain.pagination.Pagination
import com.lukinhasssss.catalogo.domain.video.Video
import com.lukinhasssss.catalogo.domain.video.VideoGateway
import com.lukinhasssss.catalogo.domain.video.VideoSearchQuery
import com.lukinhasssss.catalogo.infrastructure.video.VideoQueryBuilder.Companion.containingCastMembers
import com.lukinhasssss.catalogo.infrastructure.video.VideoQueryBuilder.Companion.containingCategories
import com.lukinhasssss.catalogo.infrastructure.video.VideoQueryBuilder.Companion.containingGenres
import com.lukinhasssss.catalogo.infrastructure.video.VideoQueryBuilder.Companion.launchedAtEquals
import com.lukinhasssss.catalogo.infrastructure.video.VideoQueryBuilder.Companion.onlyPublished
import com.lukinhasssss.catalogo.infrastructure.video.VideoQueryBuilder.Companion.ratingEquals
import com.lukinhasssss.catalogo.infrastructure.video.VideoQueryBuilder.Companion.titleOrDescriptionContaining
import com.lukinhasssss.catalogo.infrastructure.video.persistence.VideoDocument
import com.lukinhasssss.catalogo.infrastructure.video.persistence.VideoRepository
import org.springframework.context.annotation.Profile
Expand Down Expand Up @@ -39,36 +43,18 @@ class VideoElasticsearchGateway(
if (id.isBlank()) null else videoRepository.findById(id).map { it.toVideo() }.orElse(null)

override fun findAll(aQuery: VideoSearchQuery): Pagination<Video> = with(aQuery) {
val must = mutableListOf<Query>()

must.add(QueryBuilders.term { term -> term.field("published").value(true) })

if (categories.isNotEmpty()) {
must.add(QueryBuilders.terms { terms -> terms.field("categories").terms { it.value(fieldValues(categories)) } })
}

if (castMembers.isNotEmpty()) {
must.add(QueryBuilders.terms { terms -> terms.field("cast_members").terms { it.value(fieldValues(castMembers)) } })
}

if (genres.isNotEmpty()) {
must.add(QueryBuilders.terms { terms -> terms.field("genres").terms { it.value(fieldValues(genres)) } })
}

if (launchedAt != null) {
must.add(QueryBuilders.term { term -> term.field("launched_at").value(launchedAt!!.toLong()) })
}

if (rating.isNullOrBlank().not()) {
must.add(QueryBuilders.term { term -> term.field("rating").value(rating) })
}

if (terms.isNotBlank()) {
must.add(QueryBuilders.queryString { it.fields("title", "description").query("*$terms*") })
}
val aQueryBuilder = VideoQueryBuilder(
onlyPublished(),
containingCategories(categories),
containingCastMembers(castMembers),
containingGenres(genres),
launchedAtEquals(launchedAt),
ratingEquals(rating),
titleOrDescriptionContaining(terms)
).build()

val query = NativeQuery.builder()
.withQuery(QueryBuilders.bool { bool -> bool.must(must) })
.withQuery(aQueryBuilder)
.withPageable(PageRequest.of(page, perPage, Sort.by(Direction.fromString(direction), buildSort(sort))))
.build()

Expand All @@ -87,7 +73,5 @@ class VideoElasticsearchGateway(

override fun deleteById(id: String) = if (id.isBlank()) Unit else videoRepository.deleteById(id)

private fun fieldValues(ids: Set<String>): List<FieldValue> = ids.map { FieldValue.of(it) }

private fun buildSort(sort: String) = if (TITLE_PROP.equals(sort, ignoreCase = true)) sort.plus(KEYWORD) else sort
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.lukinhasssss.catalogo.infrastructure.video

import co.elastic.clients.elasticsearch._types.FieldValue
import co.elastic.clients.elasticsearch._types.query_dsl.Query
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders
import com.lukinhasssss.catalogo.infrastructure.video.VideoQueryBuilder.Option

class VideoQueryBuilder(vararg opts: Option) {
private val must: MutableList<Query> = mutableListOf()

init {
opts.forEach { it.invoke(this) }
}

fun must(aQuery: Query): VideoQueryBuilder {
must.add(aQuery)
return this
}

fun build(): Query = QueryBuilders.bool { b -> b.must(must) }

fun interface Option : (VideoQueryBuilder) -> Unit

companion object {
private val NOOP: Option = Option { }

fun onlyPublished(): Option = Option { b -> b.must(QueryBuilders.term { t -> t.field("published").value(true) }) }

fun containingCategories(categories: Set<String>): Option {
if (categories.isEmpty()) {
return NOOP
}

return Option { b -> b.must(QueryBuilders.terms { t -> t.field("categories").terms { it.value(fieldValues(categories)) } }) }
}

fun containingCastMembers(members: Set<String>): Option {
if (members.isEmpty()) {
return NOOP
}

return Option { b -> b.must(QueryBuilders.terms { t -> t.field("cast_members").terms { it.value(fieldValues(members)) } }) }
}

fun containingGenres(genres: Set<String>): Option {
if (genres.isEmpty()) {
return NOOP
}

return Option { b -> b.must(QueryBuilders.terms { t -> t.field("genres").terms { it.value(fieldValues(genres)) } }) }
}

fun launchedAtEquals(launchedAt: Int?): Option {
if (launchedAt == null) {
return NOOP
}

return Option { b -> b.must(QueryBuilders.term { t -> t.field("launched_at").value(launchedAt.toLong()) }) }
}

fun ratingEquals(rating: String?): Option {
if (rating.isNullOrBlank()) {
return NOOP
}

return Option { b -> b.must(QueryBuilders.term { t -> t.field("rating").value(rating) }) }
}

fun titleOrDescriptionContaining(terms: String?): Option {
if (terms.isNullOrBlank()) {
return NOOP
}

return Option { b -> b.must(QueryBuilders.queryString { q -> q.fields("title", "description").query("*$terms*") }) }
}

private fun fieldValues(ids: Set<String>): List<FieldValue> = ids.map { FieldValue.of(it) }
}
}

0 comments on commit d469972

Please sign in to comment.