Skip to content

Commit

Permalink
Create a separate function for domain filtered search
Browse files Browse the repository at this point in the history
  • Loading branch information
TrulsStenrud committed Aug 29, 2023
1 parent 4519357 commit b6490d4
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 47 deletions.
93 changes: 53 additions & 40 deletions src/main/kotlin/no/liflig/documentstore/dao/Dao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -254,56 +254,57 @@ abstract class AbstractSearchRepository<I, A, Q>(
offset: Int? = null,
orderBy: String? = null,
orderDesc: Boolean = false,
domainFilter: ((A) -> Boolean)? = null,
bind: Query.() -> Query = { this }
): List<VersionedEntity<A>> = mapExceptions {
val transaction = transactionHandle.get()
inTransaction(jdbi) { handle ->
val orderDirection = if (orderDesc) "DESC" else "ASC"
val orderByString = orderBy ?: "created_at"

if (transaction != null) {
innerGetByPredicate(sqlWhere, transaction, limit, offset, orderBy, orderDesc, domainFilter, bind)
} else {
jdbi.open().use { handle ->
innerGetByPredicate(sqlWhere, handle, limit, offset, orderBy, orderDesc, domainFilter, bind)
}
val limitString = limit?.let { "LIMIT $it" } ?: ""
val offsetString = offset?.let { "OFFSET $it" } ?: ""

handle
.select(
"""
SELECT id, data, version, created_at, modified_at
FROM "$sqlTableName"
WHERE ($sqlWhere)
ORDER BY $orderByString $orderDirection
$limitString
$offsetString
""".trimIndent()
)
.bind()
.map(rowMapper)
.list()
}
}

private fun innerGetByPredicate(
sqlWhere: String,
handle: Handle,
protected open fun getByPredicateDomainFiltered(
sqlWhere: String = "TRUE",
limit: Int? = null,
offset: Int? = null,
orderBy: String? = null,
desc: Boolean = false,
domainFilter: ((A) -> Boolean)? = null,
orderDesc: Boolean = false,
domainFilter: (A) -> Boolean = { true },
bind: Query.() -> Query = { this }
): List<VersionedEntity<A>> {

val orderDirection = if (desc) "DESC" else "ASC"
val orderByString = orderBy ?: "created_at"

// only use if domainFilter is null
val limitString = limit?.let { "LIMIT $it" }?.takeIf { domainFilter == null } ?: ""
val offsetString = offset?.let { "OFFSET $it" }?.takeIf { domainFilter == null } ?: ""
): List<VersionedEntity<A>> = mapExceptions {
inTransaction(jdbi) { handle ->
val orderDirection = if (orderDesc) "DESC" else "ASC"
val orderByString = orderBy ?: "created_at"

val result = handle
.select(
"""
handle
.select(
"""
SELECT id, data, version, created_at, modified_at
FROM "$sqlTableName"
WHERE ($sqlWhere)
ORDER BY $orderByString $orderDirection
$limitString
$offsetString
""".trimIndent()
)
.bind()
.map(rowMapper)

return if (domainFilter == null)
result.list()
else {
result.asSequence()
""".trimIndent()
)
.bind()
.map(rowMapper)
.asSequence()
.filter { domainFilter(it.item) }
.run { offset?.let { drop(it) } ?: this }
.run { limit?.let { take(it) } ?: this }
Expand All @@ -312,32 +313,44 @@ abstract class AbstractSearchRepository<I, A, Q>(
}
}

abstract class QueryObject<A> {
abstract class QueryObject {
open val sqlWhere: String = "TRUE"
open val bindSqlParameters: Query.() -> Query = { this } // Default no-op
open val limit: Int? = null
open val offset: Int? = null
open val orderBy: String? = null
open val orderDesc: Boolean = false
open val domainFilter: ((A) -> Boolean)? = null
}

class SearchRepositoryJdbi<I, A, Q>(
jdbi: Jdbi,
sqlTableName: String,
serializationAdapter: SerializationAdapter<A>,
) : AbstractSearchRepository<I, A, Q>(jdbi, sqlTableName, serializationAdapter) where I : EntityId,
A : EntityRoot<I>,
Q : QueryObject<A> {
A : EntityRoot<I>,
Q : QueryObject {
override fun search(query: Q): List<VersionedEntity<A>> = getByPredicate(
sqlWhere = query.sqlWhere,
limit = query.limit,
offset = query.offset,
orderBy = query.orderBy,
orderDesc = query.orderDesc,
domainFilter = query.domainFilter,
bind = query.bindSqlParameters,
)

/**
* A slightly slower version of [search], but with the possibility to filter based on the domain entities.
*/
fun searchDomainFiltered(query: Q, domainFilter: (A) -> Boolean): List<VersionedEntity<A>> =
getByPredicateDomainFiltered(
sqlWhere = query.sqlWhere,
limit = query.limit,
offset = query.offset,
orderBy = query.orderBy,
orderDesc = query.orderDesc,
domainFilter = domainFilter,
bind = query.bindSqlParameters,
)
}

/**
Expand Down
12 changes: 12 additions & 0 deletions src/main/kotlin/no/liflig/documentstore/dao/Transactional.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ import java.lang.Exception

val transactionHandle = ThreadLocal<Handle?>()

internal fun <T> inTransaction(jdbi: Jdbi, useHandle: (Handle) -> T): T {
val transactionHandle = transactionHandle.get()

return if (transactionHandle != null) {
useHandle(transactionHandle)
} else {
jdbi.open().use { handle ->
useHandle(handle)
}
}
}

fun <T> transactional(jdbi: Jdbi, block: () -> T): T {
val existingHandle = transactionHandle.get()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SearchRepositoryTest {
val mockAdapter: ExampleSerializationAdapter = mockk {
every { fromJson(any()) } returns createEntity("")
}
val searchRepositoryWithMock = SearchRepositoryJdbi(jdbi, "example", mockAdapter)
val searchRepositoryWithMock = SearchRepositoryJdbi<ExampleId, ExampleEntity, ExampleQuery>(jdbi, "example", mockAdapter)

@BeforeEach
fun clearDatabase() {
Expand Down Expand Up @@ -104,13 +104,12 @@ class SearchRepositoryTest {
dao.create(createEntity("this-D"))

val result =
searchRepository.search(
searchRepository.searchDomainFiltered(
ExampleQuery(
offset = 1,
limit = 2,
domainFilter = { it.text.startsWith("this") }
)
)
) { it.text.startsWith("this") }
.map { it.item.text }

result shouldHaveSize 2
Expand Down Expand Up @@ -167,7 +166,7 @@ class SearchRepositoryTest {
dao.create(createEntity("B"))
dao.create(createEntity("C"))

val result = searchRepository.search(ExampleQuery(domainFilter = { it.text == "B" }))
val result = searchRepository.searchDomainFiltered(ExampleQuery()) { it.text == "B" }

result shouldHaveSize 1
result.first().item.text shouldBeEqual "B"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ package no.liflig.documentstore.examples

import no.liflig.documentstore.dao.QueryObject
import org.jdbi.v3.core.statement.Query
import java.awt.SystemColor.text

class ExampleQuery(
override val limit: Int? = null,
override val offset: Int? = null,
override val orderBy: String? = null,
override val orderDesc: Boolean = false,
override val domainFilter: ((ExampleEntity) -> Boolean)? = null,

val text: String? = null,

) : QueryObject<ExampleEntity>() {
) : QueryObject() {
override val sqlWhere: String = ":wildcardQuery IS NULL OR data->>'text' ILIKE :wildcardQuery "
override val bindSqlParameters: Query.() -> Query = { bind("wildcardQuery", text?.let { "%$it%" }) }
}

0 comments on commit b6490d4

Please sign in to comment.