Skip to content

Commit

Permalink
draft - separate _id filter queries
Browse files Browse the repository at this point in the history
  • Loading branch information
LZRS committed Nov 8, 2024
1 parent 414c777 commit 569a24f
Show file tree
Hide file tree
Showing 15 changed files with 151 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -229,49 +229,54 @@ internal class DatabaseImpl(
override suspend fun <R : Resource> search(
query: SearchQuery,
): List<ResourceWithUUID<R>> {
return db.withTransaction {
resourceDao.getResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray())).pmap(
Dispatchers.Default,
) {
ResourceWithUUID(
it.uuid,
FhirContext.forR4Cached().newJsonParser().parseResource(it.serializedResource) as R,
)
val dbResult =
db.withTransaction {
resourceDao.getResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray()))
}

return dbResult.pmap(
Dispatchers.Default,
) {
ResourceWithUUID(
it.uuid,
FhirContext.forR4Cached().newJsonParser().parseResource(it.serializedResource) as R,
)
}
}

override suspend fun searchForwardReferencedResources(
query: SearchQuery,
): List<ForwardIncludeSearchResult> {
return db.withTransaction {
resourceDao
.getForwardReferencedResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray()))
.pmap(Dispatchers.Default) {
ForwardIncludeSearchResult(
it.matchingIndex,
it.baseResourceUUID,
FhirContext.forR4Cached().newJsonParser().parseResource(it.serializedResource)
as Resource,
)
}
val dbResult =
db.withTransaction {
resourceDao.getForwardReferencedResources(
SimpleSQLiteQuery(query.query, query.args.toTypedArray()),
)
}
return dbResult.pmap(Dispatchers.Default) {
ForwardIncludeSearchResult(
it.matchingIndex,
it.baseResourceUUID,
FhirContext.forR4Cached().newJsonParser().parseResource(it.serializedResource) as Resource,
)
}
}

