From 17029eb9e2d0b1f5934525784ec961bf525dd90b Mon Sep 17 00:00:00 2001 From: Martin Ndegwa Date: Tue, 10 Dec 2024 20:52:35 +0300 Subject: [PATCH 1/4] Refactor usages of Json Parser for thread safety (#2749) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor usages of Json Parser * Fix failing tests ✅ * Update JsonParser usages for Dao classes Spotless clean * Fix failing unit test ✅ --- .../android/fhir/db/impl/DatabaseImplTest.kt | 11 +++--- .../db/impl/EncryptedDatabaseErrorTest.kt | 14 ++------ .../fhir/db/impl/dao/LocalChangeDaoTest.kt | 16 ++++----- .../com/google/android/fhir/FhirServices.kt | 8 +---- .../android/fhir/db/impl/DatabaseImpl.kt | 34 ++++++++----------- .../fhir/db/impl/dao/LocalChangeDao.kt | 7 ++-- .../android/fhir/db/impl/dao/ResourceDao.kt | 20 +++++++---- .../upload/request/UrlRequestGenerator.kt | 12 +++---- .../android/fhir/impl/FhirEngineImplTest.kt | 8 +++-- gradle/libs.versions.toml | 4 +-- .../fhir/knowledge/KnowledgeManager.kt | 4 +-- 11 files changed, 62 insertions(+), 76 deletions(-) diff --git a/engine/src/androidTest/java/com/google/android/fhir/db/impl/DatabaseImplTest.kt b/engine/src/androidTest/java/com/google/android/fhir/db/impl/DatabaseImplTest.kt index cd568b023a..1807b4b2d6 100644 --- a/engine/src/androidTest/java/com/google/android/fhir/db/impl/DatabaseImplTest.kt +++ b/engine/src/androidTest/java/com/google/android/fhir/db/impl/DatabaseImplTest.kt @@ -118,6 +118,7 @@ class DatabaseImplTest { @JvmField @Parameterized.Parameter(0) var encrypted: Boolean = false private val context: Context = ApplicationProvider.getApplicationContext() + private val parser = FhirContext.forR4Cached().newJsonParser() private lateinit var services: FhirServices private lateinit var database: Database @@ -202,7 +203,7 @@ class DatabaseImplTest { fun getLocalChanges_withSingleLocaleChange_shouldReturnSingleLocalChanges() = runBlocking { val patient: Patient = readFromFile(Patient::class.java, "/date_test_patient.json") database.insert(patient) - val patientString = services.parser.encodeResourceToString(patient) + val patientString = parser.encodeResourceToString(patient) val resourceLocalChanges = database.getLocalChanges(patient.resourceType, patient.logicalId) assertThat(resourceLocalChanges.size).isEqualTo(1) with(resourceLocalChanges[0]) { @@ -269,7 +270,7 @@ class DatabaseImplTest { fun clearDatabase_shouldClearAllTablesData() = runBlocking { val patient: Patient = readFromFile(Patient::class.java, "/date_test_patient.json") database.insert(patient) - val patientString = services.parser.encodeResourceToString(patient) + val patientString = parser.encodeResourceToString(patient) val resourceLocalChanges = database.getLocalChanges(patient.resourceType, patient.logicalId) assertThat(resourceLocalChanges.size).isEqualTo(1) with(resourceLocalChanges[0]) { @@ -393,7 +394,7 @@ class DatabaseImplTest { @Test fun insert_shouldAddInsertLocalChange() = runBlocking { - val testPatient2String = services.parser.encodeResourceToString(TEST_PATIENT_2) + val testPatient2String = parser.encodeResourceToString(TEST_PATIENT_2) database.insert(TEST_PATIENT_2) val resourceLocalChanges = database.getAllLocalChanges().filter { it.resourceId.equals(TEST_PATIENT_2_ID) } @@ -481,7 +482,7 @@ class DatabaseImplTest { database.insert(patient) patient = readFromFile(Patient::class.java, "/update_test_patient_1.json") database.update(patient) - services.parser.encodeResourceToString(patient) + parser.encodeResourceToString(patient) val localChangeTokenIds = database .getAllLocalChanges() @@ -4101,7 +4102,7 @@ class DatabaseImplTest { val observationLocalChange = updatedObservationLocalChanges[0] assertThat(observationLocalChange.type).isEqualTo(LocalChange.Type.INSERT) val observationLocalChangePayload = - services.parser.parseResource(observationLocalChange.payload) as Observation + parser.parseResource(observationLocalChange.payload) as Observation assertThat(observationLocalChangePayload.subject.reference) .isEqualTo("Patient/$remotelyCreatedPatientResourceId") } diff --git a/engine/src/androidTest/java/com/google/android/fhir/db/impl/EncryptedDatabaseErrorTest.kt b/engine/src/androidTest/java/com/google/android/fhir/db/impl/EncryptedDatabaseErrorTest.kt index 412da27b45..76c359596f 100644 --- a/engine/src/androidTest/java/com/google/android/fhir/db/impl/EncryptedDatabaseErrorTest.kt +++ b/engine/src/androidTest/java/com/google/android/fhir/db/impl/EncryptedDatabaseErrorTest.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. @@ -22,7 +22,6 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import ca.uhn.fhir.context.FhirContext -import ca.uhn.fhir.context.FhirVersionEnum import ca.uhn.fhir.util.FhirTerser import com.google.android.fhir.DatabaseErrorStrategy.RECREATE_AT_OPEN import com.google.android.fhir.DatabaseErrorStrategy.UNSPECIFIED @@ -49,8 +48,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class EncryptedDatabaseErrorTest { private val context: Context = ApplicationProvider.getApplicationContext() - private val parser = FhirContext.forR4().newJsonParser() - private val terser = FhirTerser(FhirContext.forCached(FhirVersionEnum.R4)) + private val terser = FhirTerser(FhirContext.forR4Cached()) private val resourceIndexer = ResourceIndexer(SearchParamDefinitionsProviderImpl()) @After @@ -66,7 +64,6 @@ class EncryptedDatabaseErrorTest { // GIVEN an unencrypted database. DatabaseImpl( context, - parser, terser, DatabaseConfig( inMemory = false, @@ -84,7 +81,6 @@ class EncryptedDatabaseErrorTest { // THEN it should throw SQLiteException DatabaseImpl( context, - parser, terser, DatabaseConfig( inMemory = false, @@ -115,7 +111,6 @@ class EncryptedDatabaseErrorTest { // GIVEN an unencrypted database. DatabaseImpl( context, - parser, terser, DatabaseConfig( inMemory = false, @@ -139,7 +134,6 @@ class EncryptedDatabaseErrorTest { // THEN it should throw SQLiteException DatabaseImpl( context, - parser, terser, DatabaseConfig( inMemory = false, @@ -169,7 +163,6 @@ class EncryptedDatabaseErrorTest { // GIVEN an unencrypted database. DatabaseImpl( context, - parser, terser, DatabaseConfig( inMemory = false, @@ -193,7 +186,6 @@ class EncryptedDatabaseErrorTest { // THEN it should recreate the database DatabaseImpl( context, - parser, terser, DatabaseConfig( inMemory = false, @@ -226,7 +218,6 @@ class EncryptedDatabaseErrorTest { // GIVEN an encrypted database. DatabaseImpl( context, - parser, terser, DatabaseConfig( inMemory = false, @@ -244,7 +235,6 @@ class EncryptedDatabaseErrorTest { // THEN it should recreate database. DatabaseImpl( context, - parser, terser, DatabaseConfig( inMemory = false, diff --git a/engine/src/androidTest/java/com/google/android/fhir/db/impl/dao/LocalChangeDaoTest.kt b/engine/src/androidTest/java/com/google/android/fhir/db/impl/dao/LocalChangeDaoTest.kt index b4280911c0..1b89d8e951 100644 --- a/engine/src/androidTest/java/com/google/android/fhir/db/impl/dao/LocalChangeDaoTest.kt +++ b/engine/src/androidTest/java/com/google/android/fhir/db/impl/dao/LocalChangeDaoTest.kt @@ -49,6 +49,7 @@ import org.junit.runner.RunWith class LocalChangeDaoTest { private lateinit var database: ResourceDatabase private lateinit var localChangeDao: LocalChangeDao + private val iParser = FhirContext.forR4Cached().newJsonParser() @Before fun setupDatabase() { @@ -62,7 +63,6 @@ class LocalChangeDaoTest { localChangeDao = database.localChangeDao().also { - it.iParser = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser() it.fhirTerser = FhirTerser(FhirContext.forCached(FhirVersionEnum.R4)) } } @@ -97,8 +97,7 @@ class LocalChangeDaoTest { assertThat(carePlanLocalChange1.resourceUuid).isEqualTo(carePlanResourceUuid) assertThat(carePlanLocalChange1.resourceId).isEqualTo(carePlan.id) assertThat(carePlanLocalChange1.type).isEqualTo(LocalChangeEntity.Type.INSERT) - assertThat(carePlanLocalChange1.payload) - .isEqualTo(localChangeDao.iParser.encodeResourceToString(carePlan)) + assertThat(carePlanLocalChange1.payload).isEqualTo(iParser.encodeResourceToString(carePlan)) val carePlanLocalChange1Id = carePlanLocalChange1.id val localChangeResourceReferences = @@ -150,7 +149,7 @@ class LocalChangeDaoTest { resourceId = originalCarePlan.logicalId, resourceType = originalCarePlan.resourceType, resourceUuid = carePlanResourceUuid, - serializedResource = localChangeDao.iParser.encodeResourceToString(originalCarePlan), + serializedResource = iParser.encodeResourceToString(originalCarePlan), ), updatedResource = modifiedCarePlan, timeOfLocalChange = carePlanUpdateTime, @@ -163,7 +162,7 @@ class LocalChangeDaoTest { assertThat(carePlanLocalChange1.resourceId).isEqualTo(originalCarePlan.id) assertThat(carePlanLocalChange1.type).isEqualTo(LocalChangeEntity.Type.INSERT) assertThat(carePlanLocalChange1.payload) - .isEqualTo(localChangeDao.iParser.encodeResourceToString(originalCarePlan)) + .isEqualTo(iParser.encodeResourceToString(originalCarePlan)) val carePlanLocalChange2 = carePlanLocalChanges[1] assertThat(carePlanLocalChange2.resourceUuid).isEqualTo(carePlanResourceUuid) @@ -224,8 +223,7 @@ class LocalChangeDaoTest { assertThat(carePlanLocalChange1.resourceUuid).isEqualTo(carePlanResourceUuid) assertThat(carePlanLocalChange1.resourceId).isEqualTo(carePlan.id) assertThat(carePlanLocalChange1.type).isEqualTo(LocalChangeEntity.Type.INSERT) - assertThat(carePlanLocalChange1.payload) - .isEqualTo(localChangeDao.iParser.encodeResourceToString(carePlan)) + assertThat(carePlanLocalChange1.payload).isEqualTo(iParser.encodeResourceToString(carePlan)) val carePlanLocalChange2 = carePlanLocalChanges[1] assertThat(carePlanLocalChange2.resourceUuid).isEqualTo(carePlanResourceUuid) @@ -285,7 +283,7 @@ class LocalChangeDaoTest { resourceId = originalCarePlan.logicalId, resourceType = originalCarePlan.resourceType, resourceUuid = carePlanResourceUuid, - serializedResource = localChangeDao.iParser.encodeResourceToString(originalCarePlan), + serializedResource = iParser.encodeResourceToString(originalCarePlan), ), updatedResource = modifiedCarePlan, timeOfLocalChange = carePlanUpdateTime, @@ -318,7 +316,7 @@ class LocalChangeDaoTest { activityFirstRep.detail.performer.add(Reference("Patient/$updatedPatientId")) } assertThat(carePlanLocalChange1.payload) - .isEqualTo(localChangeDao.iParser.encodeResourceToString(updatedReferencesCarePlan)) + .isEqualTo(iParser.encodeResourceToString(updatedReferencesCarePlan)) val carePlanLocalChange1Id = carePlanLocalChange1.id // assert that LocalChangeReferences are updated as well val localChange1ResourceReferences = diff --git a/engine/src/main/java/com/google/android/fhir/FhirServices.kt b/engine/src/main/java/com/google/android/fhir/FhirServices.kt index efa76f0536..bf95e983be 100644 --- a/engine/src/main/java/com/google/android/fhir/FhirServices.kt +++ b/engine/src/main/java/com/google/android/fhir/FhirServices.kt @@ -18,8 +18,6 @@ package com.google.android.fhir import android.content.Context import ca.uhn.fhir.context.FhirContext -import ca.uhn.fhir.context.FhirVersionEnum -import ca.uhn.fhir.parser.IParser import ca.uhn.fhir.util.FhirTerser import com.google.android.fhir.db.Database import com.google.android.fhir.db.impl.DatabaseConfig @@ -38,7 +36,6 @@ import timber.log.Timber internal data class FhirServices( val fhirEngine: FhirEngine, - val parser: IParser, val database: Database, val remoteDataSource: DataSource? = null, val fhirDataStore: FhirDataStore, @@ -74,15 +71,13 @@ internal data class FhirServices( } fun build(): FhirServices { - val parser = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser() - val terser = FhirTerser(FhirContext.forCached(FhirVersionEnum.R4)) + val terser = FhirTerser(FhirContext.forR4Cached()) val searchParamMap = searchParameters?.asMapOfResourceTypeToSearchParamDefinitions() ?: emptyMap() val provider = SearchParamDefinitionsProviderImpl(searchParamMap) val db = DatabaseImpl( context = context, - iParser = parser, fhirTerser = terser, DatabaseConfig(inMemory, enableEncryption, databaseErrorStrategy), resourceIndexer = ResourceIndexer(provider), @@ -100,7 +95,6 @@ internal data class FhirServices( } return FhirServices( fhirEngine = engine, - parser = parser, database = db, remoteDataSource = remoteDataSource, fhirDataStore = FhirDataStore(context), diff --git a/engine/src/main/java/com/google/android/fhir/db/impl/DatabaseImpl.kt b/engine/src/main/java/com/google/android/fhir/db/impl/DatabaseImpl.kt index 03796f6466..1e8333ea5b 100644 --- a/engine/src/main/java/com/google/android/fhir/db/impl/DatabaseImpl.kt +++ b/engine/src/main/java/com/google/android/fhir/db/impl/DatabaseImpl.kt @@ -22,7 +22,6 @@ import androidx.room.Room import androidx.room.withTransaction import androidx.sqlite.db.SimpleSQLiteQuery import ca.uhn.fhir.context.FhirContext -import ca.uhn.fhir.parser.IParser import ca.uhn.fhir.util.FhirTerser import com.google.android.fhir.DatabaseErrorStrategy import com.google.android.fhir.LocalChange @@ -56,7 +55,6 @@ import org.hl7.fhir.r4.model.ResourceType @Suppress("UNCHECKED_CAST") internal class DatabaseImpl( private val context: Context, - private val iParser: IParser, private val fhirTerser: FhirTerser, databaseConfig: DatabaseConfig, private val resourceIndexer: ResourceIndexer, @@ -122,18 +120,9 @@ internal class DatabaseImpl( .build() } - private val resourceDao by lazy { - db.resourceDao().also { - it.iParser = iParser - it.resourceIndexer = resourceIndexer - } - } + private val resourceDao by lazy { db.resourceDao().also { it.resourceIndexer = resourceIndexer } } - private val localChangeDao = - db.localChangeDao().also { - it.iParser = iParser - it.fhirTerser = fhirTerser - } + private val localChangeDao = db.localChangeDao().also { it.fhirTerser = fhirTerser } override suspend fun insert(vararg resource: R): List { val logicalIds = mutableListOf() @@ -191,10 +180,13 @@ internal class DatabaseImpl( db.withTransaction { resourceDao.getResourceEntity(oldResourceId, resourceType)?.let { oldResourceEntity -> val updatedResource = - (iParser.parseResource(oldResourceEntity.serializedResource) as Resource).apply { - idElement = IdType(newResourceId) - updateMeta(versionId, lastUpdatedRemote) - } + (FhirContext.forR4Cached() + .newJsonParser() + .parseResource(oldResourceEntity.serializedResource) as Resource) + .apply { + idElement = IdType(newResourceId) + updateMeta(versionId, lastUpdatedRemote) + } updateResourceAndReferences(oldResourceId, updatedResource) } } @@ -202,7 +194,7 @@ internal class DatabaseImpl( override suspend fun select(type: ResourceType, id: String): Resource { return resourceDao.getResource(resourceId = id, resourceType = type)?.let { - iParser.parseResource(it) as Resource + FhirContext.forR4Cached().newJsonParser().parseResource(it) as Resource } ?: throw ResourceNotFoundException(type.name, id) } @@ -317,7 +309,10 @@ internal class DatabaseImpl( ) { db.withTransaction { val currentResourceEntity = selectEntity(updatedResource.resourceType, currentResourceId) - val oldResource = iParser.parseResource(currentResourceEntity.serializedResource) as Resource + val oldResource = + FhirContext.forR4Cached() + .newJsonParser() + .parseResource(currentResourceEntity.serializedResource) as Resource val resourceUuid = currentResourceEntity.resourceUuid updateResourceEntity(resourceUuid, updatedResource) @@ -375,6 +370,7 @@ internal class DatabaseImpl( val updatedReferenceValue = "${updatedResource.resourceType.name}/${updatedResource.logicalId}" referringResourcesUuids.forEach { resourceUuid -> resourceDao.getResourceEntity(resourceUuid)?.let { + val iParser = FhirContext.forR4Cached().newJsonParser() val referringResource = iParser.parseResource(it.serializedResource) as Resource val updatedReferringResource = addUpdatedReferenceToResource( diff --git a/engine/src/main/java/com/google/android/fhir/db/impl/dao/LocalChangeDao.kt b/engine/src/main/java/com/google/android/fhir/db/impl/dao/LocalChangeDao.kt index 68069b8ab7..158067cb85 100644 --- a/engine/src/main/java/com/google/android/fhir/db/impl/dao/LocalChangeDao.kt +++ b/engine/src/main/java/com/google/android/fhir/db/impl/dao/LocalChangeDao.kt @@ -21,6 +21,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction +import ca.uhn.fhir.context.FhirContext import ca.uhn.fhir.parser.IParser import ca.uhn.fhir.util.FhirTerser import ca.uhn.fhir.util.ResourceReferenceInfo @@ -50,8 +51,6 @@ import timber.log.Timber */ @Dao internal abstract class LocalChangeDao { - - lateinit var iParser: IParser lateinit var fhirTerser: FhirTerser @Insert(onConflict = OnConflictStrategy.REPLACE) @@ -70,7 +69,7 @@ internal abstract class LocalChangeDao { open suspend fun addInsert(resource: Resource, resourceUuid: UUID, timeOfLocalChange: Instant) { val resourceId = resource.logicalId val resourceType = resource.resourceType - val resourceString = iParser.encodeResourceToString(resource) + val resourceString = FhirContext.forR4Cached().newJsonParser().encodeResourceToString(resource) val localChangeEntity = LocalChangeEntity( @@ -128,6 +127,7 @@ internal abstract class LocalChangeDao { "Unexpected DELETE when updating $resourceType/$resourceId. UPDATE failed.", ) } + val iParser = FhirContext.forR4Cached().newJsonParser() val oldResource = iParser.parseResource(oldEntity.serializedResource) as Resource val jsonDiff = diff(iParser, oldResource, updatedResource) if (jsonDiff.length() == 0) { @@ -475,6 +475,7 @@ internal abstract class LocalChangeDao { oldReference: String, updatedReference: String, ): LocalChangeEntity { + val iParser = FhirContext.forR4Cached().newJsonParser() return when (localChange.type) { LocalChangeEntity.Type.INSERT -> { val insertResourcePayload = iParser.parseResource(localChange.payload) as Resource diff --git a/engine/src/main/java/com/google/android/fhir/db/impl/dao/ResourceDao.kt b/engine/src/main/java/com/google/android/fhir/db/impl/dao/ResourceDao.kt index 0f219d84dd..65015fc9c8 100644 --- a/engine/src/main/java/com/google/android/fhir/db/impl/dao/ResourceDao.kt +++ b/engine/src/main/java/com/google/android/fhir/db/impl/dao/ResourceDao.kt @@ -24,7 +24,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.RawQuery import androidx.sqlite.db.SupportSQLiteQuery -import ca.uhn.fhir.parser.IParser +import ca.uhn.fhir.context.FhirContext import com.google.android.fhir.db.ResourceNotFoundException import com.google.android.fhir.db.impl.entities.DateIndexEntity import com.google.android.fhir.db.impl.entities.DateTimeIndexEntity @@ -55,7 +55,6 @@ import org.hl7.fhir.r4.model.ResourceType internal abstract class ResourceDao { // this is ugly but there is no way to inject these right now in Room as it is the one creating // the dao - lateinit var iParser: IParser lateinit var resourceIndexer: ResourceIndexer /** @@ -69,7 +68,8 @@ internal abstract class ResourceDao { getResourceEntity(resource.logicalId, resource.resourceType)?.let { val entity = it.copy( - serializedResource = iParser.encodeResourceToString(resource), + serializedResource = + FhirContext.forR4Cached().newJsonParser().encodeResourceToString(resource), lastUpdatedLocal = timeOfLocalChange, lastUpdatedRemote = resource.meta.lastUpdated?.toInstant() ?: it.lastUpdatedRemote, ) @@ -86,7 +86,8 @@ internal abstract class ResourceDao { val entity = it.copy( resourceId = updatedResource.logicalId, - serializedResource = iParser.encodeResourceToString(updatedResource), + serializedResource = + FhirContext.forR4Cached().newJsonParser().encodeResourceToString(updatedResource), lastUpdatedRemote = updatedResource.lastUpdated ?: it.lastUpdatedRemote, versionId = updatedResource.versionId ?: it.versionId, ) @@ -107,7 +108,8 @@ internal abstract class ResourceDao { getResourceEntity(resource.logicalId, resource.resourceType)?.let { val entity = it.copy( - serializedResource = iParser.encodeResourceToString(resource), + serializedResource = + FhirContext.forR4Cached().newJsonParser().encodeResourceToString(resource), lastUpdatedRemote = resource.meta.lastUpdated?.toInstant(), versionId = resource.versionId, ) @@ -267,7 +269,8 @@ internal abstract class ResourceDao { resourceType = resource.resourceType, resourceUuid = resourceUuid, resourceId = resource.logicalId, - serializedResource = iParser.encodeResourceToString(resource), + serializedResource = + FhirContext.forR4Cached().newJsonParser().encodeResourceToString(resource), versionId = resource.versionId, lastUpdatedRemote = resource.lastUpdated, lastUpdatedLocal = lastUpdatedLocal, @@ -297,7 +300,10 @@ internal abstract class ResourceDao { lastUpdatedRemote: Instant?, ) { getResourceEntity(resourceId, resourceType)?.let { oldResourceEntity -> - val resource = iParser.parseResource(oldResourceEntity.serializedResource) as Resource + val resource = + FhirContext.forR4Cached() + .newJsonParser() + .parseResource(oldResourceEntity.serializedResource) as Resource resource.updateMeta(versionId, lastUpdatedRemote) updateResourceWithUuid(oldResourceEntity.resourceUuid, resource) } diff --git a/engine/src/main/java/com/google/android/fhir/sync/upload/request/UrlRequestGenerator.kt b/engine/src/main/java/com/google/android/fhir/sync/upload/request/UrlRequestGenerator.kt index 8e576760c3..d868cf2467 100644 --- a/engine/src/main/java/com/google/android/fhir/sync/upload/request/UrlRequestGenerator.kt +++ b/engine/src/main/java/com/google/android/fhir/sync/upload/request/UrlRequestGenerator.kt @@ -17,7 +17,6 @@ package com.google.android.fhir.sync.upload.request import ca.uhn.fhir.context.FhirContext -import ca.uhn.fhir.context.FhirVersionEnum import com.google.android.fhir.ContentTypes import com.google.android.fhir.sync.upload.patch.Patch import com.google.android.fhir.sync.upload.patch.PatchMapping @@ -58,8 +57,6 @@ internal class UrlRequestGenerator( companion object Factory { - private val parser = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser() - private val createMapping = mapOf( HttpVerb.POST to this::postForCreateResource, @@ -107,21 +104,24 @@ internal class UrlRequestGenerator( UrlUploadRequest( httpVerb = HttpVerb.DELETE, url = "${patch.resourceType}/${patch.resourceId}", - resource = parser.parseResource(patch.payload) as Resource, + resource = + FhirContext.forR4Cached().newJsonParser().parseResource(patch.payload) as Resource, ) private fun postForCreateResource(patch: Patch) = UrlUploadRequest( httpVerb = HttpVerb.POST, url = patch.resourceType, - resource = parser.parseResource(patch.payload) as Resource, + resource = + FhirContext.forR4Cached().newJsonParser().parseResource(patch.payload) as Resource, ) private fun putForCreateResource(patch: Patch) = UrlUploadRequest( httpVerb = HttpVerb.PUT, url = "${patch.resourceType}/${patch.resourceId}", - resource = parser.parseResource(patch.payload) as Resource, + resource = + FhirContext.forR4Cached().newJsonParser().parseResource(patch.payload) as Resource, ) private fun patchForUpdateResource(patch: Patch) = 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 31303f3e88..64bfc6ace4 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.context.FhirContext import ca.uhn.fhir.rest.gclient.TokenClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.FhirServices.Companion.builder @@ -74,6 +75,7 @@ import org.robolectric.RobolectricTestRunner class FhirEngineImplTest { private val services = builder(ApplicationProvider.getApplicationContext()).inMemory().build() private val fhirEngine = services.fhirEngine + private val parser = FhirContext.forR4Cached().newJsonParser() @Before fun setUp(): Unit = runBlocking { fhirEngine.create(TEST_PATIENT_1) } @@ -388,7 +390,7 @@ class FhirEngineImplTest { assertThat(resourceType).isEqualTo(ResourceType.Patient.toString()) assertThat(resourceId).isEqualTo(TEST_PATIENT_1.id) assertThat(type).isEqualTo(Type.INSERT) - assertThat(payload).isEqualTo(services.parser.encodeResourceToString(TEST_PATIENT_1)) + assertThat(payload).isEqualTo(parser.encodeResourceToString(TEST_PATIENT_1)) } assertThat(emittedProgress).hasSize(2) @@ -446,7 +448,7 @@ class FhirEngineImplTest { fun `getLocalChanges() should return single local change`() = runBlocking { val patient: Patient = readFromFile(Patient::class.java, "/date_test_patient.json") fhirEngine.create(patient) - val patientString = services.parser.encodeResourceToString(patient) + val patientString = parser.encodeResourceToString(patient) val resourceLocalChanges = fhirEngine.getLocalChanges(patient.resourceType, patient.logicalId) with(resourceLocalChanges) { assertThat(size).isEqualTo(1) @@ -497,7 +499,7 @@ class FhirEngineImplTest { fun `clearDatabase() should clear all tables data`() = runBlocking { val patient: Patient = readFromFile(Patient::class.java, "/date_test_patient.json") fhirEngine.create(patient) - val patientString = services.parser.encodeResourceToString(patient) + val patientString = parser.encodeResourceToString(patient) val resourceLocalChanges = fhirEngine.getLocalChanges(patient.resourceType, patient.logicalId) with(resourceLocalChanges) { assertThat(size).isEqualTo(1) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8f273e2bd1..79fc02d4d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ android-fhir-common = "0.1.0-alpha05" android-fhir-engine = "0.1.0-beta05" android-fhir-knowledge = "0.1.0-beta01" -androidx-acivity = "1.7.2" +androidx-activity = "1.7.2" androidx-appcompat = "1.6.1" androidx-arch-core = "2.2.0" androidx-benchmark = "1.1.1" @@ -37,7 +37,7 @@ material = "1.9.0" android-fhir-common = { module = "com.google.android.fhir:common", version.ref = "android-fhir-common" } android-fhir-engine = { module = "com.google.android.fhir:engine", version.ref = "android-fhir-engine" } android-fhir-knowledge = { module = "com.google.android.fhir:knowledge", version.ref = "android-fhir-knowledge" } -androidx-activity = { module = "androidx.activity:activity", version.ref = "androidx-acivity" } +androidx-activity = { module = "androidx.activity:activity", version.ref = "androidx-activity" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-arch-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "androidx-arch-core" } androidx-benchmark-junit4 = { module = "androidx.benchmark:benchmark-junit4", version.ref = "androidx-benchmark" } diff --git a/knowledge/src/main/java/com/google/android/fhir/knowledge/KnowledgeManager.kt b/knowledge/src/main/java/com/google/android/fhir/knowledge/KnowledgeManager.kt index 8066cda7d9..953b936e26 100644 --- a/knowledge/src/main/java/com/google/android/fhir/knowledge/KnowledgeManager.kt +++ b/knowledge/src/main/java/com/google/android/fhir/knowledge/KnowledgeManager.kt @@ -19,7 +19,6 @@ package com.google.android.fhir.knowledge import android.content.Context import androidx.room.Room import ca.uhn.fhir.context.FhirContext -import ca.uhn.fhir.parser.IParser import com.google.android.fhir.knowledge.db.KnowledgeDatabase import com.google.android.fhir.knowledge.db.entities.ImplementationGuideEntity import com.google.android.fhir.knowledge.db.entities.ResourceMetadataEntity @@ -63,7 +62,6 @@ internal constructor( knowledgeDatabase: KnowledgeDatabase, private val npmFileManager: NpmFileManager, private val npmPackageDownloader: NpmPackageDownloader, - private val jsonParser: IParser = FhirContext.forR4().newJsonParser(), ) { private val knowledgeDao = knowledgeDatabase.knowledgeDao() @@ -296,7 +294,7 @@ internal constructor( private suspend fun readResourceOrNull(file: File): IBaseResource? = withContext(Dispatchers.IO) { try { - FileInputStream(file).use(jsonParser::parseResource) + FileInputStream(file).use(FhirContext.forR4Cached().newJsonParser()::parseResource) } catch (e: Exception) { Timber.e(e, "Unable to load resource from $file") null From dc44a44a6ab380b3ed73e9234376ae951f281b0f Mon Sep 17 00:00:00 2001 From: santosh-pingle <86107848+santosh-pingle@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:07:47 +0530 Subject: [PATCH 2/4] perform parsing on datetype value. (#2723) Co-authored-by: Santosh Pingle --- .../com/google/android/fhir/demo/PatientListViewModel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/demo/src/main/java/com/google/android/fhir/demo/PatientListViewModel.kt b/demo/src/main/java/com/google/android/fhir/demo/PatientListViewModel.kt index 55c85a4d33..fbf7b43fae 100644 --- a/demo/src/main/java/com/google/android/fhir/demo/PatientListViewModel.kt +++ b/demo/src/main/java/com/google/android/fhir/demo/PatientListViewModel.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. @@ -28,7 +28,7 @@ import com.google.android.fhir.search.StringFilterModifier import com.google.android.fhir.search.count import com.google.android.fhir.search.search import java.time.LocalDate -import java.time.format.DateTimeFormatter +import java.time.ZoneId import kotlinx.coroutines.launch import org.hl7.fhir.r4.model.Patient import org.hl7.fhir.r4.model.RiskAssessment @@ -183,7 +183,7 @@ internal fun Patient.toPatientItem(position: Int): PatientListViewModel.PatientI val gender = if (hasGenderElement()) genderElement.valueAsString else "" val dob = if (hasBirthDateElement()) { - LocalDate.parse(birthDateElement.valueAsString, DateTimeFormatter.ISO_DATE) + birthDateElement.value.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() } else { null } From 736578fb4e2db009d096ebd7a84ae546ac2ace95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:13:41 +0300 Subject: [PATCH 3/4] Prefer use of `resourceUuid` index over `resourceType` when selecting from `resourceentity` table (#2739) * Remove resourceType when searching with resourceUuid * Remove inOrder assertion for search results not sorted * Fix NumberSearchParameterizedTest with correct query * Add SearchResultCorrespondenceUnorderedIncludeRevInclude Switching from resourceType multi-column index to resourceUuid, the order of results in include/revInclude is no longer predictable since the resourceUuids are randomly generated, also saved in the db as blob and hence ordered by byte representation of the resourceUuid * Refactor filterStatement to include ifBlank * Revert add Unit type in DatabaseImplTest test methods --------- Co-authored-by: Benjamin Mwalimu --- .../android/fhir/db/impl/DatabaseImplTest.kt | 382 ++++++++++-------- .../google/android/fhir/search/MoreSearch.kt | 61 +-- .../android/fhir/search/NestedSearch.kt | 2 +- .../search/NumberSearchParameterizedTest.kt | 12 +- .../google/android/fhir/search/SearchTest.kt | 278 ++++--------- 5 files changed, 323 insertions(+), 412 deletions(-) diff --git a/engine/src/androidTest/java/com/google/android/fhir/db/impl/DatabaseImplTest.kt b/engine/src/androidTest/java/com/google/android/fhir/db/impl/DatabaseImplTest.kt index 1807b4b2d6..e8e7876358 100644 --- a/engine/src/androidTest/java/com/google/android/fhir/db/impl/DatabaseImplTest.kt +++ b/engine/src/androidTest/java/com/google/android/fhir/db/impl/DatabaseImplTest.kt @@ -2633,92 +2633,93 @@ class DatabaseImplTest { } @Test - fun search_filter_param_values_disjunction_covid_immunization_records() = runBlocking { - val resources = - listOf( - Immunization().apply { - id = "immunization-1" - vaccineCode = - CodeableConcept( - Coding( - "http://id.who.int/icd11/mms", - "XM1NL1", - "COVID-19 vaccine, inactivated virus", - ), - ) - status = Immunization.ImmunizationStatus.COMPLETED - }, - Immunization().apply { - id = "immunization-2" - vaccineCode = - CodeableConcept( - Coding( - "http://id.who.int/icd11/mms", - "XM5DF6", - "COVID-19 vaccine, live attenuated virus", - ), - ) - status = Immunization.ImmunizationStatus.COMPLETED - }, - Immunization().apply { - id = "immunization-3" - vaccineCode = - CodeableConcept( - Coding("http://id.who.int/icd11/mms", "XM6AT1", "COVID-19 vaccine, DNA based"), - ) - status = Immunization.ImmunizationStatus.COMPLETED - }, - Immunization().apply { - id = "immunization-4" - vaccineCode = - CodeableConcept( - Coding( - "http://hl7.org/fhir/sid/cvx", - "140", - "Influenza, seasonal, injectable, preservative free", - ), - ) - status = Immunization.ImmunizationStatus.COMPLETED - }, - ) + fun search_filter_param_values_disjunction_covid_immunization_records() { + runBlocking { + val resources = + listOf( + Immunization().apply { + id = "immunization-1" + vaccineCode = + CodeableConcept( + Coding( + "http://id.who.int/icd11/mms", + "XM1NL1", + "COVID-19 vaccine, inactivated virus", + ), + ) + status = Immunization.ImmunizationStatus.COMPLETED + }, + Immunization().apply { + id = "immunization-2" + vaccineCode = + CodeableConcept( + Coding( + "http://id.who.int/icd11/mms", + "XM5DF6", + "COVID-19 vaccine, live attenuated virus", + ), + ) + status = Immunization.ImmunizationStatus.COMPLETED + }, + Immunization().apply { + id = "immunization-3" + vaccineCode = + CodeableConcept( + Coding("http://id.who.int/icd11/mms", "XM6AT1", "COVID-19 vaccine, DNA based"), + ) + status = Immunization.ImmunizationStatus.COMPLETED + }, + Immunization().apply { + id = "immunization-4" + vaccineCode = + CodeableConcept( + Coding( + "http://hl7.org/fhir/sid/cvx", + "140", + "Influenza, seasonal, injectable, preservative free", + ), + ) + status = Immunization.ImmunizationStatus.COMPLETED + }, + ) - database.insert(*resources.toTypedArray()) + database.insert(*resources.toTypedArray()) - val result = - database.search( - Search(ResourceType.Immunization) - .apply { - filter( - Immunization.VACCINE_CODE, - { - value = - of( - Coding( - "http://id.who.int/icd11/mms", - "XM1NL1", - "COVID-19 vaccine, inactivated virus", - ), - ) - }, - { - value = - of( - Coding( - "http://id.who.int/icd11/mms", - "XM5DF6", - "COVID-19 vaccine, inactivated virus", - ), - ) - }, - operation = Operation.OR, - ) - } - .getQuery(), - ) + val result = + database.search( + Search(ResourceType.Immunization) + .apply { + filter( + Immunization.VACCINE_CODE, + { + value = + of( + Coding( + "http://id.who.int/icd11/mms", + "XM1NL1", + "COVID-19 vaccine, inactivated virus", + ), + ) + }, + { + value = + of( + Coding( + "http://id.who.int/icd11/mms", + "XM5DF6", + "COVID-19 vaccine, inactivated virus", + ), + ) + }, + operation = Operation.OR, + ) + } + .getQuery(), + ) - assertThat(result.map { it.resource.vaccineCode.codingFirstRep.code }) - .containsExactly("XM1NL1", "XM5DF6") - .inOrder() + assertThat(result.map { it.resource.vaccineCode.codingFirstRep.code }) + .containsExactly("XM1NL1", "XM5DF6") + } } @Test @@ -2797,95 +2798,96 @@ class DatabaseImplTest { } @Test - fun test_search_multiple_param_conjunction_with_multiple_values_disjunction() = runBlocking { - val resources = - listOf( - Patient().apply { - id = "patient-01" - addName( - HumanName().apply { - addGiven("John") - family = "Doe" - }, - ) - }, - Patient().apply { - id = "patient-02" - addName( - HumanName().apply { - addGiven("Jane") - family = "Doe" - }, - ) - }, - Patient().apply { - id = "patient-03" - addName( - HumanName().apply { - addGiven("John") - family = "Roe" - }, - ) - }, - Patient().apply { - id = "patient-04" - addName( - HumanName().apply { - addGiven("Jane") - family = "Roe" - }, - ) - }, - Patient().apply { - id = "patient-05" - addName( - HumanName().apply { - addGiven("Rocky") - family = "Balboa" - }, - ) - }, - ) - database.insert(*resources.toTypedArray()) - - val result = - database.search( - Search(ResourceType.Patient) - .apply { - filter( - Patient.GIVEN, - { - value = "John" - modifier = StringFilterModifier.MATCHES_EXACTLY + fun test_search_multiple_param_conjunction_with_multiple_values_disjunction() { + runBlocking { + val resources = + listOf( + Patient().apply { + id = "patient-01" + addName( + HumanName().apply { + addGiven("John") + family = "Doe" }, - { - value = "Jane" - modifier = StringFilterModifier.MATCHES_EXACTLY + ) + }, + Patient().apply { + id = "patient-02" + addName( + HumanName().apply { + addGiven("Jane") + family = "Doe" }, - operation = Operation.OR, ) - - filter( - Patient.FAMILY, - { - value = "Doe" - modifier = StringFilterModifier.MATCHES_EXACTLY + }, + Patient().apply { + id = "patient-03" + addName( + HumanName().apply { + addGiven("John") + family = "Roe" }, - { - value = "Roe" - modifier = StringFilterModifier.MATCHES_EXACTLY + ) + }, + Patient().apply { + id = "patient-04" + addName( + HumanName().apply { + addGiven("Jane") + family = "Roe" + }, + ) + }, + Patient().apply { + id = "patient-05" + addName( + HumanName().apply { + addGiven("Rocky") + family = "Balboa" }, - operation = Operation.OR, ) + }, + ) + database.insert(*resources.toTypedArray()) - operation = Operation.AND - } - .getQuery(), - ) + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter( + Patient.GIVEN, + { + value = "John" + modifier = StringFilterModifier.MATCHES_EXACTLY + }, + { + value = "Jane" + modifier = StringFilterModifier.MATCHES_EXACTLY + }, + operation = Operation.OR, + ) - assertThat(result.map { it.resource.nameFirstRep.nameAsSingleString }) - .containsExactly("John Doe", "Jane Doe", "John Roe", "Jane Roe") - .inOrder() + filter( + Patient.FAMILY, + { + value = "Doe" + modifier = StringFilterModifier.MATCHES_EXACTLY + }, + { + value = "Roe" + modifier = StringFilterModifier.MATCHES_EXACTLY + }, + operation = Operation.OR, + ) + + operation = Operation.AND + } + .getQuery(), + ) + + assertThat(result.map { it.resource.nameFirstRep.nameAsSingleString }) + .containsExactly("John Doe", "Jane Doe", "John Roe", "Jane Roe") + } } @Test @@ -3137,7 +3139,6 @@ class DatabaseImplTest { revIncluded = null, ), ) - .inOrder() } @Test @@ -3230,7 +3231,6 @@ class DatabaseImplTest { mapOf((ResourceType.Condition to Condition.SUBJECT.paramName) to listOf(con3)), ), ) - .inOrder() } @Test @@ -3534,7 +3534,7 @@ class DatabaseImplTest { .execute(database) assertThat(result) - .comparingElementsUsing(SearchResultCorrespondence) + .comparingElementsUsing(SearchResultCorrespondenceUnorderedIncludeRevInclude) .displayingDiffsPairedBy { it.resource.logicalId } .containsExactly( SearchResult( @@ -3576,7 +3576,6 @@ class DatabaseImplTest { ), ), ) - .inOrder() } @Test @@ -3695,11 +3694,10 @@ class DatabaseImplTest { revIncluded = null, ), ) - .inOrder() } @Test - fun search_patient_and_revinclude_person_should_map_common_person_to_all_matching_patients() = + fun search_patient_and_revinclude_person_should_map_common_person_to_all_matching_patients() { runBlocking { val person1 = Person().apply { @@ -3813,8 +3811,8 @@ class DatabaseImplTest { mapOf(Pair(ResourceType.Person, Person.LINK.paramName) to listOf(person2, person3)), ), ) - .inOrder() } + } @Test fun search_patient_and_revInclude_encounters_sorted_by_date_descending(): Unit = runBlocking { @@ -5239,6 +5237,18 @@ class DatabaseImplTest { ) .formattingDiffsUsing(::formatDiff) + /** + * [Correspondence] to provide a custom [equalityCheck] for the [SearchResult]s whereby + * [SearchResult.included] and [SearchResult.revIncluded] may not be in the correct order + */ + val SearchResultCorrespondenceUnorderedIncludeRevInclude: + Correspondence, SearchResult> = + Correspondence.from, SearchResult>( + ::equalityCheckUnordered, + "is shallow equals (by logical id comparison) to the ", + ) + .formattingDiffsUsing(::formatDiff) + private fun equalityCheck( actual: SearchResult, expected: SearchResult, @@ -5248,6 +5258,15 @@ class DatabaseImplTest { equalsShallow(actual.revIncluded, expected.revIncluded) } + private fun equalityCheckUnordered( + actual: SearchResult, + expected: SearchResult, + ): Boolean { + return equalsShallow(actual.resource, expected.resource) && + equalsShallow(actual.included, expected.included, inOrder = false) && + equalsShallow(actual.revIncluded, expected.revIncluded, inOrder = false) + } + private fun equalsShallow(first: Resource, second: Resource) = first.resourceType == second.resourceType && first.logicalId == second.logicalId @@ -5255,13 +5274,24 @@ class DatabaseImplTest { first.size == second.size && first.asSequence().zip(second.asSequence()).all { (x, y) -> equalsShallow(x, y) } + private fun resourceTypeAndIdEqualUnordered(first: List, second: List) = + first.size == second.size && + first.map { it.resourceType to it.logicalId }.toSet() == + second.map { it.resourceType to it.logicalId }.toSet() + private fun equalsShallow( first: Map>?, second: Map>?, + inOrder: Boolean = true, ) = if (first != null && second != null && first.size == second.size) { first.entries.asSequence().zip(second.entries.asSequence()).all { (x, y) -> - x.key == y.key && equalsShallow(x.value, y.value) + x.key == y.key && + if (inOrder) { + equalsShallow(x.value, y.value) + } else { + resourceTypeAndIdEqualUnordered(x.value, y.value) + } } } else { first?.size == second?.size @@ -5271,10 +5301,16 @@ class DatabaseImplTest { private fun equalsShallow( first: Map, List>?, second: Map, List>?, + inOrder: Boolean = true, ) = if (first != null && second != null && first.size == second.size) { first.entries.asSequence().zip(second.entries.asSequence()).all { (x, y) -> - x.key == y.key && equalsShallow(x.value, y.value) + x.key == y.key && + if (inOrder) { + equalsShallow(x.value, y.value) + } else { + resourceTypeAndIdEqualUnordered(x.value, y.value) + } } } else { first?.size == second?.size diff --git a/engine/src/main/java/com/google/android/fhir/search/MoreSearch.kt b/engine/src/main/java/com/google/android/fhir/search/MoreSearch.kt index e12919b8d7..daa577b68f 100644 --- a/engine/src/main/java/com/google/android/fhir/search/MoreSearch.kt +++ b/engine/src/main/java/com/google/android/fhir/search/MoreSearch.kt @@ -116,7 +116,6 @@ internal fun Search.getRevIncludeQuery(includeIds: List): SearchQuery { args.add(resourceToInclude.name) args.add(param.paramName) args.addAll(includeIds) - args.add(resourceToInclude.name) var filterQuery = "" val filters = search.getFilterQueries() @@ -136,6 +135,7 @@ internal fun Search.getRevIncludeQuery(includeIds: List): SearchQuery { } } } + if (filters.isEmpty()) args.add(resourceToInclude.name) return filterQuery } @@ -151,8 +151,8 @@ internal fun Search.getRevIncludeQuery(includeIds: List): SearchQuery { JOIN ReferenceIndexEntity rie ON re.resourceUuid = rie.resourceUuid ${join.query} - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN ($uuidsString) AND re.resourceType = ? - ${if (filterQuery.isNotEmpty()) "AND re.resourceUuid IN ($filterQuery)" else ""} + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN ($uuidsString) + ${if (filterQuery.isNotBlank()) "AND re.resourceUuid IN ($filterQuery)" else "AND re.resourceType = ?"} $order """ .trimIndent() @@ -179,7 +179,6 @@ internal fun Search.getIncludeQuery(includeIds: List): SearchQuery { args.add(baseResourceType.name) args.add(param.paramName) args.addAll(includeIds.map { convertUUIDToByte(it) }) - args.add(resourceToInclude.name) var filterQuery = "" val filters = search.getFilterQueries() @@ -199,6 +198,7 @@ internal fun Search.getIncludeQuery(includeIds: List): SearchQuery { } } } + if (filters.isEmpty()) args.add(resourceToInclude.name) return filterQuery } @@ -214,8 +214,8 @@ internal fun Search.getIncludeQuery(includeIds: List): SearchQuery { JOIN ReferenceIndexEntity rie ON re.resourceType||"/"||re.resourceId = rie.index_value ${join.query} - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN ($uuidsString) AND re.resourceType = ? - ${if (filterQuery.isNotEmpty()) "AND re.resourceUuid IN ($filterQuery)" else ""} + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN ($uuidsString) + ${if (filterQuery.isNotBlank()) "AND re.resourceUuid IN ($filterQuery)" else "AND re.resourceType = ?"} $order """ .trimIndent() @@ -363,21 +363,19 @@ internal fun Search.getQuery( val sortOrderStatement = order val sortArgs = join.args - var filterStatement = "" - val filterArgs = mutableListOf() val filterQuery = getFilterQueries() - filterQuery.forEachIndexed { i, it -> - filterStatement += + val filterQueryStatement = + filterQuery.joinToString(separator = "${operation.logicalOperator} ") { // spotless:off - """ - ${if (i == 0) "AND a.resourceUuid IN (" else "a.resourceUuid IN ("} + """ + a.resourceUuid IN ( ${it.query} ) - ${if (i != filterQuery.lastIndex) "${operation.logicalOperator} " else ""} + """.trimIndent() // spotless:on - filterArgs.addAll(it.args) - } + } + val filterQueryArgs = filterQuery.flatMap { it.args } var limitStatement = "" val limitArgs = mutableListOf() @@ -390,10 +388,20 @@ internal fun Search.getQuery( } } - nestedSearches.nestedQuery(type, operation)?.let { - filterStatement += it.query - filterArgs.addAll(it.args) - } + val nestedFilterQuery = nestedSearches.nestedQuery(type, operation) + val nestedQueryFilterStatement = nestedFilterQuery?.query ?: "" + val nestedQueryFilterArgs = nestedFilterQuery?.args ?: emptyList() + + // Combines filter statements derived from filter queries and nested queries, that use the + // resourceUuid field, + // and defaults to filter statement with the resourceType field when blank + val filterStatement = + listOf(filterQueryStatement, nestedQueryFilterStatement) + .filter { it.isNotBlank() } + .joinToString(separator = " AND ") + .ifBlank { "a.resourceType = ?" } + val filterArgs = (filterQueryArgs + nestedQueryFilterArgs).ifEmpty { listOf(type.name) } + val whereArgs = mutableListOf() val nestedArgs = mutableListOf() val query = @@ -404,8 +412,7 @@ internal fun Search.getQuery( SELECT COUNT(*) FROM ResourceEntity a $sortJoinStatement - WHERE a.resourceType = ? - $filterStatement + WHERE $filterStatement $sortOrderStatement $limitStatement """ @@ -423,8 +430,7 @@ internal fun Search.getQuery( SELECT substr(a.index_value, $start) FROM ReferenceIndexEntity a $sortJoinStatement - WHERE a.resourceType = ? AND a.index_name = ? - $filterStatement + WHERE a.index_name = ? AND $filterStatement $sortOrderStatement $limitStatement) """ @@ -436,8 +442,7 @@ internal fun Search.getQuery( SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a $sortJoinStatement - WHERE a.resourceType = ? - $filterStatement + WHERE $filterStatement $sortOrderStatement $limitStatement """ @@ -446,7 +451,11 @@ internal fun Search.getQuery( .split("\n") .filter { it.isNotBlank() } .joinToString("\n") { it.trim() } - return SearchQuery(query, nestedArgs + sortArgs + type.name + whereArgs + filterArgs + limitArgs) + + return SearchQuery( + query, + nestedArgs + sortArgs + whereArgs + filterArgs + limitArgs, + ) } private val Order?.sqlString: String diff --git a/engine/src/main/java/com/google/android/fhir/search/NestedSearch.kt b/engine/src/main/java/com/google/android/fhir/search/NestedSearch.kt index 4818680ec5..1f39a00c32 100644 --- a/engine/src/main/java/com/google/android/fhir/search/NestedSearch.kt +++ b/engine/src/main/java/com/google/android/fhir/search/NestedSearch.kt @@ -219,7 +219,7 @@ internal fun List.nestedQuery( SearchQuery( query = searchQueries.joinToString( - prefix = "AND a.resourceUuid IN ", + prefix = "a.resourceUuid IN ", separator = " ${operation.logicalOperator} a.resourceUuid IN", ) { searchQuery -> "(\n${searchQuery.query}\n) " diff --git a/engine/src/test/java/com/google/android/fhir/search/NumberSearchParameterizedTest.kt b/engine/src/test/java/com/google/android/fhir/search/NumberSearchParameterizedTest.kt index 7dcb4e177d..a7f597bedb 100644 --- a/engine/src/test/java/com/google/android/fhir/search/NumberSearchParameterizedTest.kt +++ b/engine/src/test/java/com/google/android/fhir/search/NumberSearchParameterizedTest.kt @@ -53,8 +53,7 @@ class NumberSearchParameterizedTest( """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM NumberIndexEntity """ .trimIndent() @@ -86,7 +85,6 @@ class NumberSearchParameterizedTest( assertThat(search.args) .isEqualTo( listOf( - ResourceType.RiskAssessment.name, ResourceType.RiskAssessment.name, RiskAssessment.PROBABILITY.paramName, lowerBound.toDouble(), @@ -122,7 +120,6 @@ class NumberSearchParameterizedTest( assertThat(search.args) .isEqualTo( listOf( - ResourceType.RiskAssessment.name, ResourceType.RiskAssessment.name, RiskAssessment.PROBABILITY.paramName, lowerBound.toDouble(), @@ -158,7 +155,6 @@ class NumberSearchParameterizedTest( assertThat(search.args) .isEqualTo( listOf( - ResourceType.RiskAssessment.name, ResourceType.RiskAssessment.name, RiskAssessment.PROBABILITY.paramName, num.toDouble(), @@ -193,7 +189,6 @@ class NumberSearchParameterizedTest( assertThat(search.args) .isEqualTo( listOf( - ResourceType.RiskAssessment.name, ResourceType.RiskAssessment.name, RiskAssessment.PROBABILITY.paramName, num.toDouble(), @@ -228,7 +223,6 @@ class NumberSearchParameterizedTest( assertThat(search.args) .isEqualTo( listOf( - ResourceType.RiskAssessment.name, ResourceType.RiskAssessment.name, RiskAssessment.PROBABILITY.paramName, num.toDouble(), @@ -262,7 +256,6 @@ class NumberSearchParameterizedTest( assertThat(search.args) .isEqualTo( listOf( - ResourceType.RiskAssessment.name, ResourceType.RiskAssessment.name, RiskAssessment.PROBABILITY.paramName, num.toDouble(), @@ -317,7 +310,6 @@ class NumberSearchParameterizedTest( assertThat(search.args) .isEqualTo( listOf( - ResourceType.RiskAssessment.name, ResourceType.RiskAssessment.name, RiskAssessment.PROBABILITY.paramName, num.toDouble(), @@ -372,7 +364,6 @@ class NumberSearchParameterizedTest( assertThat(search.args) .isEqualTo( listOf( - ResourceType.RiskAssessment.name, ResourceType.RiskAssessment.name, RiskAssessment.PROBABILITY.paramName, num.toDouble(), @@ -407,7 +398,6 @@ class NumberSearchParameterizedTest( assertThat(search.args) .isEqualTo( listOf( - ResourceType.RiskAssessment.name, ResourceType.RiskAssessment.name, RiskAssessment.PROBABILITY.paramName, 0.09, 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 6fea58fc2a..2ddb427a55 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 @@ -156,8 +156,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?) ) @@ -178,7 +177,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, diffStart, @@ -209,8 +207,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND index_from > ? ) @@ -221,7 +218,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.epochDay, @@ -249,8 +245,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND index_to < ? ) @@ -261,7 +256,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.epochDay, @@ -289,8 +283,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ?) ) @@ -301,7 +294,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateType("2013-03-14").value.epochDay, @@ -324,8 +316,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?) ) @@ -336,7 +327,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateType("2013-03-14").value.epochDay, @@ -367,8 +357,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND index_to > ? ) @@ -379,7 +368,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.epochDay, @@ -407,8 +395,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND index_to >= ? ) @@ -419,7 +406,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.epochDay, @@ -447,8 +433,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND index_from < ? ) @@ -459,7 +444,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateType("2013-03-14").value.epochDay, @@ -487,8 +471,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND index_from <= ? ) @@ -499,7 +482,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateType("2013-03-14").value.epochDay, @@ -532,8 +514,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateTimeIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?) ) @@ -555,7 +536,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, diffStart, @@ -586,8 +566,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateTimeIndexEntity WHERE resourceType = ? AND index_name = ? AND index_from > ? ) @@ -598,7 +577,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-15").value.time - 1, @@ -626,8 +604,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateTimeIndexEntity WHERE resourceType = ? AND index_name = ? AND index_to < ? ) @@ -638,7 +615,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.time, @@ -666,8 +642,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateTimeIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ?) ) @@ -678,7 +653,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.time, @@ -709,8 +683,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateTimeIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?) ) @@ -721,7 +694,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.time, @@ -752,8 +724,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateTimeIndexEntity WHERE resourceType = ? AND index_name = ? AND index_to > ? ) @@ -764,7 +735,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-15").value.time - 1, @@ -792,8 +762,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateTimeIndexEntity WHERE resourceType = ? AND index_name = ? AND index_to >= ? ) @@ -804,7 +773,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.time, @@ -832,8 +800,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateTimeIndexEntity WHERE resourceType = ? AND index_name = ? AND index_from < ? ) @@ -844,7 +811,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.time, @@ -872,8 +838,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM DateTimeIndexEntity WHERE resourceType = ? AND index_name = ? AND index_from <= ? ) @@ -884,7 +849,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-15").value.time - 1, @@ -904,8 +868,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM StringIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value LIKE ? || '%' COLLATE NOCASE ) @@ -914,7 +877,6 @@ class SearchTest { ) assertThat(query.args) .containsExactly( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.ADDRESS.paramName, "someValue", @@ -941,8 +903,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM StringIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -951,7 +912,6 @@ class SearchTest { ) assertThat(query.args) .containsExactly( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.ADDRESS.paramName, "someValue", @@ -978,8 +938,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM StringIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value LIKE '%' || ? || '%' COLLATE NOCASE ) @@ -988,7 +947,6 @@ class SearchTest { ) assertThat(query.args) .containsExactly( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.ADDRESS.paramName, "someValue", @@ -1015,8 +973,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) @@ -1026,7 +983,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.GENDER.paramName, "male", @@ -1055,8 +1011,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) @@ -1066,7 +1021,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Immunization.name, ResourceType.Immunization.name, Immunization.VACCINE_CODE.paramName, "260385009", @@ -1090,8 +1044,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) @@ -1101,7 +1054,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.IDENTIFIER.paramName, "12345", @@ -1135,8 +1087,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) @@ -1146,7 +1097,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.TELECOM.paramName, "test@gmail.com", @@ -1179,8 +1129,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -1190,7 +1139,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.TELECOM.paramName, "test@gmail.com", @@ -1209,8 +1157,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -1220,7 +1167,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.GENDER.paramName, "male", @@ -1238,8 +1184,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -1249,7 +1194,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.ACTIVE.paramName, "true", @@ -1274,8 +1218,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -1285,7 +1228,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.IDENTIFIER.paramName, "16009886-bd57-11eb-8529-0242ac130003", @@ -1305,8 +1247,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -1316,7 +1257,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.PHONE.paramName, "+14845219791", @@ -1344,8 +1284,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM QuantityIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_code = ? AND index_value >= ? AND index_value < ?) ) @@ -1355,7 +1294,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOfNotNull( - ResourceType.Observation.name, ResourceType.Observation.name, Observation.VALUE_QUANTITY.paramName, "g", @@ -1385,8 +1323,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM QuantityIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_code = ? AND index_value < ?) ) @@ -1396,7 +1333,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOfNotNull( - ResourceType.Observation.name, ResourceType.Observation.name, Observation.VALUE_QUANTITY.paramName, "g", @@ -1425,8 +1361,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM QuantityIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_system = ? AND index_value <= ?) ) @@ -1436,7 +1371,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOfNotNull( - ResourceType.Observation.name, ResourceType.Observation.name, Observation.VALUE_QUANTITY.paramName, "http://unitsofmeasure.org", @@ -1465,8 +1399,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM QuantityIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_system = ? AND index_value > ?) ) @@ -1476,7 +1409,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOfNotNull( - ResourceType.Observation.name, ResourceType.Observation.name, Observation.VALUE_QUANTITY.paramName, "http://unitsofmeasure.org", @@ -1504,8 +1436,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM QuantityIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value >= ? ) @@ -1515,7 +1446,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOfNotNull( - ResourceType.Observation.name, ResourceType.Observation.name, Observation.VALUE_QUANTITY.paramName, BigDecimal("5.403").toDouble(), @@ -1542,8 +1472,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM QuantityIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value < ? OR index_value >= ?) ) @@ -1553,7 +1482,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOfNotNull( - ResourceType.Observation.name, ResourceType.Observation.name, Observation.VALUE_QUANTITY.paramName, BigDecimal("5.4025").toDouble(), @@ -1581,8 +1509,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM QuantityIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value > ? ) @@ -1592,7 +1519,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOfNotNull( - ResourceType.Observation.name, ResourceType.Observation.name, Observation.VALUE_QUANTITY.paramName, BigDecimal("5.403").toDouble(), @@ -1619,8 +1545,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM QuantityIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value < ? ) @@ -1630,7 +1555,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOfNotNull( - ResourceType.Observation.name, ResourceType.Observation.name, Observation.VALUE_QUANTITY.paramName, BigDecimal("5.403").toDouble(), @@ -1659,8 +1583,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM QuantityIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_system = ? AND index_code = ? AND index_value >= ? AND index_value < ?) ) @@ -1670,7 +1593,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOfNotNull( - ResourceType.Observation.name, ResourceType.Observation.name, Observation.VALUE_QUANTITY.paramName, "http://unitsofmeasure.org", @@ -1691,8 +1613,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM UriIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -1701,7 +1622,6 @@ class SearchTest { ) assertThat(query.args) .containsExactly( - ResourceType.Library.name, ResourceType.Library.name, Library.URL.paramName, "someValue", @@ -1794,8 +1714,7 @@ class SearchTest { FROM ResourceEntity a LEFT JOIN StringIndexEntity b ON a.resourceUuid = b.resourceUuid AND b.index_name = ? - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM StringIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value LIKE ? || '%' COLLATE NOCASE ) @@ -1811,7 +1730,6 @@ class SearchTest { listOf( Patient.GIVEN.paramName, ResourceType.Patient.name, - ResourceType.Patient.name, Patient.FAMILY.paramName, "Jones", 10, @@ -1839,15 +1757,13 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM ResourceEntity a WHERE a.resourceType = ? AND a.resourceId IN ( SELECT substr(a.index_value, 9) FROM ReferenceIndexEntity a - WHERE a.resourceType = ? AND a.index_name = ? - AND a.resourceUuid IN ( + WHERE a.index_name = ? AND a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) @@ -1861,8 +1777,6 @@ class SearchTest { .isEqualTo( listOf( ResourceType.Patient.name, - ResourceType.Patient.name, - ResourceType.Condition.name, Condition.SUBJECT.paramName, ResourceType.Condition.name, Condition.CODE.paramName, @@ -1915,8 +1829,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM StringIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -1926,8 +1839,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT substr(a.index_value, 9) FROM ReferenceIndexEntity a - WHERE a.resourceType = ? AND a.index_name = ? - AND a.resourceUuid IN ( + WHERE a.index_name = ? AND a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) @@ -1944,12 +1856,10 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - ResourceType.Patient.name, ResourceType.Patient.name, Patient.ADDRESS_COUNTRY.paramName, "IN", ResourceType.Patient.name, - ResourceType.Immunization.name, Immunization.PATIENT.paramName, ResourceType.Immunization.name, Immunization.VACCINE_CODE.paramName, @@ -1964,7 +1874,7 @@ class SearchTest { } @Test - fun practitioner_has_patient_has_condition_diabetes_and_hypertension() { + fun search_has_patient_has_condition_diabetes_and_hypertension() { val query = Search(ResourceType.Patient) .apply { @@ -1988,15 +1898,13 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM ResourceEntity a WHERE a.resourceType = ? AND a.resourceId IN ( SELECT substr(a.index_value, 9) FROM ReferenceIndexEntity a - WHERE a.resourceType = ? AND a.index_name = ? - AND a.resourceUuid IN ( + WHERE a.index_name = ? AND a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) @@ -2007,8 +1915,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT substr(a.index_value, 9) FROM ReferenceIndexEntity a - WHERE a.resourceType = ? AND a.index_name = ? - AND a.resourceUuid IN ( + WHERE a.index_name = ? AND a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) @@ -2022,15 +1929,12 @@ class SearchTest { .isEqualTo( listOf( ResourceType.Patient.name, - ResourceType.Patient.name, - ResourceType.Condition.name, Condition.SUBJECT.paramName, ResourceType.Condition.name, Condition.CODE.paramName, "44054006", "http://snomed.info/sct", ResourceType.Patient.name, - ResourceType.Condition.name, Condition.SUBJECT.paramName, ResourceType.Condition.name, Condition.CODE.paramName, @@ -2111,8 +2015,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM StringIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? OR index_value = ?) ) @@ -2120,7 +2023,7 @@ class SearchTest { .trimIndent(), ) - assertThat(query.args).isEqualTo(listOf("Patient", "Patient", "given", "John", "Jane")) + assertThat(query.args).isEqualTo(listOf("Patient", "given", "John", "Jane")) } @Test @@ -2152,8 +2055,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM StringIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -2165,8 +2067,7 @@ class SearchTest { .trimIndent(), ) - assertThat(query.args) - .isEqualTo(listOf("Patient", "Patient", "given", "John", "Patient", "given", "Jane")) + assertThat(query.args).isEqualTo(listOf("Patient", "given", "John", "Patient", "given", "Jane")) } // Test for https://github.com/google/android-fhir/issues/903 @@ -2185,8 +2086,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM StringIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value LIKE ? || '%' COLLATE NOCASE ) @@ -2199,7 +2099,7 @@ class SearchTest { ) assertThat(query.args) - .isEqualTo(listOf("Patient", "Patient", "given", "John", "Patient", "family", "Doe", "Roe")) + .isEqualTo(listOf("Patient", "given", "John", "Patient", "family", "Doe", "Roe")) } @Test @@ -2216,8 +2116,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -2225,8 +2124,7 @@ class SearchTest { .trimIndent(), ) - assertThat(query.args) - .isEqualTo(listOf("Condition", "Condition", "clinical-status", "test-code")) + assertThat(query.args).isEqualTo(listOf("Condition", "clinical-status", "test-code")) } @Test @@ -2254,8 +2152,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) ) @@ -2266,7 +2163,6 @@ class SearchTest { assertThat(query.args) .isEqualTo( listOf( - "Condition", "Condition", "clinical-status", "test-code", @@ -2295,7 +2191,8 @@ class SearchTest { FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceType||"/"||re.resourceId = rie.index_value - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) + AND re.resourceType = ? ) """ .trimIndent(), @@ -2337,7 +2234,7 @@ class SearchTest { FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceType||"/"||re.resourceId = rie.index_value - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) AND re.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? @@ -2355,7 +2252,6 @@ class SearchTest { convertUUIDToByte(UUID.fromString("e2c79e28-ed4d-2029-a12c-108d1eb5bedb")), convertUUIDToByte(UUID.fromString("e2c79e28-ed4d-4029-a12c-108d1eb5bedb")), "Practitioner", - "Practitioner", "active", "true", ) @@ -2389,7 +2285,7 @@ class SearchTest { ON re.resourceType||"/"||re.resourceId = rie.index_value LEFT JOIN StringIndexEntity b ON re.resourceUuid = b.resourceUuid AND b.index_name = ? - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) AND re.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? @@ -2411,7 +2307,6 @@ class SearchTest { convertUUIDToByte(UUID.fromString("e2c79e28-ed4d-2029-a12c-108d1eb5bedb")), convertUUIDToByte(UUID.fromString("e2c79e28-ed4d-4029-a12c-108d1eb5bedb")), "Practitioner", - "Practitioner", "active", "true", ) @@ -2450,7 +2345,7 @@ class SearchTest { ON re.resourceType||"/"||re.resourceId = rie.index_value LEFT JOIN StringIndexEntity b ON re.resourceUuid = b.resourceUuid AND b.index_name = ? - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) AND re.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? @@ -2467,7 +2362,7 @@ class SearchTest { ON re.resourceType||"/"||re.resourceId = rie.index_value LEFT JOIN StringIndexEntity b ON re.resourceUuid = b.resourceUuid AND b.index_name = ? - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.resourceUuid IN (?, ?) AND re.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? @@ -2489,7 +2384,6 @@ class SearchTest { convertUUIDToByte(UUID.fromString("e2c79e28-ed4d-2029-a12c-108d1eb5bedb")), convertUUIDToByte(UUID.fromString("e2c79e28-ed4d-4029-a12c-108d1eb5bedb")), "Practitioner", - "Practitioner", "active", "true", "name", @@ -2498,7 +2392,6 @@ class SearchTest { convertUUIDToByte(UUID.fromString("e2c79e28-ed4d-2029-a12c-108d1eb5bedb")), convertUUIDToByte(UUID.fromString("e2c79e28-ed4d-4029-a12c-108d1eb5bedb")), "Organization", - "Organization", "active", "true", ) @@ -2520,7 +2413,8 @@ class SearchTest { FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceUuid = rie.resourceUuid - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) + AND re.resourceType = ? ) """ .trimIndent(), @@ -2558,7 +2452,7 @@ class SearchTest { FROM ResourceEntity re JOIN ReferenceIndexEntity rie ON re.resourceUuid = rie.resourceUuid - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) AND re.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) @@ -2575,7 +2469,6 @@ class SearchTest { "Patient/pa01", "Patient/pa02", "Condition", - "Condition", "code", "44054006", "http://snomed.info/sct", @@ -2615,7 +2508,7 @@ class SearchTest { ON re.resourceUuid = b.resourceUuid AND b.index_name = ? LEFT JOIN DateTimeIndexEntity c ON re.resourceUuid = c.resourceUuid AND c.index_name = ? - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) AND re.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) @@ -2637,7 +2530,6 @@ class SearchTest { "Patient/pa01", "Patient/pa02", "Condition", - "Condition", "code", "44054006", "http://snomed.info/sct", @@ -2694,7 +2586,7 @@ class SearchTest { ON re.resourceUuid = b.resourceUuid AND b.index_name = ? LEFT JOIN DateTimeIndexEntity c ON re.resourceUuid = c.resourceUuid AND c.index_name = ? - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) AND re.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) @@ -2713,7 +2605,7 @@ class SearchTest { ON re.resourceUuid = b.resourceUuid AND b.index_name = ? LEFT JOIN DateTimeIndexEntity c ON re.resourceUuid = c.resourceUuid AND c.index_name = ? - WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) AND re.resourceType = ? + WHERE rie.resourceType = ? AND rie.index_name = ? AND rie.index_value IN (?, ?) AND re.resourceUuid IN ( SELECT resourceUuid FROM TokenIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? AND IFNULL(index_system,'') = ?) @@ -2735,7 +2627,6 @@ class SearchTest { "Patient/pa01", "Patient/pa02", "Encounter", - "Encounter", "status", "arrived", "http://hl7.org/fhir/encounter-status", @@ -2746,7 +2637,6 @@ class SearchTest { "Patient/pa01", "Patient/pa02", "Condition", - "Condition", "code", "44054006", "http://snomed.info/sct", @@ -2772,8 +2662,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE 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 = ?)))))))))) ) @@ -2783,7 +2672,6 @@ class SearchTest { assertThat(query.args) .containsExactly( - "CarePlan", "CarePlan", "subject", *patientIdReferenceList.toTypedArray(), @@ -2801,8 +2689,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM ReferenceIndexEntity WHERE resourceType = ? AND index_name = ? ) @@ -2812,7 +2699,6 @@ class SearchTest { assertThat(query.args) .containsExactly( - "CarePlan", "CarePlan", "subject", ) @@ -2832,8 +2718,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM ReferenceIndexEntity WHERE resourceType = ? AND index_name = ? AND index_value = ? ) @@ -2843,7 +2728,6 @@ class SearchTest { assertThat(query.args) .containsExactly( - "CarePlan", "CarePlan", "subject", "Patient/patient-0", @@ -2866,8 +2750,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM ReferenceIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? OR index_value = ?) ) @@ -2877,7 +2760,6 @@ class SearchTest { assertThat(query.args) .containsExactly( - "CarePlan", "CarePlan", "subject", "Patient/patient-0", @@ -2906,8 +2788,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM ReferenceIndexEntity WHERE resourceType = ? AND index_name = ? AND (index_value = ? OR (index_value = ? OR index_value = ?)) ) @@ -2917,7 +2798,6 @@ class SearchTest { assertThat(query.args) .containsExactly( - "CarePlan", "CarePlan", "subject", "Patient/patient-0", @@ -2948,8 +2828,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE a.resourceUuid IN ( SELECT resourceUuid FROM ReferenceIndexEntity WHERE resourceType = ? AND index_name = ? AND ((index_value = ? OR index_value = ?) OR (index_value = ? OR index_value = ?)) ) @@ -2959,7 +2838,6 @@ class SearchTest { assertThat(query.args) .containsExactly( - "CarePlan", "CarePlan", "subject", "Patient/patient-0", @@ -2987,8 +2865,7 @@ class SearchTest { """ SELECT a.resourceUuid, a.serializedResource FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceUuid IN ( + WHERE 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 = ?))) ) @@ -2998,7 +2875,6 @@ class SearchTest { assertThat(query.args) .containsExactly( - "CarePlan", "CarePlan", "subject", *patientIdReferenceList.toTypedArray(), From 69b8ebf156c2282be64af8905d209260046fd33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=E2=89=A1ZRS?= <12814349+LZRS@users.noreply.github.com> Date: Fri, 20 Dec 2024 18:46:02 +0300 Subject: [PATCH 4/4] Check if previous/next page is hidden to show previous/next buttons (#2569) * Only select QuestionnaireItem with 'page' extension as page * Fix hasNextPage and hasPreviousPage to check if page is hidden * Resolve NIT, renaming test linkId to page4-noExtension-hidden --- .../datacapture/QuestionnaireViewModel.kt | 4 +- .../datacapture/QuestionnaireViewModelTest.kt | 178 +++++++++++++++++- 2 files changed, 179 insertions(+), 3 deletions(-) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireViewModel.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireViewModel.kt index 056cd47aad..ecc019b0a8 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireViewModel.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireViewModel.kt @@ -1183,7 +1183,7 @@ internal data class QuestionnairePage( ) internal val QuestionnairePagination.hasPreviousPage: Boolean - get() = pages.any { it.index < currentPageIndex && it.enabled } + get() = pages.any { it.index < currentPageIndex && it.enabled && !it.hidden } internal val QuestionnairePagination.hasNextPage: Boolean - get() = pages.any { it.index > currentPageIndex && it.enabled } + get() = pages.any { it.index > currentPageIndex && it.enabled && !it.hidden } diff --git a/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt b/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt index 51e00eaa1d..659429a95a 100644 --- a/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt +++ b/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt @@ -1533,6 +1533,102 @@ class QuestionnaireViewModelTest { // Pagination // // // // ==================================================================== // + @Test + fun `should include all top level items as pages when any item has page extension`() = runTest { + val questionnaire = + Questionnaire().apply { + id = "a-questionnaire" + addItem( + QuestionnaireItemComponent().apply { + linkId = "page1-noExtension" + type = Questionnaire.QuestionnaireItemType.GROUP + addItem( + QuestionnaireItemComponent().apply { + linkId = "page1-1" + type = Questionnaire.QuestionnaireItemType.BOOLEAN + text = "Question on page 1" + }, + ) + }, + ) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page2" + type = Questionnaire.QuestionnaireItemType.GROUP + addExtension(paginationExtension) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page2-1" + type = Questionnaire.QuestionnaireItemType.BOOLEAN + text = "Question on page 2" + }, + ) + }, + ) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page3-noExtension" + type = Questionnaire.QuestionnaireItemType.GROUP + addItem( + QuestionnaireItemComponent().apply { + linkId = "page3-1" + type = Questionnaire.QuestionnaireItemType.BOOLEAN + text = "Question on page 3" + }, + ) + }, + ) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page4-noExtension-hidden" + addExtension(hiddenExtension) + type = Questionnaire.QuestionnaireItemType.GROUP + addItem( + QuestionnaireItemComponent().apply { + linkId = "page4-1" + type = Questionnaire.QuestionnaireItemType.BOOLEAN + text = "Question on page 4" + }, + ) + }, + ) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page5" + type = Questionnaire.QuestionnaireItemType.GROUP + addExtension(paginationExtension) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page5-1" + type = Questionnaire.QuestionnaireItemType.BOOLEAN + text = "Question on page 5" + }, + ) + }, + ) + } + + val viewModel = createQuestionnaireViewModel(questionnaire) + viewModel.runViewModelBlocking { + assertThat( + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, + ) + .isEqualTo( + QuestionnairePagination( + isPaginated = true, + pages = + listOf( + QuestionnairePage(0, enabled = true, hidden = false), + QuestionnairePage(1, enabled = true, hidden = false), + QuestionnairePage(2, enabled = true, hidden = false), + QuestionnairePage(3, enabled = true, hidden = true), + QuestionnairePage(4, enabled = true, hidden = false), + ), + currentPageIndex = 0, + ), + ) + } + } @Test fun `should show current page`() = runTest { @@ -1830,8 +1926,11 @@ class QuestionnaireViewModelTest { } val viewModel = createQuestionnaireViewModel(questionnaire) viewModel.runViewModelBlocking { + val questionnaireStatePagination = + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, + questionnaireStatePagination, ) .isEqualTo( QuestionnairePagination( @@ -1845,6 +1944,83 @@ class QuestionnaireViewModelTest { currentPageIndex = 1, ), ) + + assertThat(questionnaireStatePagination.hasPreviousPage).isFalse() + } + } + + @Test + fun `should skip last page if it is hidden`() = runTest { + val questionnaire = + Questionnaire().apply { + id = "a-questionnaire" + addItem( + QuestionnaireItemComponent().apply { + linkId = "page1" + type = Questionnaire.QuestionnaireItemType.GROUP + addExtension(paginationExtension) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page1-1" + type = Questionnaire.QuestionnaireItemType.BOOLEAN + text = "Question on page 1" + }, + ) + }, + ) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page2" + type = Questionnaire.QuestionnaireItemType.GROUP + addExtension(paginationExtension) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page2-1" + type = Questionnaire.QuestionnaireItemType.BOOLEAN + text = "Question on page 2" + }, + ) + }, + ) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page3" + type = Questionnaire.QuestionnaireItemType.GROUP + addExtension(paginationExtension) + addExtension(hiddenExtension) + addItem( + QuestionnaireItemComponent().apply { + linkId = "page3-1" + type = Questionnaire.QuestionnaireItemType.BOOLEAN + text = "Question on page 3" + }, + ) + }, + ) + } + val viewModel = createQuestionnaireViewModel(questionnaire) + viewModel.runViewModelBlocking { + viewModel.goToNextPage() + val questionnaireStatePagination = + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + + assertThat( + questionnaireStatePagination, + ) + .isEqualTo( + QuestionnairePagination( + isPaginated = true, + pages = + listOf( + QuestionnairePage(0, enabled = true, hidden = false), + QuestionnairePage(1, enabled = true, hidden = false), + QuestionnairePage(2, enabled = true, hidden = true), + ), + currentPageIndex = 1, + ), + ) + + assertThat(questionnaireStatePagination.hasNextPage).isFalse() } }