From 6b3d4c4efafb86343fa805d9cae890a0c7abcee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Sun, 9 Jun 2024 02:39:58 +0300 Subject: [PATCH 1/8] Modify toQueryString to chunk large list of ConditionParam https://stackoverflow.com/a/17032196 --- .../fhir/search/filter/FilterCriterion.kt | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt index f6755b685d..f770ae623c 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 Google LLC + * Copyright 2021-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,15 +86,18 @@ internal sealed class FilterCriteria( * intended. */ private fun List>.toQueryString(operation: Operation) = - this.joinToString( - separator = " ${operation.logicalOperator} ", - prefix = if (size > 1) "(" else "", - postfix = if (size > 1) ")" else "", - ) { - if (it.params.size > 1) { - "(${it.condition})" - } else { - it.condition + this.chunked(50) { conditionParams -> + conditionParams.joinToString( + separator = " ${operation.logicalOperator} ", + prefix = if (size > 1) "(" else "", + postfix = if (size > 1) ")" else "", + ) { + if (it.params.size > 1) { + "(${it.condition})" + } else { + it.condition + } + } } - } + .joinToString(separator = " ${operation.logicalOperator} ") } From c73037dcbfbe8a1410063980168bf15fcdc64132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Fri, 21 Jun 2024 20:49:54 +0300 Subject: [PATCH 2/8] Add test for condition params chunking and wrapping in brackets --- .../fhir/search/filter/FilterCriterion.kt | 12 ++++- .../google/android/fhir/search/SearchTest.kt | 48 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt index f770ae623c..3ef234f494 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt @@ -86,7 +86,7 @@ internal sealed class FilterCriteria( * intended. */ private fun List>.toQueryString(operation: Operation) = - this.chunked(50) { conditionParams -> + this.chunked(CONDITION_PARAMS_CHUNK_SIZE) { conditionParams -> conditionParams.joinToString( separator = " ${operation.logicalOperator} ", prefix = if (size > 1) "(" else "", @@ -100,4 +100,14 @@ internal sealed class FilterCriteria( } } .joinToString(separator = " ${operation.logicalOperator} ") + + companion object { + /** + * Represents the number of [ConditionParam]s that can be wrapped within a bracket + * + * This is to prevent SQLite expression tree exceeding max depth of 1000 See + * https://www.sqlite.org/limits.html for Maximum Depth Of An Expression Tree + */ + const val CONDITION_PARAMS_CHUNK_SIZE = 50 + } } diff --git a/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt b/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt index d46c110664..85371439f1 100644 --- a/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt +++ b/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt @@ -22,6 +22,8 @@ import ca.uhn.fhir.model.api.TemporalPrecisionEnum import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.DateProvider import com.google.android.fhir.epochDay +import com.google.android.fhir.search.filter.FilterCriteria +import com.google.android.fhir.search.filter.ReferenceParamFilterCriterion import com.google.common.truth.Correspondence import com.google.common.truth.Truth.assertThat import java.math.BigDecimal @@ -31,6 +33,7 @@ import java.util.UUID import kotlin.math.absoluteValue import kotlin.math.roundToLong import kotlinx.coroutines.runBlocking +import org.hl7.fhir.r4.model.CarePlan import org.hl7.fhir.r4.model.CodeType import org.hl7.fhir.r4.model.CodeableConcept import org.hl7.fhir.r4.model.Coding @@ -2728,6 +2731,51 @@ class SearchTest { .inOrder() } + @Test + fun `search CarePlan filter with large list of patient reference`() { + val patientIdReferenceList = (1..500).map { "Patient/patient-$it" } + val patientIdList = + patientIdReferenceList.map Unit> { + { value = it } + } + val query = + Search(ResourceType.CarePlan) + .apply { filter(CarePlan.SUBJECT, *patientIdList.toTypedArray()) } + .getQuery() + + val queryString = query.query + assertThat(queryString) + .isEqualTo( + """ + SELECT a.resourceUuid, a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceUuid IN ( + SELECT resourceUuid FROM ReferenceIndexEntity + WHERE resourceType = ? AND index_name = ? AND (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) + ) + """ + .trimIndent(), + ) + val paramConditionListsSubstring = queryString.substring(queryString.indexOf("(index_value")) + val extractTextInBracketsRegex = Regex("\\((.*?)\\)") + assertThat( + extractTextInBracketsRegex.findAll(paramConditionListsSubstring).all { + it.value.split("OR").size <= FilterCriteria.CONDITION_PARAMS_CHUNK_SIZE + }, + ) + .isTrue() + + assertThat(query.args) + .containsExactly( + "CarePlan", + "CarePlan", + "subject", + *patientIdReferenceList.toTypedArray(), + ) + .inOrder() + } + private companion object { const val mockEpochTimeStamp = 1628516301000 const val APPROXIMATION_COEFFICIENT = 0.1 From d76411522790c7c96c950287b786b799e8a77e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Thu, 27 Jun 2024 01:21:02 +0300 Subject: [PATCH 3/8] Add support for chunkSize param in SearchDsl filters --- .../google/android/fhir/search/BaseSearch.kt | 10 +++++- .../google/android/fhir/search/SearchDsl.kt | 31 ++++++++++++++----- .../search/filter/DateParamFilterCriterion.kt | 19 +++++++++--- .../fhir/search/filter/FilterCriterion.kt | 20 ++++++------ .../filter/NumberParamFilterCriterion.kt | 5 +-- .../filter/QuantityParamFilterCriterion.kt | 5 +-- .../filter/ReferenceParamFilterCriterion.kt | 5 +-- .../filter/StringParamFilterCriterion.kt | 5 +-- .../filter/TokenParamFilterCriterion.kt | 5 +-- .../search/filter/UriParamFilterCriterion.kt | 5 +-- .../google/android/fhir/search/SearchTest.kt | 8 ++--- 11 files changed, 79 insertions(+), 39 deletions(-) diff --git a/engine/src/main/java/com/google/android/fhir/search/BaseSearch.kt b/engine/src/main/java/com/google/android/fhir/search/BaseSearch.kt index 79be44b747..37cbc42b6f 100644 --- a/engine/src/main/java/com/google/android/fhir/search/BaseSearch.kt +++ b/engine/src/main/java/com/google/android/fhir/search/BaseSearch.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 Google LLC + * Copyright 2022-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.gclient.TokenClientParam import ca.uhn.fhir.rest.gclient.UriClientParam import com.google.android.fhir.search.filter.DateParamFilterCriterion +import com.google.android.fhir.search.filter.FilterCriteria import com.google.android.fhir.search.filter.NumberParamFilterCriterion import com.google.android.fhir.search.filter.QuantityParamFilterCriterion import com.google.android.fhir.search.filter.ReferenceParamFilterCriterion @@ -52,42 +53,49 @@ interface BaseSearch { stringParameter: StringClientParam, vararg init: StringParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, + chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( referenceParameter: ReferenceClientParam, vararg init: ReferenceParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, + chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( dateParameter: DateClientParam, vararg init: DateParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, + chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( quantityParameter: QuantityClientParam, vararg init: QuantityParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, + chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( tokenParameter: TokenClientParam, vararg init: TokenParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, + chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( numberParameter: NumberClientParam, vararg init: NumberParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, + chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( uriParam: UriClientParam, vararg init: UriParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, + chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun sort(parameter: StringClientParam, order: Order) diff --git a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt index fc2e005e42..8298b20898 100644 --- a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 Google LLC + * Copyright 2022-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,21 +68,25 @@ class Search( stringParameter: StringClientParam, vararg init: StringParamFilterCriterion.() -> Unit, operation: Operation, + chunkSize: Int, ) { val filters = mutableListOf() init.forEach { StringParamFilterCriterion(stringParameter).apply(it).also(filters::add) } - stringFilterCriteria.add(StringParamFilterCriteria(stringParameter, filters, operation)) + stringFilterCriteria.add( + StringParamFilterCriteria(stringParameter, filters, operation, chunkSize), + ) } override fun filter( referenceParameter: ReferenceClientParam, vararg init: ReferenceParamFilterCriterion.() -> Unit, operation: Operation, + chunkSize: Int, ) { val filters = mutableListOf() init.forEach { ReferenceParamFilterCriterion(referenceParameter).apply(it).also(filters::add) } referenceFilterCriteria.add( - ReferenceParamFilterCriteria(referenceParameter, filters, operation), + ReferenceParamFilterCriteria(referenceParameter, filters, operation, chunkSize), ) } @@ -90,50 +94,61 @@ class Search( dateParameter: DateClientParam, vararg init: DateParamFilterCriterion.() -> Unit, operation: Operation, + chunkSize: Int, ) { val filters = mutableListOf() init.forEach { DateParamFilterCriterion(dateParameter).apply(it).also(filters::add) } - dateTimeFilterCriteria.add(DateClientParamFilterCriteria(dateParameter, filters, operation)) + dateTimeFilterCriteria.add( + DateClientParamFilterCriteria(dateParameter, filters, operation, chunkSize), + ) } override fun filter( quantityParameter: QuantityClientParam, vararg init: QuantityParamFilterCriterion.() -> Unit, operation: Operation, + chunkSize: Int, ) { val filters = mutableListOf() init.forEach { QuantityParamFilterCriterion(quantityParameter).apply(it).also(filters::add) } - quantityFilterCriteria.add(QuantityParamFilterCriteria(quantityParameter, filters, operation)) + quantityFilterCriteria.add( + QuantityParamFilterCriteria(quantityParameter, filters, operation, chunkSize), + ) } override fun filter( tokenParameter: TokenClientParam, vararg init: TokenParamFilterCriterion.() -> Unit, operation: Operation, + chunkSize: Int, ) { val filters = mutableListOf() init.forEach { TokenParamFilterCriterion(tokenParameter).apply(it).also(filters::add) } - tokenFilterCriteria.add(TokenParamFilterCriteria(tokenParameter, filters, operation)) + tokenFilterCriteria.add(TokenParamFilterCriteria(tokenParameter, filters, operation, chunkSize)) } override fun filter( numberParameter: NumberClientParam, vararg init: NumberParamFilterCriterion.() -> Unit, operation: Operation, + chunkSize: Int, ) { val filters = mutableListOf() init.forEach { NumberParamFilterCriterion(numberParameter).apply(it).also(filters::add) } - numberFilterCriteria.add(NumberParamFilterCriteria(numberParameter, filters, operation)) + numberFilterCriteria.add( + NumberParamFilterCriteria(numberParameter, filters, operation, chunkSize), + ) } override fun filter( uriParam: UriClientParam, vararg init: UriParamFilterCriterion.() -> Unit, operation: Operation, + chunkSize: Int, ) { val filters = mutableListOf() init.forEach { UriParamFilterCriterion(uriParam).apply(it).also(filters::add) } - uriFilterCriteria.add(UriFilterCriteria(uriParam, filters, operation)) + uriFilterCriteria.add(UriFilterCriteria(uriParam, filters, operation, chunkSize)) } override fun sort(parameter: StringClientParam, order: Order) { diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt index 4c0d32e982..52ecbf74d0 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 Google LLC + * Copyright 2021-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,16 +72,23 @@ internal data class DateClientParamFilterCriteria( val parameter: DateClientParam, override val filters: List, override val operation: Operation, -) : FilterCriteria(filters, operation, parameter, "") { + override val chunkSize: Int, +) : FilterCriteria(filters, operation, parameter, "", chunkSize) { override fun query(type: ResourceType): SearchQuery { val filterCriteria = listOf( - DateFilterCriteria(parameter, filters.filter { it.value!!.date != null }, operation), + DateFilterCriteria( + parameter, + filters.filter { it.value!!.date != null }, + operation, + chunkSize, + ), DateTimeFilterCriteria( parameter, filters.filter { it.value!!.dateTime != null }, operation, + chunkSize, ), ) @@ -106,12 +113,14 @@ internal data class DateClientParamFilterCriteria( val parameter: DateClientParam, override val filters: List, override val operation: Operation, - ) : FilterCriteria(filters, operation, parameter, "DateIndexEntity") + override val chunkSize: Int, + ) : FilterCriteria(filters, operation, parameter, "DateIndexEntity", chunkSize) /** Internal class used to generate query for DateTime type Criterion */ private data class DateTimeFilterCriteria( val parameter: DateClientParam, override val filters: List, override val operation: Operation, - ) : FilterCriteria(filters, operation, parameter, "DateTimeIndexEntity") + override val chunkSize: Int, + ) : FilterCriteria(filters, operation, parameter, "DateTimeIndexEntity", chunkSize) } diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt index 3ef234f494..2f4c52a963 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt @@ -40,14 +40,22 @@ internal interface FilterCriterion { * An api call like filter(Patient.GIVEN,{value = "John"},{value = "Jane"}) will create a * [StringParamFilterCriteria] with two [StringParamFilterCriterion] one with * [StringParamFilterCriterion.value] as "John" and other as "Jane." + * + * @param filters list of [FilterCriterion]s + * @param operation [Operation] + * @param param Search param + * @param entityTableName Representative entity table used + * @param chunkSize Number of filter [ConditionParam]s in a chunk to be grouped/wrapped in a + * bracket. Chunking can be used to prevent SQLite fail with error 'Expression tree exceeding max + * depth of 1000' (https://www.sqlite.org/limits.html). */ internal sealed class FilterCriteria( open val filters: List, open val operation: Operation, val param: IParam, private val entityTableName: String, + open val chunkSize: Int, ) { - /** * Returns a [SearchQuery] for the [FilterCriteria] based on all the [FilterCriterion]. In case a * particular FilterCriteria wants to return [SearchQuery] in custom manner, it should override @@ -86,7 +94,7 @@ internal sealed class FilterCriteria( * intended. */ private fun List>.toQueryString(operation: Operation) = - this.chunked(CONDITION_PARAMS_CHUNK_SIZE) { conditionParams -> + this.chunked(chunkSize) { conditionParams -> conditionParams.joinToString( separator = " ${operation.logicalOperator} ", prefix = if (size > 1) "(" else "", @@ -102,12 +110,6 @@ internal sealed class FilterCriteria( .joinToString(separator = " ${operation.logicalOperator} ") companion object { - /** - * Represents the number of [ConditionParam]s that can be wrapped within a bracket - * - * This is to prevent SQLite expression tree exceeding max depth of 1000 See - * https://www.sqlite.org/limits.html for Maximum Depth Of An Expression Tree - */ - const val CONDITION_PARAMS_CHUNK_SIZE = 50 + const val DEFAULT_CONDITION_PARAMS_CHUNK_SIZE = 50 } } diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt index 0f9072c93d..b4dfb34cb3 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 Google LLC + * Copyright 2021-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,4 +41,5 @@ internal data class NumberParamFilterCriteria( val parameter: NumberClientParam, override val filters: List, override val operation: Operation, -) : FilterCriteria(filters, operation, parameter, "NumberIndexEntity") + override val chunkSize: Int, +) : FilterCriteria(filters, operation, parameter, "NumberIndexEntity", chunkSize) diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt index 69d16c6451..ccffcf975c 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 Google LLC + * Copyright 2021-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,4 +43,5 @@ internal data class QuantityParamFilterCriteria( val parameter: QuantityClientParam, override val filters: List, override val operation: Operation, -) : FilterCriteria(filters, operation, parameter, "QuantityIndexEntity") + override val chunkSize: Int, +) : FilterCriteria(filters, operation, parameter, "QuantityIndexEntity", chunkSize) diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt index b5184b1ae5..9920ccf47a 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 Google LLC + * Copyright 2021-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,4 +38,5 @@ internal data class ReferenceParamFilterCriteria( val parameter: ReferenceClientParam, override val filters: List, override val operation: Operation, -) : FilterCriteria(filters, operation, parameter, "ReferenceIndexEntity") + override val chunkSize: Int, +) : FilterCriteria(filters, operation, parameter, "ReferenceIndexEntity", chunkSize) diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt index dba524e829..9cf37d4704 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 Google LLC + * Copyright 2021-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,4 +51,5 @@ internal data class StringParamFilterCriteria( val parameter: StringClientParam, override val filters: List, override val operation: Operation, -) : FilterCriteria(filters, operation, parameter, "StringIndexEntity") + override val chunkSize: Int, +) : FilterCriteria(filters, operation, parameter, "StringIndexEntity", chunkSize) diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt index 79b7b9d483..19e105a0f4 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 Google LLC + * Copyright 2022-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -112,4 +112,5 @@ internal data class TokenParamFilterCriteria( var parameter: TokenClientParam, override val filters: List, override val operation: Operation, -) : FilterCriteria(filters, operation, parameter, "TokenIndexEntity") + override val chunkSize: Int, +) : FilterCriteria(filters, operation, parameter, "TokenIndexEntity", chunkSize) diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt index a48bbef852..ef8711d21d 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 Google LLC + * Copyright 2021-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,4 +36,5 @@ internal data class UriFilterCriteria( val parameter: UriClientParam, override val filters: List, override val operation: Operation, -) : FilterCriteria(filters, operation, parameter, "UriIndexEntity") + override val chunkSize: Int, +) : FilterCriteria(filters, operation, parameter, "UriIndexEntity", chunkSize) diff --git a/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt b/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt index 85371439f1..f3fee50772 100644 --- a/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt +++ b/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt @@ -22,7 +22,6 @@ import ca.uhn.fhir.model.api.TemporalPrecisionEnum import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.DateProvider import com.google.android.fhir.epochDay -import com.google.android.fhir.search.filter.FilterCriteria import com.google.android.fhir.search.filter.ReferenceParamFilterCriterion import com.google.common.truth.Correspondence import com.google.common.truth.Truth.assertThat @@ -2738,9 +2737,10 @@ class SearchTest { patientIdReferenceList.map Unit> { { value = it } } + val chunkSize100 = 100 val query = Search(ResourceType.CarePlan) - .apply { filter(CarePlan.SUBJECT, *patientIdList.toTypedArray()) } + .apply { filter(CarePlan.SUBJECT, *patientIdList.toTypedArray(), chunkSize = chunkSize100) } .getQuery() val queryString = query.query @@ -2752,7 +2752,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceUuid IN ( SELECT resourceUuid FROM ReferenceIndexEntity - WHERE resourceType = ? AND index_name = ? AND (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) + WHERE resourceType = ? AND index_name = ? AND (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) ) """ .trimIndent(), @@ -2761,7 +2761,7 @@ class SearchTest { val extractTextInBracketsRegex = Regex("\\((.*?)\\)") assertThat( extractTextInBracketsRegex.findAll(paramConditionListsSubstring).all { - it.value.split("OR").size <= FilterCriteria.CONDITION_PARAMS_CHUNK_SIZE + it.value.split("OR").size <= chunkSize100 }, ) .isTrue() From d509f532e9797589bba6cea71e49ad76980ede29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Thu, 27 Jun 2024 11:57:08 +0300 Subject: [PATCH 4/8] Update workflow engine dependency to use latest --- workflow/build.gradle.kts | 2 +- .../fhir/workflow/SearchParamMapper.kt | 30 +++++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/workflow/build.gradle.kts b/workflow/build.gradle.kts index a16b9418dd..018a6c316b 100644 --- a/workflow/build.gradle.kts +++ b/workflow/build.gradle.kts @@ -98,7 +98,7 @@ dependencies { implementation(Dependencies.Kotlin.kotlinCoroutinesAndroid) implementation(Dependencies.Kotlin.kotlinCoroutinesCore) implementation(Dependencies.Kotlin.stdlib) - implementation(Dependencies.androidFhirEngine) { exclude(module = "truth") } + implementation(project(":engine")) { exclude(module = "truth") } implementation(Dependencies.androidFhirKnowledge) implementation(Dependencies.timber) implementation(Dependencies.xerces) diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt b/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt index 1e2d95fa49..f6feefe9e2 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Google LLC + * Copyright 2023-2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,16 +36,27 @@ import com.google.android.fhir.search.Search import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.DateTimeType -fun Search.applyFilterParam(name: String, param: IQueryParameterType, type: Operation) = +fun Search.applyFilterParam( + name: String, + param: IQueryParameterType, + type: Operation, + chunkSize: Int = 50, +) = when (param) { is NumberParam -> { - this.filter(NumberClientParam(name), { value = param.value }, operation = type) + this.filter( + NumberClientParam(name), + { value = param.value }, + operation = type, + chunkSize = chunkSize, + ) } is DateParam -> { this.filter( DateClientParam(name), { value = of(DateTimeType(param.value)) }, operation = type, + chunkSize = chunkSize, ) } is QuantityParam -> { @@ -57,22 +68,29 @@ fun Search.applyFilterParam(name: String, param: IQueryParameterType, type: Oper unit = param.units }, operation = type, + chunkSize = chunkSize, ) } is StringParam -> { - this.filter(StringClientParam(name), { value = param.value }, operation = type) + this.filter( + StringClientParam(name), + { value = param.value }, + operation = type, + chunkSize = chunkSize, + ) } is TokenParam -> { this.filter( TokenClientParam(name), { value = of(Coding(param.system, param.value, null)) }, + chunkSize = chunkSize, ) } is ReferenceParam -> { - this.filter(ReferenceClientParam(name), { value = param.value }) + this.filter(ReferenceClientParam(name), { value = param.value }, chunkSize = chunkSize) } is UriParam -> { - this.filter(UriClientParam(name), { value = param.value }) + this.filter(UriClientParam(name), { value = param.value }, chunkSize = chunkSize) } else -> { throw UnsupportedOperationException("$param type not supported in FhirEngineDal") From b5e67df0ab2082c336070453e4bc0dd9c4b032aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Thu, 22 Aug 2024 03:54:36 +0300 Subject: [PATCH 5/8] Refactor remove `chunkSize` parameter --- .../google/android/fhir/search/BaseSearch.kt | 8 -- .../google/android/fhir/search/SearchDsl.kt | 21 ++---- .../search/filter/DateParamFilterCriterion.kt | 11 +-- .../fhir/search/filter/FilterCriterion.kt | 33 ++++----- .../filter/NumberParamFilterCriterion.kt | 3 +- .../filter/QuantityParamFilterCriterion.kt | 3 +- .../filter/ReferenceParamFilterCriterion.kt | 3 +- .../filter/StringParamFilterCriterion.kt | 3 +- .../filter/TokenParamFilterCriterion.kt | 3 +- .../search/filter/UriParamFilterCriterion.kt | 3 +- .../google/android/fhir/search/SearchTest.kt | 73 +++++++++++++------ .../fhir/workflow/SearchParamMapper.kt | 10 +-- 12 files changed, 81 insertions(+), 93 deletions(-) diff --git a/engine/src/main/java/com/google/android/fhir/search/BaseSearch.kt b/engine/src/main/java/com/google/android/fhir/search/BaseSearch.kt index 737845241f..9bfd11a594 100644 --- a/engine/src/main/java/com/google/android/fhir/search/BaseSearch.kt +++ b/engine/src/main/java/com/google/android/fhir/search/BaseSearch.kt @@ -24,7 +24,6 @@ import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.gclient.TokenClientParam import ca.uhn.fhir.rest.gclient.UriClientParam import com.google.android.fhir.search.filter.DateParamFilterCriterion -import com.google.android.fhir.search.filter.FilterCriteria import com.google.android.fhir.search.filter.NumberParamFilterCriterion import com.google.android.fhir.search.filter.QuantityParamFilterCriterion import com.google.android.fhir.search.filter.ReferenceParamFilterCriterion @@ -55,49 +54,42 @@ interface BaseSearch { stringParameter: StringClientParam, vararg init: StringParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, - chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( referenceParameter: ReferenceClientParam, vararg init: ReferenceParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, - chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( dateParameter: DateClientParam, vararg init: DateParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, - chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( quantityParameter: QuantityClientParam, vararg init: QuantityParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, - chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( tokenParameter: TokenClientParam, vararg init: TokenParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, - chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( numberParameter: NumberClientParam, vararg init: NumberParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, - chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) fun filter( uriParam: UriClientParam, vararg init: UriParamFilterCriterion.() -> Unit, operation: Operation = Operation.OR, - chunkSize: Int = FilterCriteria.DEFAULT_CONDITION_PARAMS_CHUNK_SIZE, ) /** diff --git a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt index 8298b20898..0ebe0ebb36 100644 --- a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -68,12 +68,11 @@ class Search( stringParameter: StringClientParam, vararg init: StringParamFilterCriterion.() -> Unit, operation: Operation, - chunkSize: Int, ) { val filters = mutableListOf() init.forEach { StringParamFilterCriterion(stringParameter).apply(it).also(filters::add) } stringFilterCriteria.add( - StringParamFilterCriteria(stringParameter, filters, operation, chunkSize), + StringParamFilterCriteria(stringParameter, filters, operation), ) } @@ -81,12 +80,11 @@ class Search( referenceParameter: ReferenceClientParam, vararg init: ReferenceParamFilterCriterion.() -> Unit, operation: Operation, - chunkSize: Int, ) { val filters = mutableListOf() init.forEach { ReferenceParamFilterCriterion(referenceParameter).apply(it).also(filters::add) } referenceFilterCriteria.add( - ReferenceParamFilterCriteria(referenceParameter, filters, operation, chunkSize), + ReferenceParamFilterCriteria(referenceParameter, filters, operation), ) } @@ -94,12 +92,11 @@ class Search( dateParameter: DateClientParam, vararg init: DateParamFilterCriterion.() -> Unit, operation: Operation, - chunkSize: Int, ) { val filters = mutableListOf() init.forEach { DateParamFilterCriterion(dateParameter).apply(it).also(filters::add) } dateTimeFilterCriteria.add( - DateClientParamFilterCriteria(dateParameter, filters, operation, chunkSize), + DateClientParamFilterCriteria(dateParameter, filters, operation), ) } @@ -107,12 +104,11 @@ class Search( quantityParameter: QuantityClientParam, vararg init: QuantityParamFilterCriterion.() -> Unit, operation: Operation, - chunkSize: Int, ) { val filters = mutableListOf() init.forEach { QuantityParamFilterCriterion(quantityParameter).apply(it).also(filters::add) } quantityFilterCriteria.add( - QuantityParamFilterCriteria(quantityParameter, filters, operation, chunkSize), + QuantityParamFilterCriteria(quantityParameter, filters, operation), ) } @@ -120,23 +116,21 @@ class Search( tokenParameter: TokenClientParam, vararg init: TokenParamFilterCriterion.() -> Unit, operation: Operation, - chunkSize: Int, ) { val filters = mutableListOf() init.forEach { TokenParamFilterCriterion(tokenParameter).apply(it).also(filters::add) } - tokenFilterCriteria.add(TokenParamFilterCriteria(tokenParameter, filters, operation, chunkSize)) + tokenFilterCriteria.add(TokenParamFilterCriteria(tokenParameter, filters, operation)) } override fun filter( numberParameter: NumberClientParam, vararg init: NumberParamFilterCriterion.() -> Unit, operation: Operation, - chunkSize: Int, ) { val filters = mutableListOf() init.forEach { NumberParamFilterCriterion(numberParameter).apply(it).also(filters::add) } numberFilterCriteria.add( - NumberParamFilterCriteria(numberParameter, filters, operation, chunkSize), + NumberParamFilterCriteria(numberParameter, filters, operation), ) } @@ -144,11 +138,10 @@ class Search( uriParam: UriClientParam, vararg init: UriParamFilterCriterion.() -> Unit, operation: Operation, - chunkSize: Int, ) { val filters = mutableListOf() init.forEach { UriParamFilterCriterion(uriParam).apply(it).also(filters::add) } - uriFilterCriteria.add(UriFilterCriteria(uriParam, filters, operation, chunkSize)) + uriFilterCriteria.add(UriFilterCriteria(uriParam, filters, operation)) } override fun sort(parameter: StringClientParam, order: Order) { diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt index 52ecbf74d0..8f7f755761 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt @@ -72,8 +72,7 @@ internal data class DateClientParamFilterCriteria( val parameter: DateClientParam, override val filters: List, override val operation: Operation, - override val chunkSize: Int, -) : FilterCriteria(filters, operation, parameter, "", chunkSize) { +) : FilterCriteria(filters, operation, parameter, "") { override fun query(type: ResourceType): SearchQuery { val filterCriteria = @@ -82,13 +81,11 @@ internal data class DateClientParamFilterCriteria( parameter, filters.filter { it.value!!.date != null }, operation, - chunkSize, ), DateTimeFilterCriteria( parameter, filters.filter { it.value!!.dateTime != null }, operation, - chunkSize, ), ) @@ -113,14 +110,12 @@ internal data class DateClientParamFilterCriteria( val parameter: DateClientParam, override val filters: List, override val operation: Operation, - override val chunkSize: Int, - ) : FilterCriteria(filters, operation, parameter, "DateIndexEntity", chunkSize) + ) : FilterCriteria(filters, operation, parameter, "DateIndexEntity") /** Internal class used to generate query for DateTime type Criterion */ private data class DateTimeFilterCriteria( val parameter: DateClientParam, override val filters: List, override val operation: Operation, - override val chunkSize: Int, - ) : FilterCriteria(filters, operation, parameter, "DateTimeIndexEntity", chunkSize) + ) : FilterCriteria(filters, operation, parameter, "DateTimeIndexEntity") } diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt index 2f4c52a963..cf914c6b05 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt @@ -45,16 +45,12 @@ internal interface FilterCriterion { * @param operation [Operation] * @param param Search param * @param entityTableName Representative entity table used - * @param chunkSize Number of filter [ConditionParam]s in a chunk to be grouped/wrapped in a - * bracket. Chunking can be used to prevent SQLite fail with error 'Expression tree exceeding max - * depth of 1000' (https://www.sqlite.org/limits.html). */ internal sealed class FilterCriteria( open val filters: List, open val operation: Operation, val param: IParam, private val entityTableName: String, - open val chunkSize: Int, ) { /** * Returns a [SearchQuery] for the [FilterCriteria] based on all the [FilterCriterion]. In case a @@ -93,21 +89,20 @@ internal sealed class FilterCriteria( * This function takes care of wrapping the conditions in brackets so that they are evaluated as * intended. */ - private fun List>.toQueryString(operation: Operation) = - this.chunked(chunkSize) { conditionParams -> - conditionParams.joinToString( - separator = " ${operation.logicalOperator} ", - prefix = if (size > 1) "(" else "", - postfix = if (size > 1) ")" else "", - ) { - if (it.params.size > 1) { - "(${it.condition})" - } else { - it.condition - } - } - } - .joinToString(separator = " ${operation.logicalOperator} ") + private fun List>.toQueryString(operation: Operation): String { + if (this.size == 1) return first().condition + if (this.isEmpty()) return "" + + val mid = this.size / 2 + val left = this.subList(0, mid).toQueryString(operation) + val right = this.subList(mid, this.size).toQueryString(operation) + + return when { + left.isNotBlank() && right.isNotBlank() -> "($left ${operation.logicalOperator} $right)" + left.isNotBlank() -> left + else -> right + } + } companion object { const val DEFAULT_CONDITION_PARAMS_CHUNK_SIZE = 50 diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt index b4dfb34cb3..5d15f594cd 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt @@ -41,5 +41,4 @@ internal data class NumberParamFilterCriteria( val parameter: NumberClientParam, override val filters: List, override val operation: Operation, - override val chunkSize: Int, -) : FilterCriteria(filters, operation, parameter, "NumberIndexEntity", chunkSize) +) : FilterCriteria(filters, operation, parameter, "NumberIndexEntity") diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt index ccffcf975c..0ba05aa122 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt @@ -43,5 +43,4 @@ internal data class QuantityParamFilterCriteria( val parameter: QuantityClientParam, override val filters: List, override val operation: Operation, - override val chunkSize: Int, -) : FilterCriteria(filters, operation, parameter, "QuantityIndexEntity", chunkSize) +) : FilterCriteria(filters, operation, parameter, "QuantityIndexEntity") diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt index 9920ccf47a..30fef91f6c 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt @@ -38,5 +38,4 @@ internal data class ReferenceParamFilterCriteria( val parameter: ReferenceClientParam, override val filters: List, override val operation: Operation, - override val chunkSize: Int, -) : FilterCriteria(filters, operation, parameter, "ReferenceIndexEntity", chunkSize) +) : FilterCriteria(filters, operation, parameter, "ReferenceIndexEntity") diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt index 9cf37d4704..9cf5b1be23 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt @@ -51,5 +51,4 @@ internal data class StringParamFilterCriteria( val parameter: StringClientParam, override val filters: List, override val operation: Operation, - override val chunkSize: Int, -) : FilterCriteria(filters, operation, parameter, "StringIndexEntity", chunkSize) +) : FilterCriteria(filters, operation, parameter, "StringIndexEntity") diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt index 19e105a0f4..d6aed29355 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt @@ -112,5 +112,4 @@ internal data class TokenParamFilterCriteria( var parameter: TokenClientParam, override val filters: List, override val operation: Operation, - override val chunkSize: Int, -) : FilterCriteria(filters, operation, parameter, "TokenIndexEntity", chunkSize) +) : FilterCriteria(filters, operation, parameter, "TokenIndexEntity") diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt index ef8711d21d..5550543f48 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt @@ -36,5 +36,4 @@ internal data class UriFilterCriteria( val parameter: UriClientParam, override val filters: List, override val operation: Operation, - override val chunkSize: Int, -) : FilterCriteria(filters, operation, parameter, "UriIndexEntity", chunkSize) +) : FilterCriteria(filters, operation, parameter, "UriIndexEntity") diff --git a/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt b/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt index f3fee50772..742d8527b0 100644 --- a/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt +++ b/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt @@ -1721,7 +1721,9 @@ class SearchTest { LEFT JOIN StringIndexEntity b ON a.resourceUuid = b.resourceUuid AND b.index_name = ? WHERE a.resourceType = ? - ORDER BY b.index_value ASC + GROUP BY a.resourceUuid + HAVING MIN(IFNULL(b.index_value,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_value, 9223372036854775808) ASC """ .trimIndent(), ) @@ -1741,7 +1743,9 @@ class SearchTest { LEFT JOIN StringIndexEntity b ON a.resourceUuid = b.resourceUuid AND b.index_name = ? WHERE a.resourceType = ? - ORDER BY b.index_value DESC + GROUP BY a.resourceUuid + HAVING MAX(IFNULL(b.index_value,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_value, -9223372036854775808) DESC """ .trimIndent(), ) @@ -1763,7 +1767,9 @@ class SearchTest { LEFT JOIN NumberIndexEntity b ON a.resourceUuid = b.resourceUuid AND b.index_name = ? WHERE a.resourceType = ? - ORDER BY b.index_value ASC + GROUP BY a.resourceUuid + HAVING MIN(IFNULL(b.index_value,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_value, 9223372036854775808) ASC """ .trimIndent(), ) @@ -1793,7 +1799,9 @@ class SearchTest { SELECT resourceUuid FROM StringIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value LIKE ? || '%' COLLATE NOCASE ) - ORDER BY b.index_value ASC + GROUP BY a.resourceUuid + HAVING MIN(IFNULL(b.index_value,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_value, 9223372036854775808) ASC LIMIT ? OFFSET ? """ .trimIndent(), @@ -2047,8 +2055,10 @@ class SearchTest { LEFT JOIN DateTimeIndexEntity c ON a.resourceUuid = c.resourceUuid AND c.index_name = ? WHERE a.resourceType = ? - ORDER BY b.index_from ASC, c.index_from ASC - """ + GROUP BY a.resourceUuid + HAVING MIN(IFNULL(b.index_from,0) + IFNULL(c.index_from,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_from, 9223372036854775808) ASC, IFNULL(c.index_from, 9223372036854775808) ASC + """ .trimIndent(), ) } @@ -2068,7 +2078,9 @@ class SearchTest { LEFT JOIN DateTimeIndexEntity c ON a.resourceUuid = c.resourceUuid AND c.index_name = ? WHERE a.resourceType = ? - ORDER BY b.index_from DESC, c.index_from DESC + GROUP BY a.resourceUuid + HAVING MAX(IFNULL(b.index_from,0) + IFNULL(c.index_from,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_from, -9223372036854775808) DESC, IFNULL(c.index_from, -9223372036854775808) DESC """ .trimIndent(), ) @@ -2279,7 +2291,7 @@ class SearchTest { .isEqualTo( """ SELECT * FROM ( - SELECT rie.index_name, rie.resourceUuid, re.serializedResource + SELECT rie.index_name, rie.resourceUuid, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceType||"/"||re.resourceId = rie.index_value @@ -2321,7 +2333,7 @@ class SearchTest { .isEqualTo( """ SELECT * FROM ( - SELECT rie.index_name, rie.resourceUuid, re.serializedResource + SELECT rie.index_name, rie.resourceUuid, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceType||"/"||re.resourceId = rie.index_value @@ -2371,7 +2383,7 @@ class SearchTest { .isEqualTo( """ SELECT * FROM ( - SELECT rie.index_name, rie.resourceUuid, re.serializedResource + SELECT rie.index_name, rie.resourceUuid, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceType||"/"||re.resourceId = rie.index_value @@ -2382,7 +2394,9 @@ class SearchTest { SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) - ORDER BY b.index_value DESC + GROUP BY re.resourceUuid , rie.resourceuuid + HAVING MAX(IFNULL(b.index_value,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_value, -9223372036854775808) DESC ) """ .trimIndent(), @@ -2430,7 +2444,7 @@ class SearchTest { .isEqualTo( """ SELECT * FROM ( - SELECT rie.index_name, rie.resourceUuid, re.serializedResource + SELECT rie.index_name, rie.resourceUuid, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceType||"/"||re.resourceId = rie.index_value @@ -2441,11 +2455,13 @@ class SearchTest { SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) - ORDER BY b.index_value DESC + GROUP BY re.resourceUuid , rie.resourceuuid + HAVING MAX(IFNULL(b.index_value,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_value, -9223372036854775808) DESC ) UNION ALL SELECT * FROM ( - SELECT rie.index_name, rie.resourceUuid, re.serializedResource + SELECT rie.index_name, rie.resourceUuid, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceType||"/"||re.resourceId = rie.index_value @@ -2456,7 +2472,9 @@ class SearchTest { SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) - ORDER BY b.index_value DESC + GROUP BY re.resourceUuid , rie.resourceuuid + HAVING MAX(IFNULL(b.index_value,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_value, -9223372036854775808) DESC ) """ .trimIndent(), @@ -2498,7 +2516,7 @@ class SearchTest { .isEqualTo( """ SELECT * FROM ( - SELECT rie.index_name, rie.index_value, re.serializedResource + SELECT rie.index_name, rie.index_value, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceUuid = rie.resourceUuid @@ -2536,7 +2554,7 @@ class SearchTest { .isEqualTo( """ SELECT * FROM ( - SELECT rie.index_name, rie.index_value, re.serializedResource + SELECT rie.index_name, rie.index_value, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceUuid = rie.resourceUuid @@ -2589,7 +2607,7 @@ class SearchTest { .isEqualTo( """ SELECT * FROM ( - SELECT rie.index_name, rie.index_value, re.serializedResource + SELECT rie.index_name, rie.index_value, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceUuid = rie.resourceUuid @@ -2602,7 +2620,9 @@ class SearchTest { SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) - ORDER BY b.index_from DESC, c.index_from DESC + GROUP BY re.resourceUuid , rie.index_value + HAVING MAX(IFNULL(b.index_from,0) + IFNULL(c.index_from,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_from, -9223372036854775808) DESC, IFNULL(c.index_from, -9223372036854775808) DESC ) """ .trimIndent(), @@ -2666,7 +2686,7 @@ class SearchTest { .isEqualTo( """ SELECT * FROM ( - SELECT rie.index_name, rie.index_value, re.serializedResource + SELECT rie.index_name, rie.index_value, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceUuid = rie.resourceUuid @@ -2679,11 +2699,13 @@ class SearchTest { SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) - ORDER BY b.index_from DESC, c.index_from DESC + GROUP BY re.resourceUuid , rie.index_value + HAVING MAX(IFNULL(b.index_from,0) + IFNULL(c.index_from,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_from, -9223372036854775808) DESC, IFNULL(c.index_from, -9223372036854775808) DESC ) UNION ALL SELECT * FROM ( - SELECT rie.index_name, rie.index_value, re.serializedResource + SELECT rie.index_name, rie.index_value, re.serializedResource FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceUuid = rie.resourceUuid @@ -2696,7 +2718,9 @@ class SearchTest { SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) - ORDER BY b.index_from DESC, c.index_from DESC + GROUP BY re.resourceUuid , rie.index_value + HAVING MAX(IFNULL(b.index_from,0) + IFNULL(c.index_from,0)) >= -9223372036854775808 + ORDER BY IFNULL(b.index_from, -9223372036854775808) DESC, IFNULL(c.index_from, -9223372036854775808) DESC ) """ .trimIndent(), @@ -2732,7 +2756,7 @@ class SearchTest { @Test fun `search CarePlan filter with large list of patient reference`() { - val patientIdReferenceList = (1..500).map { "Patient/patient-$it" } + val patientIdReferenceList = (1..1000).map { "Patient/patient-$it" } val patientIdList = patientIdReferenceList.map Unit> { { value = it } @@ -2744,6 +2768,7 @@ class SearchTest { .getQuery() val queryString = query.query + println(queryString) assertThat(queryString) .isEqualTo( """ diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt b/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt index f6feefe9e2..5cd8c4387b 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt @@ -40,7 +40,6 @@ fun Search.applyFilterParam( name: String, param: IQueryParameterType, type: Operation, - chunkSize: Int = 50, ) = when (param) { is NumberParam -> { @@ -48,7 +47,6 @@ fun Search.applyFilterParam( NumberClientParam(name), { value = param.value }, operation = type, - chunkSize = chunkSize, ) } is DateParam -> { @@ -56,7 +54,6 @@ fun Search.applyFilterParam( DateClientParam(name), { value = of(DateTimeType(param.value)) }, operation = type, - chunkSize = chunkSize, ) } is QuantityParam -> { @@ -68,7 +65,6 @@ fun Search.applyFilterParam( unit = param.units }, operation = type, - chunkSize = chunkSize, ) } is StringParam -> { @@ -76,21 +72,19 @@ fun Search.applyFilterParam( StringClientParam(name), { value = param.value }, operation = type, - chunkSize = chunkSize, ) } is TokenParam -> { this.filter( TokenClientParam(name), { value = of(Coding(param.system, param.value, null)) }, - chunkSize = chunkSize, ) } is ReferenceParam -> { - this.filter(ReferenceClientParam(name), { value = param.value }, chunkSize = chunkSize) + this.filter(ReferenceClientParam(name), { value = param.value }) } is UriParam -> { - this.filter(UriClientParam(name), { value = param.value }, chunkSize = chunkSize) + this.filter(UriClientParam(name), { value = param.value }) } else -> { throw UnsupportedOperationException("$param type not supported in FhirEngineDal") From b9708a6edb2511c5fe58eb5ca21839b95f0fb466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Thu, 22 Aug 2024 03:56:31 +0300 Subject: [PATCH 6/8] Recursively bifurcate expression tree to reduce depth --- .../fhir/search/filter/FilterCriterion.kt | 29 +++++++++++------- .../google/android/fhir/search/SearchTest.kt | 30 +++++++------------ 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt index cf914c6b05..ca2e4bfabd 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt @@ -90,21 +90,28 @@ internal sealed class FilterCriteria( * intended. */ private fun List>.toQueryString(operation: Operation): String { - if (this.size == 1) return first().condition - if (this.isEmpty()) return "" + if (this.size <= 1) { + return map { + if (it.params.size > 1) { + "(${it.condition})" + } else { + it.condition + } + } + .firstOrNull() + ?: "" + } val mid = this.size / 2 val left = this.subList(0, mid).toQueryString(operation) val right = this.subList(mid, this.size).toQueryString(operation) - return when { - left.isNotBlank() && right.isNotBlank() -> "($left ${operation.logicalOperator} $right)" - left.isNotBlank() -> left - else -> right - } - } - - companion object { - const val DEFAULT_CONDITION_PARAMS_CHUNK_SIZE = 50 + return listOf(left, right) + .filter { it.isNotBlank() } + .joinToString( + separator = " ${operation.logicalOperator} ", + prefix = "(", + postfix = ")", + ) } } diff --git a/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt b/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt index 742d8527b0..ea61195da1 100644 --- a/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt +++ b/engine/src/test/java/com/google/android/fhir/search/SearchTest.kt @@ -2756,40 +2756,30 @@ class SearchTest { @Test fun `search CarePlan filter with large list of patient reference`() { - val patientIdReferenceList = (1..1000).map { "Patient/patient-$it" } + val patientIdReferenceList = (1..1001).map { "Patient/patient-$it" } val patientIdList = patientIdReferenceList.map Unit> { { value = it } } - val chunkSize100 = 100 val query = Search(ResourceType.CarePlan) - .apply { filter(CarePlan.SUBJECT, *patientIdList.toTypedArray(), chunkSize = chunkSize100) } + .apply { filter(CarePlan.SUBJECT, *patientIdList.toTypedArray()) } .getQuery() val queryString = query.query - println(queryString) assertThat(queryString) .isEqualTo( """ - SELECT a.resourceUuid, a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( - SELECT resourceUuid FROM ReferenceIndexEntity - WHERE resourceType = ? AND index_name = ? AND (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ? OR index_value = ?) - ) - """ + SELECT a.resourceUuid, a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceUuid IN ( + SELECT resourceUuid FROM ReferenceIndexEntity + WHERE resourceType = ? AND index_name = ? AND (((((((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR ((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))) OR (((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR (((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))))) OR ((((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR ((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))) OR (((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR (((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))))) OR (((((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR ((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))) OR (((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR (((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))))) OR ((((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR ((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))) OR (((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR (((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))))))) OR ((((((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR ((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))) OR (((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR (((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))))) OR ((((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR ((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))) OR (((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR (((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))))) OR (((((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR ((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))) OR (((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR (((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))))) OR ((((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR (((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))) OR (((((index_value = ? OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))))) OR (((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))) OR ((((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?))) OR (((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) OR ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)))))))))) + ) + """ .trimIndent(), ) - val paramConditionListsSubstring = queryString.substring(queryString.indexOf("(index_value")) - val extractTextInBracketsRegex = Regex("\\((.*?)\\)") - assertThat( - extractTextInBracketsRegex.findAll(paramConditionListsSubstring).all { - it.value.split("OR").size <= chunkSize100 - }, - ) - .isTrue() assertThat(query.args) .containsExactly( From 821feab676413522feda08385488d67a47e9e7d6 Mon Sep 17 00:00:00 2001 From: LZRS <12814349+LZRS@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:57:24 +0300 Subject: [PATCH 7/8] Revert touched files not relevant for the PR --- .../google/android/fhir/search/SearchDsl.kt | 18 +++++------------ .../search/filter/DateParamFilterCriterion.kt | 8 ++------ .../fhir/search/filter/FilterCriterion.kt | 6 +----- .../filter/NumberParamFilterCriterion.kt | 2 +- .../filter/QuantityParamFilterCriterion.kt | 2 +- .../filter/ReferenceParamFilterCriterion.kt | 2 +- .../filter/StringParamFilterCriterion.kt | 2 +- .../filter/TokenParamFilterCriterion.kt | 2 +- .../search/filter/UriParamFilterCriterion.kt | 2 +- .../fhir/workflow/SearchParamMapper.kt | 20 ++++--------------- 10 files changed, 18 insertions(+), 46 deletions(-) diff --git a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt index 0ebe0ebb36..fc2e005e42 100644 --- a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 Google LLC + * Copyright 2022-2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,9 +71,7 @@ class Search( ) { val filters = mutableListOf() init.forEach { StringParamFilterCriterion(stringParameter).apply(it).also(filters::add) } - stringFilterCriteria.add( - StringParamFilterCriteria(stringParameter, filters, operation), - ) + stringFilterCriteria.add(StringParamFilterCriteria(stringParameter, filters, operation)) } override fun filter( @@ -95,9 +93,7 @@ class Search( ) { val filters = mutableListOf() init.forEach { DateParamFilterCriterion(dateParameter).apply(it).also(filters::add) } - dateTimeFilterCriteria.add( - DateClientParamFilterCriteria(dateParameter, filters, operation), - ) + dateTimeFilterCriteria.add(DateClientParamFilterCriteria(dateParameter, filters, operation)) } override fun filter( @@ -107,9 +103,7 @@ class Search( ) { val filters = mutableListOf() init.forEach { QuantityParamFilterCriterion(quantityParameter).apply(it).also(filters::add) } - quantityFilterCriteria.add( - QuantityParamFilterCriteria(quantityParameter, filters, operation), - ) + quantityFilterCriteria.add(QuantityParamFilterCriteria(quantityParameter, filters, operation)) } override fun filter( @@ -129,9 +123,7 @@ class Search( ) { val filters = mutableListOf() init.forEach { NumberParamFilterCriterion(numberParameter).apply(it).also(filters::add) } - numberFilterCriteria.add( - NumberParamFilterCriteria(numberParameter, filters, operation), - ) + numberFilterCriteria.add(NumberParamFilterCriteria(numberParameter, filters, operation)) } override fun filter( diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt index 8f7f755761..4c0d32e982 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/DateParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 Google LLC + * Copyright 2021-2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,11 +77,7 @@ internal data class DateClientParamFilterCriteria( override fun query(type: ResourceType): SearchQuery { val filterCriteria = listOf( - DateFilterCriteria( - parameter, - filters.filter { it.value!!.date != null }, - operation, - ), + DateFilterCriteria(parameter, filters.filter { it.value!!.date != null }, operation), DateTimeFilterCriteria( parameter, filters.filter { it.value!!.dateTime != null }, diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt index ca2e4bfabd..be49ecb4ee 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt @@ -40,11 +40,6 @@ internal interface FilterCriterion { * An api call like filter(Patient.GIVEN,{value = "John"},{value = "Jane"}) will create a * [StringParamFilterCriteria] with two [StringParamFilterCriterion] one with * [StringParamFilterCriterion.value] as "John" and other as "Jane." - * - * @param filters list of [FilterCriterion]s - * @param operation [Operation] - * @param param Search param - * @param entityTableName Representative entity table used */ internal sealed class FilterCriteria( open val filters: List, @@ -52,6 +47,7 @@ internal sealed class FilterCriteria( val param: IParam, private val entityTableName: String, ) { + /** * Returns a [SearchQuery] for the [FilterCriteria] based on all the [FilterCriterion]. In case a * particular FilterCriteria wants to return [SearchQuery] in custom manner, it should override diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt index 5d15f594cd..0f9072c93d 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/NumberParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 Google LLC + * Copyright 2021-2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt index 0ba05aa122..69d16c6451 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/QuantityParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 Google LLC + * Copyright 2021-2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt index 30fef91f6c..b5184b1ae5 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/ReferenceParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 Google LLC + * Copyright 2021-2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt index 9cf5b1be23..dba524e829 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/StringParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 Google LLC + * Copyright 2021-2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt index d6aed29355..79b7b9d483 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/TokenParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022-2024 Google LLC + * Copyright 2022-2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt index 5550543f48..a48bbef852 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/UriParamFilterCriterion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2021-2024 Google LLC + * Copyright 2021-2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt b/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt index 5cd8c4387b..1e2d95fa49 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/SearchParamMapper.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,18 +36,10 @@ import com.google.android.fhir.search.Search import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.DateTimeType -fun Search.applyFilterParam( - name: String, - param: IQueryParameterType, - type: Operation, -) = +fun Search.applyFilterParam(name: String, param: IQueryParameterType, type: Operation) = when (param) { is NumberParam -> { - this.filter( - NumberClientParam(name), - { value = param.value }, - operation = type, - ) + this.filter(NumberClientParam(name), { value = param.value }, operation = type) } is DateParam -> { this.filter( @@ -68,11 +60,7 @@ fun Search.applyFilterParam( ) } is StringParam -> { - this.filter( - StringClientParam(name), - { value = param.value }, - operation = type, - ) + this.filter(StringClientParam(name), { value = param.value }, operation = type) } is TokenParam -> { this.filter( From 112344f6a12345ab943595dea04d33235f304a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:45:11 +0300 Subject: [PATCH 8/8] Refactor toQueryString --- .../fhir/search/filter/FilterCriterion.kt | 14 ++---- .../android/fhir/impl/FhirEngineImplTest.kt | 44 +++++++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt index be49ecb4ee..242dc762f5 100644 --- a/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt +++ b/engine/src/main/java/com/google/android/fhir/search/filter/FilterCriterion.kt @@ -87,27 +87,21 @@ internal sealed class FilterCriteria( */ private fun List>.toQueryString(operation: Operation): String { if (this.size <= 1) { - return map { + val queryString = + firstOrNull()?.let { if (it.params.size > 1) { "(${it.condition})" } else { it.condition } } - .firstOrNull() - ?: "" + return queryString ?: "" } val mid = this.size / 2 val left = this.subList(0, mid).toQueryString(operation) val right = this.subList(mid, this.size).toQueryString(operation) - return listOf(left, right) - .filter { it.isNotBlank() } - .joinToString( - separator = " ${operation.logicalOperator} ", - prefix = "(", - postfix = ")", - ) + return "($left ${operation.logicalOperator} $right)" } } diff --git a/engine/src/test/java/com/google/android/fhir/impl/FhirEngineImplTest.kt b/engine/src/test/java/com/google/android/fhir/impl/FhirEngineImplTest.kt index 48be1a491b..268df0763c 100644 --- a/engine/src/test/java/com/google/android/fhir/impl/FhirEngineImplTest.kt +++ b/engine/src/test/java/com/google/android/fhir/impl/FhirEngineImplTest.kt @@ -17,6 +17,7 @@ package com.google.android.fhir.impl import androidx.test.core.app.ApplicationProvider +import ca.uhn.fhir.rest.gclient.TokenClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.FhirServices.Companion.builder import com.google.android.fhir.LocalChange @@ -26,6 +27,8 @@ import com.google.android.fhir.get import com.google.android.fhir.lastUpdated import com.google.android.fhir.logicalId import com.google.android.fhir.search.LOCAL_LAST_UPDATED_PARAM +import com.google.android.fhir.search.filter.TokenParamFilterCriterion +import com.google.android.fhir.search.getQuery import com.google.android.fhir.search.search import com.google.android.fhir.sync.AcceptLocalConflictResolver import com.google.android.fhir.sync.AcceptRemoteConflictResolver @@ -723,6 +726,47 @@ class FhirEngineImplTest { assertThat(result.map { it.resource.logicalId }).containsExactly("patient-id-create").inOrder() } + @Test + fun `testing search many`() = runTest { + val patient1 = Patient().apply { id = "patient-1" } + val patient2 = Patient().apply { id = "patient-2" } + val patient3 = Patient().apply { id = "patient-45" } + val patient4 = Patient().apply { id = "patient-4355" } + val patient5 = Patient().apply { id = "patient-899" } + val patient6 = Patient().apply { id = "patient-883376" } + fhirEngine.create(patient1, patient2, patient3, patient4, patient5, patient6) + + val result1 = + fhirEngine.search { + filter(TokenClientParam("_id"), { value = of(patient3.logicalId) }) + } + assertThat(result1).isNotEmpty() + assertThat(result1.size).isEqualTo(1) + + val filterValues = + listOf(patient2, patient3, patient1, patient5, patient4, patient6).map< + Patient, + TokenParamFilterCriterion.() -> Unit, + > { + { value = of(it.logicalId) } + } + val result2 = + fhirEngine.search { + filter(TokenClientParam("_id"), *filterValues.toTypedArray()) + println(getQuery().query) + println(getQuery().args) + } + assertThat(result2.map { it.resource.logicalId }) + .containsExactly( + "patient-2", + "patient-45", + "patient-1", + "patient-4355", + "patient-899", + "patient-883376", + ) + } + @Test fun `update should allow patient search with LOCAL_LAST_UPDATED_PARAM and update local entity`() = runBlocking {