override suspend fun searchReverseReferencedResources(
query: SearchQuery,
): List<ReverseIncludeSearchResult> {
return db.withTransaction {
resourceDao
.getReverseReferencedResources(SimpleSQLiteQuery(query.query, query.args.toTypedArray()))
.pmap(Dispatchers.Default) {
ReverseIncludeSearchResult(
it.matchingIndex,
it.baseResourceTypeAndId,
FhirContext.forR4Cached().newJsonParser().parseResource(it.serializedResource)
as Resource,
)
}
val dbResult =
db.withTransaction {
resourceDao.getReverseReferencedResources(
SimpleSQLiteQuery(query.query, query.args.toTypedArray()),
)
}
return dbResult.pmap(Dispatchers.Default) {
ReverseIncludeSearchResult(
it.matchingIndex,
it.baseResourceTypeAndId,
FhirContext.forR4Cached().newJsonParser().parseResource(it.serializedResource) as Resource,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import org.hl7.fhir.r4.model.ResourceType
// need for accessing underlying table data.
// Column ordered to minimise time to run sortJoinStatement in [MoreSearch], and to resolve:
// https://github.com/google/android-fhir/issues/2040
Index(value = ["index_from", "index_to", "index_name", "resourceType", "resourceUuid"]),
Index(value = ["index_name", "resourceType", "index_from", "resourceUuid"]),
Index(value = ["index_name", "resourceType", "index_to", "resourceUuid"]),
// Keep this index for faster foreign lookup
Index(value = ["resourceUuid"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import org.hl7.fhir.r4.model.ResourceType
// need for accessing underlying table data.
// Column ordered to minimise time to run sortJoinStatement in [MoreSearch], and to resolve:
// https://github.com/google/android-fhir/issues/2040
Index(value = ["index_from", "index_to", "index_name", "resourceType", "resourceUuid"]),
Index(value = ["index_name", "resourceType", "index_from", "resourceUuid"]),
Index(value = ["index_name", "resourceType", "index_to", "resourceUuid"]),
// Keep this index for faster foreign lookup
Index(value = ["resourceUuid"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import org.hl7.fhir.r4.model.ResourceType
@Entity(
indices =
[
Index(value = ["index_value", "index_name", "resourceType"]),
Index(value = ["index_name", "resourceType", "index_value", "resourceUuid"]),
// keep this index for faster foreign lookup
Index(value = ["resourceUuid"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import org.hl7.fhir.r4.model.ResourceType
@Entity(
indices =
[
Index(value = ["index_latitude", "index_longitude", "resourceType"]),
Index(value = ["resourceType", "index_longitude", "resourceUuid"]),
Index(value = ["resourceType", "index_latitude", "resourceUuid"]),
// keep this index for faster foreign lookup
Index(value = ["resourceUuid"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ import org.hl7.fhir.r4.model.ResourceType
@Entity(
indices =
[
Index(value = ["index_value", "index_name", "resourceType", "index_code"]),
Index(value = ["index_name", "resourceType", "index_value", "resourceUuid"]),
Index(value = ["index_name", "resourceType", "index_code", "index_system", "index_value", "resourceUuid"]),
Index(value = ["index_name", "resourceType", "index_code", "index_value", "resourceUuid"]),
Index(value = ["index_name", "resourceType", "index_system", "index_value", "resourceUuid"]),
// keep this index for faster foreign lookup
Index(value = ["resourceUuid"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import org.hl7.fhir.r4.model.ResourceType
@Entity(
indices =
[
Index(value = ["index_value", "index_name", "resourceType"]),
Index(value = ["index_value", "index_name", "resourceType", "resourceUuid"]),
// keep this index for faster foreign lookup
Index(value = ["resourceUuid"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import org.hl7.fhir.r4.model.ResourceType
@Entity(
indices =
[
Index(value = ["index_value", "index_name", "resourceType"]),
Index(value = ["index_value", "index_name", "resourceType", "resourceUuid"]),
// keep this index for faster foreign lookup
Index(value = ["resourceUuid"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.hl7.fhir.r4.model.ResourceType
indices =
[
Index(value = ["index_value", "index_name", "resourceType", "index_system", "resourceUuid"]),
Index(value = ["index_value", "index_name", "resourceType", "resourceUuid"]),
// Keep this index for faster foreign lookup
Index(value = ["resourceUuid"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import org.hl7.fhir.r4.model.ResourceType
@Entity(
indices =
[
Index(value = ["index_value", "index_name", "resourceType"]),
Index(value = ["index_value", "index_name", "resourceType", "resourceUuid"]),
// keep this index for faster foreign lookup
Index(value = ["resourceUuid"]),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ internal fun Search.getRevIncludeQuery(includeIds: List<String>): SearchQuery {
}
return filterQuery
}
// TODO("id filter queries")

return revIncludes
.map {
Expand Down Expand Up @@ -202,6 +203,8 @@ internal fun Search.getIncludeQuery(includeIds: List<UUID>): SearchQuery {
return filterQuery
}

// TODO("id filter queries")

return forwardIncludes
.map {
val (join, order) =
Expand Down Expand Up @@ -354,6 +357,8 @@ private fun Search.getFilterQueries() =
uriFilterCriteria)
.map { it.query(type) }

private fun Search.getIdFilterQueries() = idTokenFilterCriteria.map { it.query(type) }

internal fun Search.getQuery(
isCount: Boolean = false,
nestedContext: NestedContext? = null,
Expand All @@ -379,6 +384,22 @@ internal fun Search.getQuery(
filterArgs.addAll(it.args)
}

var filterIdStatement = ""
val filterIdArgs = mutableListOf<Any>()
val filterIdQuery = getIdFilterQueries()
filterIdQuery.forEachIndexed { i, it ->
filterIdStatement +=
// spotless:off
"""
${if (i == 0) "AND a.resourceId IN (" else "a.resourceId IN ("}
${it.query}
)
${if (i != filterIdQuery.lastIndex) "${operation.logicalOperator} " else ""}
""".trimIndent()
// spotless:on
filterIdArgs.addAll(it.args)
}

var limitStatement = ""
val limitArgs = mutableListOf<Any>()
if (count != null) {
Expand All @@ -405,6 +426,7 @@ internal fun Search.getQuery(
FROM ResourceEntity a
$sortJoinStatement
WHERE a.resourceType = ?
$filterIdStatement
$filterStatement
$sortOrderStatement
$limitStatement
Expand All @@ -424,6 +446,7 @@ internal fun Search.getQuery(
FROM ReferenceIndexEntity a
$sortJoinStatement
WHERE a.resourceType = ? AND a.index_name = ?
$filterIdStatement
$filterStatement
$sortOrderStatement
$limitStatement)
Expand All @@ -437,6 +460,7 @@ internal fun Search.getQuery(
FROM ResourceEntity a
$sortJoinStatement
WHERE a.resourceType = ?
$filterIdStatement
$filterStatement
$sortOrderStatement
$limitStatement
Expand All @@ -446,7 +470,7 @@ internal fun Search.getQuery(
.split("\n")
.filter { it.isNotBlank() }
.joinToString("\n") { it.trim() }
return SearchQuery(query, nestedArgs + sortArgs + type.name + whereArgs + filterArgs + limitArgs)
return SearchQuery(query, nestedArgs + sortArgs + type.name + whereArgs + filterIdArgs +filterArgs + limitArgs)
}

private val Order?.sqlString: String
Expand Down
14 changes: 13 additions & 1 deletion engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import ca.uhn.fhir.rest.gclient.TokenClientParam
import ca.uhn.fhir.rest.gclient.UriClientParam
import com.google.android.fhir.search.filter.DateClientParamFilterCriteria
import com.google.android.fhir.search.filter.DateParamFilterCriterion
import com.google.android.fhir.search.filter.IdTokenFilterCriterion
import com.google.android.fhir.search.filter.IdTokenParamFilterCriteria
import com.google.android.fhir.search.filter.NumberParamFilterCriteria
import com.google.android.fhir.search.filter.NumberParamFilterCriterion
import com.google.android.fhir.search.filter.QuantityParamFilterCriteria
Expand All @@ -38,6 +40,8 @@ import com.google.android.fhir.search.filter.TokenParamFilterCriteria
import com.google.android.fhir.search.filter.TokenParamFilterCriterion
import com.google.android.fhir.search.filter.UriFilterCriteria
import com.google.android.fhir.search.filter.UriParamFilterCriterion
import org.hl7.fhir.instance.model.api.IAnyResource
import org.hl7.fhir.r4.model.Patient
import org.hl7.fhir.r4.model.ResourceType

@SearchDslMarker
Expand All @@ -51,6 +55,7 @@ class Search(
internal val numberFilterCriteria = mutableListOf<NumberParamFilterCriteria>()
internal val referenceFilterCriteria = mutableListOf<ReferenceParamFilterCriteria>()
internal val tokenFilterCriteria = mutableListOf<TokenParamFilterCriteria>()
internal val idTokenFilterCriteria = mutableListOf<IdTokenParamFilterCriteria>()
internal val quantityFilterCriteria = mutableListOf<QuantityParamFilterCriteria>()
internal val uriFilterCriteria = mutableListOf<UriFilterCriteria>()
internal var sort: IParam? = null
Expand Down Expand Up @@ -113,7 +118,11 @@ class Search(
) {
val filters = mutableListOf<TokenParamFilterCriterion>()
init.forEach { TokenParamFilterCriterion(tokenParameter).apply(it).also(filters::add) }
tokenFilterCriteria.add(TokenParamFilterCriteria(tokenParameter, filters, operation))
if (tokenParameter.isIdFilter){
idTokenFilterCriteria.add(IdTokenParamFilterCriteria(filters.map { IdTokenFilterCriterion(it) }))
}else {
tokenFilterCriteria.add(TokenParamFilterCriteria(tokenParameter, filters, operation))
}
}

override fun filter(
Expand Down Expand Up @@ -168,3 +177,6 @@ enum class Operation(val logicalOperator: String) {
OR("OR"),
AND("AND"),
}

val TokenClientParam.isIdFilter
get() = this.paramName == IAnyResource.RES_ID.paramName
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.google.android.fhir.search.filter

import ca.uhn.fhir.rest.gclient.TokenClientParam
import com.google.android.fhir.search.ConditionParam
import com.google.android.fhir.search.Operation
import com.google.android.fhir.search.SearchQuery
import org.hl7.fhir.instance.model.api.IAnyResource
import org.hl7.fhir.r4.model.ResourceType

data class IdTokenFilterCriterion(private val tokenParamFilterCriterion: TokenParamFilterCriterion): FilterCriterion {
override fun getConditionalParams(): List<ConditionParam<String>> {
return tokenParamFilterCriterion.value!!.tokenFilters.map {
ConditionParam("?", it.code)
}
}
}

internal data class IdTokenParamFilterCriteria(
override val filters: List<IdTokenFilterCriterion>,
) : FilterCriteria(filters, Operation.OR, IAnyResource.RES_ID, ""){
override fun query(type: ResourceType): SearchQuery {
val conditionParams = filters.flatMap { it.getConditionalParams() }
return SearchQuery(
"""
${ if (conditionParams.isNotEmpty()) "VALUES ${conditionParams.joinToString { "(${it.condition})" } } " else ""}
""", conditionParams.flatMap { it.params })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ import ca.uhn.fhir.rest.gclient.TokenClientParam
import com.google.android.fhir.search.ConditionParam
import com.google.android.fhir.search.Operation
import com.google.android.fhir.search.SearchDslMarker
import com.google.android.fhir.search.SearchQuery
import org.hl7.fhir.instance.model.api.IAnyResource
import org.hl7.fhir.r4.model.CodeType
import org.hl7.fhir.r4.model.CodeableConcept
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.ContactPoint
import org.hl7.fhir.r4.model.Identifier
import org.hl7.fhir.r4.model.Patient
import org.hl7.fhir.r4.model.ResourceType
import org.hl7.fhir.r4.model.UriType

/**
Expand Down
31 changes: 31 additions & 0 deletions engine/src/test/java/com/google/android/fhir/search/SearchTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,37 @@ class SearchTest {
)
}

@Test
fun search_filter_token_res_id() {
val query =
Search(ResourceType.Patient).apply { filter(Patient.RES_ID, { value = of("3141451") }, { value = of("13114115") }) }.getQuery()
println(query.query)
println(query.args)

// assertThat(query.query)
// .isEqualTo(
// """
// SELECT a.resourceUuid, a.serializedResource
// FROM ResourceEntity a
// WHERE a.resourceType = ?
// AND a.resourceUuid IN (
// SELECT resourceUuid FROM TokenIndexEntity
// WHERE resourceType = ? AND index_name = ? AND index_value = ?
// )
// """
// .trimIndent(),
// )
// assertThat(query.args)
// .isEqualTo(
// listOf(
// ResourceType.Patient.name,
// ResourceType.Patient.name,
// Patient.ACTIVE.paramName,
// "true",
// ),
// )
}

@Test
fun search_filter_token_uriType() {
val query =
Expand Down

0 comments on commit 569a24f

Please sign in to comment.