From d765f84ee79336f29deb28db5fd39b0b0d81b62d Mon Sep 17 00:00:00 2001 From: epicadk Date: Fri, 2 Apr 2021 16:37:09 +0530 Subject: [PATCH 01/25] Add date to search --- .../google/android/fhir/search/MoreSearch.kt | 11 +++++++- .../google/android/fhir/search/SearchDsl.kt | 16 ++++++++++++ .../google/android/fhir/search/SearchTest.kt | 26 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt b/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt index 11836012c3..d0e374180e 100644 --- a/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt +++ b/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt @@ -43,7 +43,7 @@ fun Search.getQuery(): SearchQuery { var filterStatement = "" val filterArgs = mutableListOf() val filterQuery = - (stringFilters.map { it.query(type) } + referenceFilter.map { it.query(type) }).intersect() + (stringFilters.map { it.query(type) } + referenceFilter.map { it.query(type) } + dateFilter.map { it.query(type) }).intersect() if (filterQuery != null) { filterStatement = """ @@ -101,6 +101,15 @@ fun ReferenceFilter.query(type: ResourceType): SearchQuery { ) } +fun DateFilter.query(type: ResourceType): SearchQuery { + return SearchQuery( + """ + SELECT resourceId form DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND ? BETWEEN index_tsLow AND index_tsHigh + """, listOf(type.name, parameter.paramName, value!!)) + +} + fun List.intersect(): SearchQuery? { return if (isEmpty()) { null diff --git a/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt index e09b89be63..dd5cb6829e 100644 --- a/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -16,15 +16,18 @@ package com.google.android.fhir.search +import ca.uhn.fhir.rest.gclient.DateClientParam import ca.uhn.fhir.rest.gclient.ReferenceClientParam import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum import org.hl7.fhir.r4.model.ResourceType +import java.math.BigDecimal @SearchDslMarker data class Search(val type: ResourceType, var count: Int? = null, var from: Int? = null) { internal val stringFilters = mutableListOf() internal val referenceFilter = mutableListOf() + internal val dateFilter = mutableListOf() internal var sort: StringClientParam? = null internal var order: Order? = null @@ -40,6 +43,12 @@ data class Search(val type: ResourceType, var count: Int? = null, var from: Int? referenceFilter.add(filter) } + fun filter(dateParameter: DateClientParam , init: DateFilter.()-> Unit ){ + val filter = DateFilter(dateParameter) + filter.init() + dateFilter.add(filter) + } + fun sort(parameter: StringClientParam, order: Order) { sort = parameter this.order = order @@ -53,6 +62,13 @@ data class StringFilter( var value: String? = null ) +@SearchDslMarker +data class DateFilter( + val parameter: DateClientParam, + var prefix : ParamPrefixEnum? = null, + var value: BigDecimal? = null +) + @SearchDslMarker data class ReferenceFilter(val parameter: ReferenceClientParam?, var value: String? = null) diff --git a/core/src/test/java/com/google/android/fhir/search/SearchTest.kt b/core/src/test/java/com/google/android/fhir/search/SearchTest.kt index 0adf45f3bd..fc91086179 100644 --- a/core/src/test/java/com/google/android/fhir/search/SearchTest.kt +++ b/core/src/test/java/com/google/android/fhir/search/SearchTest.kt @@ -26,6 +26,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config +import java.math.BigDecimal /** Unit tests for [MoreSearch]. */ @RunWith(RobolectricTestRunner::class) @@ -119,6 +120,31 @@ class SearchTest { ) } + @Test + fun search_date(){ + val query = + Search(ResourceType.Patient).apply { filter(Patient.BIRTHDATE){ + prefix = ParamPrefixEnum.APPROXIMATE + value = BigDecimal.valueOf(9009090909) + } }.getQuery() + + assertThat(query.query).isEqualTo(""" + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId form DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND ? BETWEEN + index_tsHigh AND index_tsLow) + """.trimIndent()) + + assertThat(query.args).isEqualTo( + listOf(ResourceType.Patient , + Patient.BIRTHDATE.paramName, + BigDecimal.valueOf(9009090909)) + ) + } + @Test fun search_sort_ascending() { val query = From 81628a2372f456c25496880b87d4bafac19bb1a5 Mon Sep 17 00:00:00 2001 From: epicadk Date: Sun, 4 Apr 2021 00:25:17 +0530 Subject: [PATCH 02/25] Fix tests --- .../google/android/fhir/search/MoreSearch.kt | 13 ++++-- .../google/android/fhir/search/SearchDsl.kt | 6 +-- .../google/android/fhir/search/SearchTest.kt | 42 ++++++++++++------- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt b/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt index d0e374180e..2e8f354458 100644 --- a/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt +++ b/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt @@ -43,7 +43,10 @@ fun Search.getQuery(): SearchQuery { var filterStatement = "" val filterArgs = mutableListOf() val filterQuery = - (stringFilters.map { it.query(type) } + referenceFilter.map { it.query(type) } + dateFilter.map { it.query(type) }).intersect() + (stringFilters.map { it.query(type) } + + referenceFilter.map { it.query(type) } + + dateFilter.map { it.query(type) }) + .intersect() if (filterQuery != null) { filterStatement = """ @@ -105,9 +108,11 @@ fun DateFilter.query(type: ResourceType): SearchQuery { return SearchQuery( """ SELECT resourceId form DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ? BETWEEN index_tsLow AND index_tsHigh - """, listOf(type.name, parameter.paramName, value!!)) - + WHERE resourceType = ? AND index_name = ? + AND ? BETWEEN index_tsLow AND index_tsHigh + """, + listOf(type.name, parameter.paramName, value!!) + ) } fun List.intersect(): SearchQuery? { diff --git a/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt index dd5cb6829e..c57d85f794 100644 --- a/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -20,8 +20,8 @@ import ca.uhn.fhir.rest.gclient.DateClientParam import ca.uhn.fhir.rest.gclient.ReferenceClientParam import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum -import org.hl7.fhir.r4.model.ResourceType import java.math.BigDecimal +import org.hl7.fhir.r4.model.ResourceType @SearchDslMarker data class Search(val type: ResourceType, var count: Int? = null, var from: Int? = null) { @@ -43,7 +43,7 @@ data class Search(val type: ResourceType, var count: Int? = null, var from: Int? referenceFilter.add(filter) } - fun filter(dateParameter: DateClientParam , init: DateFilter.()-> Unit ){ + fun filter(dateParameter: DateClientParam, init: DateFilter.() -> Unit) { val filter = DateFilter(dateParameter) filter.init() dateFilter.add(filter) @@ -65,7 +65,7 @@ data class StringFilter( @SearchDslMarker data class DateFilter( val parameter: DateClientParam, - var prefix : ParamPrefixEnum? = null, + var prefix: ParamPrefixEnum? = null, var value: BigDecimal? = null ) diff --git a/core/src/test/java/com/google/android/fhir/search/SearchTest.kt b/core/src/test/java/com/google/android/fhir/search/SearchTest.kt index fc91086179..46ed3902b1 100644 --- a/core/src/test/java/com/google/android/fhir/search/SearchTest.kt +++ b/core/src/test/java/com/google/android/fhir/search/SearchTest.kt @@ -19,6 +19,7 @@ package com.google.android.fhir.search import android.os.Build import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.common.truth.Truth.assertThat +import java.math.BigDecimal import kotlinx.coroutines.runBlocking import org.hl7.fhir.r4.model.Patient import org.hl7.fhir.r4.model.ResourceType @@ -26,7 +27,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import java.math.BigDecimal /** Unit tests for [MoreSearch]. */ @RunWith(RobolectricTestRunner::class) @@ -121,28 +121,40 @@ class SearchTest { } @Test - fun search_date(){ + fun search_date() { val query = - Search(ResourceType.Patient).apply { filter(Patient.BIRTHDATE){ - prefix = ParamPrefixEnum.APPROXIMATE - value = BigDecimal.valueOf(9009090909) - } }.getQuery() + Search(ResourceType.Patient) + .apply { + filter(Patient.BIRTHDATE) { + prefix = ParamPrefixEnum.APPROXIMATE + value = BigDecimal.valueOf(9009090909) + } + } + .getQuery() - assertThat(query.query).isEqualTo(""" + assertThat(query.query) + .isEqualTo( + """ SELECT a.serializedResource FROM ResourceEntity a WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId form DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ? BETWEEN - index_tsHigh AND index_tsLow) - """.trimIndent()) - - assertThat(query.args).isEqualTo( - listOf(ResourceType.Patient , - Patient.BIRTHDATE.paramName, - BigDecimal.valueOf(9009090909)) + WHERE resourceType = ? AND index_name = ? + AND ? BETWEEN index_tsLow AND index_tsHigh ) + """.trimIndent() + ) + + assertThat(query.args) + .isEqualTo( + listOf( + ResourceType.Patient.name, + ResourceType.Patient.name, + Patient.BIRTHDATE.paramName, + BigDecimal.valueOf(9009090909) + ) + ) } @Test From aa2df654eb551a8734d728e8eeb8292151caecaf Mon Sep 17 00:00:00 2001 From: epicadk Date: Sun, 4 Apr 2021 01:57:05 +0530 Subject: [PATCH 03/25] Handle ParamPrefix --- .../com/google/android/fhir/search/MoreSearch.kt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt b/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt index 2e8f354458..b81c8356cd 100644 --- a/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt +++ b/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt @@ -16,6 +16,7 @@ package com.google.android.fhir.search +import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.db.Database import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.ResourceType @@ -105,11 +106,24 @@ fun ReferenceFilter.query(type: ResourceType): SearchQuery { } fun DateFilter.query(type: ResourceType): SearchQuery { + val condition = + when (this.prefix) { + ParamPrefixEnum.APPROXIMATE -> "BETWEEN index_tsLow AND index_tsHigh" + ParamPrefixEnum.STARTS_AFTER -> " < index_tsLow" + ParamPrefixEnum.ENDS_BEFORE -> " > index_tsHigh" + ParamPrefixEnum.NOT_EQUAL -> "NOT BETWEEN index_tsLow AND index_tsHigh" + ParamPrefixEnum.EQUAL -> "BETWEEN index_tsLow AND index_tsHigh" + ParamPrefixEnum.GREATERTHAN -> " > index_tsLow" + ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> " >= index_tsLow" + ParamPrefixEnum.LESSTHAN -> "< index_tsHigh" + ParamPrefixEnum.LESSTHAN_OR_EQUALS -> "<= index_tsHigh" + null -> "" // Possibly throw an error or provide a default value? + } return SearchQuery( """ SELECT resourceId form DateIndexEntity WHERE resourceType = ? AND index_name = ? - AND ? BETWEEN index_tsLow AND index_tsHigh + AND ? $condition """, listOf(type.name, parameter.paramName, value!!) ) From 875fcda81773b250b4ece9b7078d741c49e641bd Mon Sep 17 00:00:00 2001 From: epicadk Date: Sun, 4 Apr 2021 02:13:40 +0530 Subject: [PATCH 04/25] Refractor: BigDecimal to Long --- .../main/java/com/google/android/fhir/search/SearchDsl.kt | 3 +-- .../test/java/com/google/android/fhir/search/SearchTest.kt | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt index c57d85f794..f612c89a75 100644 --- a/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/core/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -20,7 +20,6 @@ import ca.uhn.fhir.rest.gclient.DateClientParam import ca.uhn.fhir.rest.gclient.ReferenceClientParam import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum -import java.math.BigDecimal import org.hl7.fhir.r4.model.ResourceType @SearchDslMarker @@ -66,7 +65,7 @@ data class StringFilter( data class DateFilter( val parameter: DateClientParam, var prefix: ParamPrefixEnum? = null, - var value: BigDecimal? = null + var value: Long? = null ) @SearchDslMarker diff --git a/core/src/test/java/com/google/android/fhir/search/SearchTest.kt b/core/src/test/java/com/google/android/fhir/search/SearchTest.kt index 46ed3902b1..161e4c8aaf 100644 --- a/core/src/test/java/com/google/android/fhir/search/SearchTest.kt +++ b/core/src/test/java/com/google/android/fhir/search/SearchTest.kt @@ -19,7 +19,6 @@ package com.google.android.fhir.search import android.os.Build import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.common.truth.Truth.assertThat -import java.math.BigDecimal import kotlinx.coroutines.runBlocking import org.hl7.fhir.r4.model.Patient import org.hl7.fhir.r4.model.ResourceType @@ -127,7 +126,7 @@ class SearchTest { .apply { filter(Patient.BIRTHDATE) { prefix = ParamPrefixEnum.APPROXIMATE - value = BigDecimal.valueOf(9009090909) + value = 9009090909 } } .getQuery() @@ -152,7 +151,7 @@ class SearchTest { ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, - BigDecimal.valueOf(9009090909) + 9009090909 ) ) } From a66fa6da0073c44a928df2f97917657766c5ba69 Mon Sep 17 00:00:00 2001 From: epicadk Date: Tue, 13 Apr 2021 00:37:20 +0530 Subject: [PATCH 05/25] Add test --- .../google/android/fhir/search/MoreSearch.kt | 2 +- .../android/fhir/impl/FhirEngineImplTest.kt | 24 +++++++++++++++++++ .../google/android/fhir/search/SearchTest.kt | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt b/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt index b81c8356cd..c1f8aa26e5 100644 --- a/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt +++ b/core/src/main/java/com/google/android/fhir/search/MoreSearch.kt @@ -121,7 +121,7 @@ fun DateFilter.query(type: ResourceType): SearchQuery { } return SearchQuery( """ - SELECT resourceId form DateIndexEntity + SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND ? $condition """, diff --git a/core/src/test/java/com/google/android/fhir/impl/FhirEngineImplTest.kt b/core/src/test/java/com/google/android/fhir/impl/FhirEngineImplTest.kt index 08cd3914e1..5b12661118 100644 --- a/core/src/test/java/com/google/android/fhir/impl/FhirEngineImplTest.kt +++ b/core/src/test/java/com/google/android/fhir/impl/FhirEngineImplTest.kt @@ -17,14 +17,17 @@ package com.google.android.fhir.impl import androidx.test.core.app.ApplicationProvider +import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.FhirServices.Companion.builder import com.google.android.fhir.ResourceNotFoundException import com.google.android.fhir.db.ResourceNotFoundInDbException import com.google.android.fhir.resource.TestingUtils +import com.google.android.fhir.search.Search import com.google.android.fhir.sync.FhirDataSource import com.google.common.truth.Truth import kotlinx.coroutines.runBlocking import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.DateType import org.hl7.fhir.r4.model.Enumerations import org.hl7.fhir.r4.model.Patient import org.hl7.fhir.r4.model.ResourceType @@ -108,6 +111,27 @@ class FhirEngineImplTest { ) } + @Test + fun search_date_shouldReturnResource() { + val patient = + Patient().apply { + id = "1" + birthDate = DateType("2001-03-04").value + } + + val res = runBlocking { + fhirEngine.save(patient) + fhirEngine.search( + Search(ResourceType.Patient).apply { + filter(Patient.BIRTHDATE) { + value = DateType("2001-03-04").value.time + prefix = ParamPrefixEnum.EQUAL + } + } + ) + } + Truth.assertThat(res[0].birthDate).isEqualTo(DateType("2001-03-04").value) + } @Test fun load_shouldReturnResource() = runBlocking { testingUtils.assertResourceEquals( diff --git a/core/src/test/java/com/google/android/fhir/search/SearchTest.kt b/core/src/test/java/com/google/android/fhir/search/SearchTest.kt index 161e4c8aaf..ef8cf0f78a 100644 --- a/core/src/test/java/com/google/android/fhir/search/SearchTest.kt +++ b/core/src/test/java/com/google/android/fhir/search/SearchTest.kt @@ -138,7 +138,7 @@ class SearchTest { FROM ResourceEntity a WHERE a.resourceType = ? AND a.resourceId IN ( - SELECT resourceId form DateIndexEntity + SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? AND ? BETWEEN index_tsLow AND index_tsHigh ) From f7b5701dbf9b73eada30b10b9f6dda87318b1838 Mon Sep 17 00:00:00 2001 From: epicadk Date: Fri, 23 Apr 2021 22:13:09 +0530 Subject: [PATCH 06/25] Empty commit From f2baaf95bae4ab662bcc21242fe7994734fe8170 Mon Sep 17 00:00:00 2001 From: epicadk Date: Thu, 6 May 2021 18:58:34 +0530 Subject: [PATCH 07/25] Fix failing tests --- .../google/android/fhir/search/MoreSearch.kt | 18 +++++++++--------- .../google/android/fhir/search/SearchTest.kt | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) 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 fb72ba1d64..af70f283af 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 @@ -108,15 +108,15 @@ fun ReferenceFilter.query(type: ResourceType): SearchQuery { fun DateFilter.query(type: ResourceType): SearchQuery { val condition = when (this.prefix) { - ParamPrefixEnum.APPROXIMATE -> "BETWEEN index_tsLow AND index_tsHigh" - ParamPrefixEnum.STARTS_AFTER -> " < index_tsLow" - ParamPrefixEnum.ENDS_BEFORE -> " > index_tsHigh" - ParamPrefixEnum.NOT_EQUAL -> "NOT BETWEEN index_tsLow AND index_tsHigh" - ParamPrefixEnum.EQUAL -> "BETWEEN index_tsLow AND index_tsHigh" - ParamPrefixEnum.GREATERTHAN -> " > index_tsLow" - ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> " >= index_tsLow" - ParamPrefixEnum.LESSTHAN -> "< index_tsHigh" - ParamPrefixEnum.LESSTHAN_OR_EQUALS -> "<= index_tsHigh" + ParamPrefixEnum.APPROXIMATE -> "BETWEEN index_from AND index_to" + ParamPrefixEnum.STARTS_AFTER -> " < index_from" + ParamPrefixEnum.ENDS_BEFORE -> " > index_to" + ParamPrefixEnum.NOT_EQUAL -> "NOT BETWEEN index_from AND index_to" + ParamPrefixEnum.EQUAL -> "BETWEEN index_from AND index_to" + ParamPrefixEnum.GREATERTHAN -> " > index_from" + ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> " >= index_from" + ParamPrefixEnum.LESSTHAN -> "< index_to" + ParamPrefixEnum.LESSTHAN_OR_EQUALS -> "<= index_to" null -> "" // Possibly throw an error or provide a default value? } return SearchQuery( 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 855b0afb10..24a839456f 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 @@ -140,7 +140,7 @@ class SearchTest { AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? - AND ? BETWEEN index_tsLow AND index_tsHigh + AND ? BETWEEN index_from AND index_to ) """.trimIndent() ) From 2263927c9f7ae4e94f44b6701d2345e60ca86ce4 Mon Sep 17 00:00:00 2001 From: epicadk Date: Mon, 10 May 2021 15:10:00 +0530 Subject: [PATCH 08/25] Add tests and minor fixes --- .../google/android/fhir/search/MoreSearch.kt | 74 +++- .../google/android/fhir/search/SearchDsl.kt | 12 +- .../android/fhir/impl/FhirEngineImplTest.kt | 24 -- .../google/android/fhir/search/SearchTest.kt | 339 +++++++++++++++++- 4 files changed, 402 insertions(+), 47 deletions(-) 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 af70f283af..d13b7d4dcc 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 @@ -16,6 +16,8 @@ package com.google.android.fhir.search +import ca.uhn.fhir.rest.gclient.NumberClientParam +import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.db.Database import org.hl7.fhir.r4.model.Resource @@ -29,16 +31,22 @@ fun Search.getQuery(): SearchQuery { var sortJoinStatement = "" var sortOrderStatement = "" val sortArgs = mutableListOf() - if (sort != null) { + sort?.let { sort -> + val sortTableName = + when (sort) { + is StringClientParam -> "StringIndexEntity" + is NumberClientParam -> "NumberIndexEntity" + else -> throw NotImplementedError("Unhandled sort parameter of type ${sort::class}: $sort") + } sortJoinStatement = """ - LEFT JOIN StringIndexEntity b + LEFT JOIN $sortTableName b ON a.resourceType = b.resourceType AND a.resourceId = b.resourceId AND b.index_name = ? """.trimIndent() sortOrderStatement = """ ORDER BY b.index_value ${order.sqlString} """.trimIndent() - sortArgs += sort!!.paramName + sortArgs += sort.paramName } var filterStatement = "" @@ -106,26 +114,62 @@ fun ReferenceFilter.query(type: ResourceType): SearchQuery { } fun DateFilter.query(type: ResourceType): SearchQuery { + val tsHigh = value!!.precision.add(value!!.value, 1).time + var useHigh = false val condition = when (this.prefix) { - ParamPrefixEnum.APPROXIMATE -> "BETWEEN index_from AND index_to" - ParamPrefixEnum.STARTS_AFTER -> " < index_from" - ParamPrefixEnum.ENDS_BEFORE -> " > index_to" - ParamPrefixEnum.NOT_EQUAL -> "NOT BETWEEN index_from AND index_to" - ParamPrefixEnum.EQUAL -> "BETWEEN index_from AND index_to" - ParamPrefixEnum.GREATERTHAN -> " > index_from" - ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> " >= index_from" - ParamPrefixEnum.LESSTHAN -> "< index_to" - ParamPrefixEnum.LESSTHAN_OR_EQUALS -> "<= index_to" - null -> "" // Possibly throw an error or provide a default value? + ParamPrefixEnum.APPROXIMATE -> { + val rangeHigh = value!!.precision.add(value!!.value, 10).time + val rangeLow = value!!.precision.add(value!!.value, -10).time + return SearchQuery( + """SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?""", + listOf(type.name, parameter.paramName, rangeLow, rangeHigh, rangeLow, rangeHigh) + ) + } + ParamPrefixEnum.STARTS_AFTER -> ">= index_from".also { useHigh = true } + ParamPrefixEnum.ENDS_BEFORE -> "<= index_to" + ParamPrefixEnum.NOT_EQUAL -> + return SearchQuery( + """SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND index_from NOT BETWEEN ? AND ? AND index_to NOT BETWEEN ? AND ?""", + listOf( + type.name, + parameter.paramName, + value!!.value.time, + tsHigh, + value!!.value.time, + tsHigh + ) + ) + ParamPrefixEnum.EQUAL, null -> + return SearchQuery( + """SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?""", + listOf( + type.name, + parameter.paramName, + value!!.value.time, + tsHigh, + value!!.value.time, + tsHigh + ) + ) + ParamPrefixEnum.GREATERTHAN -> "<= index_from".also { useHigh = true } + ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> "<= index_from" + ParamPrefixEnum.LESSTHAN -> ">= index_to" + ParamPrefixEnum.LESSTHAN_OR_EQUALS -> ">= index_to".also { useHigh = true } } return SearchQuery( """ SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? - AND ? $condition + AND ? $condition """, - listOf(type.name, parameter.paramName, value!!) + listOf(type.name, parameter.paramName, if (!useHigh) value!!.value.time else tsHigh) ) } diff --git a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt index f612c89a75..e66810a698 100644 --- a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -17,9 +17,12 @@ package com.google.android.fhir.search import ca.uhn.fhir.rest.gclient.DateClientParam +import ca.uhn.fhir.rest.gclient.IParam +import ca.uhn.fhir.rest.gclient.NumberClientParam import ca.uhn.fhir.rest.gclient.ReferenceClientParam import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum +import org.hl7.fhir.r4.model.DateTimeType import org.hl7.fhir.r4.model.ResourceType @SearchDslMarker @@ -27,7 +30,7 @@ data class Search(val type: ResourceType, var count: Int? = null, var from: Int? internal val stringFilters = mutableListOf() internal val referenceFilter = mutableListOf() internal val dateFilter = mutableListOf() - internal var sort: StringClientParam? = null + internal var sort: IParam? = null internal var order: Order? = null fun filter(stringParameter: StringClientParam, init: StringFilter.() -> Unit) { @@ -52,6 +55,11 @@ data class Search(val type: ResourceType, var count: Int? = null, var from: Int? sort = parameter this.order = order } + + fun sort(parameter: NumberClientParam, order: Order) { + sort = parameter + this.order = order + } } @SearchDslMarker @@ -65,7 +73,7 @@ data class StringFilter( data class DateFilter( val parameter: DateClientParam, var prefix: ParamPrefixEnum? = null, - var value: Long? = null + var value: DateTimeType? = null ) @SearchDslMarker 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 5f62f8502e..36b5ad28be 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,17 +17,14 @@ package com.google.android.fhir.impl import androidx.test.core.app.ApplicationProvider -import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.FhirServices.Companion.builder import com.google.android.fhir.ResourceNotFoundException import com.google.android.fhir.db.ResourceNotFoundInDbException import com.google.android.fhir.resource.TestingUtils -import com.google.android.fhir.search.Search import com.google.android.fhir.sync.FhirDataSource import com.google.common.truth.Truth import kotlinx.coroutines.runBlocking import org.hl7.fhir.r4.model.Bundle -import org.hl7.fhir.r4.model.DateType import org.hl7.fhir.r4.model.Enumerations import org.hl7.fhir.r4.model.OperationOutcome import org.hl7.fhir.r4.model.Patient @@ -133,27 +130,6 @@ class FhirEngineImplTest { ) } - @Test - fun search_date_shouldReturnResource() { - val patient = - Patient().apply { - id = "1" - birthDate = DateType("2001-03-04").value - } - - val res = runBlocking { - fhirEngine.save(patient) - fhirEngine.search( - Search(ResourceType.Patient).apply { - filter(Patient.BIRTHDATE) { - value = DateType("2001-03-04").value.time - prefix = ParamPrefixEnum.EQUAL - } - } - ) - } - Truth.assertThat(res[0].birthDate).isEqualTo(DateType("2001-03-04").value) - } @Test fun load_shouldReturnResource() = runBlocking { testingUtils.assertResourceEquals( 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 24a839456f..a2a6a94c36 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 @@ -20,8 +20,10 @@ import android.os.Build import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.runBlocking +import org.hl7.fhir.r4.model.DateTimeType import org.hl7.fhir.r4.model.Patient import org.hl7.fhir.r4.model.ResourceType +import org.hl7.fhir.r4.model.RiskAssessment import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -120,13 +122,13 @@ class SearchTest { } @Test - fun search_date() { + fun search_date_approximate() { val query = Search(ResourceType.Patient) .apply { filter(Patient.BIRTHDATE) { prefix = ParamPrefixEnum.APPROXIMATE - value = 9009090909 + value = DateTimeType("2013-03-14") } } .getQuery() @@ -140,7 +142,7 @@ class SearchTest { AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? - AND ? BETWEEN index_from AND index_to + AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ? ) """.trimIndent() ) @@ -151,13 +153,318 @@ class SearchTest { ResourceType.Patient.name, ResourceType.Patient.name, Patient.BIRTHDATE.paramName, - 9009090909 + DateTimeType("2013-03-04").value.time, + DateTimeType("2013-03-24").value.time, + DateTimeType("2013-03-04").value.time, + DateTimeType("2013-03-24").value.time ) ) } @Test - fun search_sort_ascending() { + fun search_date_starts_after() { + val query = + Search(ResourceType.Patient) + .apply { + filter(Patient.BIRTHDATE) { + prefix = ParamPrefixEnum.STARTS_AFTER + value = DateTimeType("2013-03-14") + } + } + .getQuery() + + assertThat(query.query) + .isEqualTo( + """ + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND ? >= index_from + ) + """.trimIndent() + ) + + assertThat(query.args) + .isEqualTo( + listOf( + ResourceType.Patient.name, + ResourceType.Patient.name, + Patient.BIRTHDATE.paramName, + DateTimeType("2013-03-15").value.time + ) + ) + } + + @Test + fun search_date_ends_before() { + val query = + Search(ResourceType.Patient) + .apply { + filter(Patient.BIRTHDATE) { + prefix = ParamPrefixEnum.ENDS_BEFORE + value = DateTimeType("2013-03-14") + } + } + .getQuery() + + assertThat(query.query) + .isEqualTo( + """ + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND ? <= index_to + ) + """.trimIndent() + ) + + assertThat(query.args) + .isEqualTo( + listOf( + ResourceType.Patient.name, + ResourceType.Patient.name, + Patient.BIRTHDATE.paramName, + DateTimeType("2013-03-14").value.time + ) + ) + } + + @Test + fun search_date_not_equal() { + val query = + Search(ResourceType.Patient) + .apply { + filter(Patient.BIRTHDATE) { + prefix = ParamPrefixEnum.NOT_EQUAL + value = DateTimeType("2013-03-14") + } + } + .getQuery() + + assertThat(query.query) + .isEqualTo( + """ + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND index_from NOT BETWEEN ? AND ? AND index_to NOT BETWEEN ? AND ? + ) + """.trimIndent() + ) + + assertThat(query.args) + .isEqualTo( + listOf( + ResourceType.Patient.name, + ResourceType.Patient.name, + Patient.BIRTHDATE.paramName, + DateTimeType("2013-03-14").value.time, + DateTimeType("2013-03-15").value.time, + DateTimeType("2013-03-14").value.time, + DateTimeType("2013-03-15").value.time + ) + ) + } + + @Test + fun search_date_equal() { + val query = + Search(ResourceType.Patient) + .apply { + filter(Patient.BIRTHDATE) { + prefix = ParamPrefixEnum.EQUAL + value = DateTimeType("2013-03-14") + } + } + .getQuery() + + assertThat(query.query) + .isEqualTo( + """ + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ? + ) + """.trimIndent() + ) + + assertThat(query.args) + .isEqualTo( + listOf( + ResourceType.Patient.name, + ResourceType.Patient.name, + Patient.BIRTHDATE.paramName, + DateTimeType("2013-03-14").value.time, + DateTimeType("2013-03-15").value.time, + DateTimeType("2013-03-14").value.time, + DateTimeType("2013-03-15").value.time + ) + ) + } + + @Test + fun search_date_greater() { + val query = + Search(ResourceType.Patient) + .apply { + filter(Patient.BIRTHDATE) { + prefix = ParamPrefixEnum.GREATERTHAN + value = DateTimeType("2013-03-14") + } + } + .getQuery() + + assertThat(query.query) + .isEqualTo( + """ + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND ? <= index_from + ) + """.trimIndent() + ) + + assertThat(query.args) + .isEqualTo( + listOf( + ResourceType.Patient.name, + ResourceType.Patient.name, + Patient.BIRTHDATE.paramName, + DateTimeType("2013-03-15").value.time + ) + ) + } + + @Test + fun search_date_greaterOrEqual() { + val query = + Search(ResourceType.Patient) + .apply { + filter(Patient.BIRTHDATE) { + prefix = ParamPrefixEnum.GREATERTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } + } + .getQuery() + + assertThat(query.query) + .isEqualTo( + """ + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND ? <= index_from + ) + """.trimIndent() + ) + + assertThat(query.args) + .isEqualTo( + listOf( + ResourceType.Patient.name, + ResourceType.Patient.name, + Patient.BIRTHDATE.paramName, + DateTimeType("2013-03-14").value.time + ) + ) + } + + @Test + fun search_date_less() { + val query = + Search(ResourceType.Patient) + .apply { + filter(Patient.BIRTHDATE) { + prefix = ParamPrefixEnum.LESSTHAN + value = DateTimeType("2013-03-14") + } + } + .getQuery() + + assertThat(query.query) + .isEqualTo( + """ + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND ? >= index_to + ) + """.trimIndent() + ) + + assertThat(query.args) + .isEqualTo( + listOf( + ResourceType.Patient.name, + ResourceType.Patient.name, + Patient.BIRTHDATE.paramName, + DateTimeType("2013-03-14").value.time + ) + ) + } + + @Test + fun search_date_lessOrEqual() { + val query = + Search(ResourceType.Patient) + .apply { + filter(Patient.BIRTHDATE) { + prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } + } + .getQuery() + + assertThat(query.query) + .isEqualTo( + """ + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? + AND ? >= index_to + ) + """.trimIndent() + ) + + assertThat(query.args) + .isEqualTo( + listOf( + ResourceType.Patient.name, + ResourceType.Patient.name, + Patient.BIRTHDATE.paramName, + DateTimeType("2013-03-15").value.time + ) + ) + } + + @Test + fun search_sort_string_ascending() { val query = Search(ResourceType.Patient).apply { sort(Patient.GIVEN, Order.ASCENDING) }.getQuery() @@ -176,7 +483,7 @@ class SearchTest { } @Test - fun search_sort_descending() { + fun search_sort_string_descending() { val query = Search(ResourceType.Patient).apply { sort(Patient.GIVEN, Order.DESCENDING) }.getQuery() @@ -194,6 +501,26 @@ class SearchTest { assertThat(query.args).isEqualTo(listOf(Patient.GIVEN.paramName, ResourceType.Patient.name)) } + @Test + fun search_sort_numbers_ascending() { + val query = + Search(ResourceType.RiskAssessment) + .apply { sort(RiskAssessment.PROBABILITY, Order.ASCENDING) } + .getQuery() + + assertThat(query.query) + .isEqualTo( + """ + SELECT a.serializedResource + FROM ResourceEntity a + LEFT JOIN NumberIndexEntity b + ON a.resourceType = b.resourceType AND a.resourceId = b.resourceId AND b.index_name = ? + WHERE a.resourceType = ? + ORDER BY b.index_value ASC + """.trimIndent() + ) + } + @Test fun search_filter_sort_size_from() { val query = From 53026fa0da9e29cffd91af3635df2c70c8ab41b8 Mon Sep 17 00:00:00 2001 From: epicadk Date: Mon, 10 May 2021 17:20:36 +0530 Subject: [PATCH 09/25] Add tests --- .../google/android/fhir/search/MoreSearch.kt | 10 +- .../android/fhir/search/MoreSearchTest.kt | 451 ++++++++++++++++++ 2 files changed, 456 insertions(+), 5 deletions(-) create mode 100644 engine/src/test/java/com/google/android/fhir/search/MoreSearchTest.kt 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 d13b7d4dcc..dcd8eb3d26 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 @@ -128,19 +128,19 @@ fun DateFilter.query(type: ResourceType): SearchQuery { listOf(type.name, parameter.paramName, rangeLow, rangeHigh, rangeLow, rangeHigh) ) } - ParamPrefixEnum.STARTS_AFTER -> ">= index_from".also { useHigh = true } - ParamPrefixEnum.ENDS_BEFORE -> "<= index_to" + ParamPrefixEnum.STARTS_AFTER -> "<= index_from".also { useHigh = true } + ParamPrefixEnum.ENDS_BEFORE -> ">= index_to" ParamPrefixEnum.NOT_EQUAL -> return SearchQuery( """SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? - AND index_from NOT BETWEEN ? AND ? AND index_to NOT BETWEEN ? AND ?""", + AND index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ?""", listOf( type.name, parameter.paramName, value!!.value.time, - tsHigh, - value!!.value.time, + tsHigh - 1, + value!!.value.time - 1, tsHigh ) ) diff --git a/engine/src/test/java/com/google/android/fhir/search/MoreSearchTest.kt b/engine/src/test/java/com/google/android/fhir/search/MoreSearchTest.kt new file mode 100644 index 0000000000..1ac413d932 --- /dev/null +++ b/engine/src/test/java/com/google/android/fhir/search/MoreSearchTest.kt @@ -0,0 +1,451 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.fhir.search + +import android.os.Build +import androidx.test.core.app.ApplicationProvider +import ca.uhn.fhir.rest.param.ParamPrefixEnum +import com.google.android.fhir.FhirServices +import com.google.android.fhir.sync.FhirDataSource +import com.google.common.truth.Truth +import kotlinx.coroutines.runBlocking +import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.DateTimeType +import org.hl7.fhir.r4.model.OperationOutcome +import org.hl7.fhir.r4.model.Patient +import org.hl7.fhir.r4.model.Resource +import org.hl7.fhir.r4.model.ResourceType +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.P]) +class MoreSearchTest { + private val dataSource = + object : FhirDataSource { + override suspend fun loadData(path: String): Bundle { + return Bundle() + } + + override suspend fun insert( + resourceType: String, + resourceId: String, + payload: String + ): Resource { + return Patient() + } + + override suspend fun update( + resourceType: String, + resourceId: String, + payload: String + ): OperationOutcome { + return OperationOutcome() + } + + override suspend fun delete(resourceType: String, resourceId: String): OperationOutcome { + return OperationOutcome() + } + } + private val services = + FhirServices.builder(dataSource, ApplicationProvider.getApplicationContext()).inMemory().build() + private val database = services.database + + @Test + fun search_date_approximate() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-13") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-10") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-03") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-23T10:00:00") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.APPROXIMATE + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(3) + Truth.assertThat( + res.all { + it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-04").value.time && + it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-24").value.time + } + ) + .isTrue() + } + @Test + fun search_date_starts_after() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-25") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-10") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-03") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-23T10:00:00") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.STARTS_AFTER + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(2) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } + ) + .isTrue() + } + @Test + fun search_date_ends_before() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-25") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-10") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-03") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-23T10:00:00") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.ENDS_BEFORE + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(2) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-14").value.time } + ) + .isTrue() + } + + @Test + fun search_date_not_equals() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-25") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.NOT_EQUAL + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(2) + Truth.assertThat( + res.all { + it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time || + it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time + } + ) + .isTrue() + } + + @Test + fun search_date_equals() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-25") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.EQUAL + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(2) + Truth.assertThat( + res.all { + it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time && + it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time + } + ) + .isTrue() + } + @Test + fun search_date_greater() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-13") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.GREATERTHAN + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(1) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time } + ) + .isTrue() + } + @Test + fun search_date_greater_or_equal() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-13") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.GREATERTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(3) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } + ) + .isTrue() + } + @Test + fun search_date_less() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-13") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(1) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time } + ) + .isTrue() + } + + @Test + fun search_date_less_or_equal() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-13") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(3) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-15").value.time } + ) + .isTrue() + } +} From 16e0a7a1b5f94b3d1e3fc29fc2119a2de8be1724 Mon Sep 17 00:00:00 2001 From: epicadk Date: Mon, 10 May 2021 21:49:48 +0530 Subject: [PATCH 10/25] Minor fix --- .../com/google/android/fhir/search/MoreSearch.kt | 8 ++++---- .../com/google/android/fhir/search/SearchTest.kt | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) 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 dcd8eb3d26..927d7b521d 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 @@ -140,8 +140,8 @@ fun DateFilter.query(type: ResourceType): SearchQuery { parameter.paramName, value!!.value.time, tsHigh - 1, - value!!.value.time - 1, - tsHigh + value!!.value.time, + tsHigh - 1 ) ) ParamPrefixEnum.EQUAL, null -> @@ -153,9 +153,9 @@ fun DateFilter.query(type: ResourceType): SearchQuery { type.name, parameter.paramName, value!!.value.time, - tsHigh, + tsHigh - 1, value!!.value.time, - tsHigh + tsHigh - 1 ) ) ParamPrefixEnum.GREATERTHAN -> "<= index_from".also { useHigh = true } 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 a2a6a94c36..5d112df4ff 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 @@ -182,7 +182,7 @@ class SearchTest { AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? - AND ? >= index_from + AND ? <= index_from ) """.trimIndent() ) @@ -219,7 +219,7 @@ class SearchTest { AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? - AND ? <= index_to + AND ? >= index_to ) """.trimIndent() ) @@ -256,7 +256,7 @@ class SearchTest { AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? - AND index_from NOT BETWEEN ? AND ? AND index_to NOT BETWEEN ? AND ? + AND index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ? ) """.trimIndent() ) @@ -268,9 +268,9 @@ class SearchTest { ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.time, - DateTimeType("2013-03-15").value.time, + DateTimeType("2013-03-15").value.time - 1, DateTimeType("2013-03-14").value.time, - DateTimeType("2013-03-15").value.time + DateTimeType("2013-03-15").value.time - 1 ) ) } @@ -308,9 +308,9 @@ class SearchTest { ResourceType.Patient.name, Patient.BIRTHDATE.paramName, DateTimeType("2013-03-14").value.time, - DateTimeType("2013-03-15").value.time, + DateTimeType("2013-03-15").value.time - 1, DateTimeType("2013-03-14").value.time, - DateTimeType("2013-03-15").value.time + DateTimeType("2013-03-15").value.time - 1 ) ) } From e3f59a7c9288f48967fffc836d874b06516d2ebd Mon Sep 17 00:00:00 2001 From: epicadk Date: Tue, 1 Jun 2021 14:36:03 +0530 Subject: [PATCH 11/25] Apply spotless --- .../google/android/fhir/search/MoreSearch.kt | 8 +++---- .../google/android/fhir/search/SearchDsl.kt | 4 ++-- .../google/android/fhir/search/SearchTest.kt | 21 ++++++++++--------- 3 files changed, 17 insertions(+), 16 deletions(-) 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 a061289e9f..12ec196cf6 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 @@ -53,8 +53,8 @@ fun Search.getQuery(): SearchQuery { val filterArgs = mutableListOf() val filterQuery = (stringFilters.map { it.query(type) } + - referenceFilter.map { it.query(type) } + - dateFilter.map { it.query(type) }) + referenceFilters.map { it.query(type) } + + dateFilter.map { it.query(type) } + tokenFilters.map { it.query(type) }) .intersect() if (filterQuery != null) { @@ -178,9 +178,9 @@ fun DateFilter.query(type: ResourceType): SearchQuery { AND ? $condition """, listOf(type.name, parameter.paramName, if (!useHigh) value!!.value.time else tsHigh) - ) + ) } - + fun TokenFilter.query(type: ResourceType): SearchQuery { return SearchQuery( """ diff --git a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt index 34be49735d..ef2dfc2022 100644 --- a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -21,13 +21,13 @@ import ca.uhn.fhir.rest.gclient.IParam import ca.uhn.fhir.rest.gclient.NumberClientParam import ca.uhn.fhir.rest.gclient.ReferenceClientParam import ca.uhn.fhir.rest.gclient.StringClientParam -import ca.uhn.fhir.rest.param.ParamPrefixEnum -import org.hl7.fhir.r4.model.DateTimeType import ca.uhn.fhir.rest.gclient.TokenClientParam +import ca.uhn.fhir.rest.param.ParamPrefixEnum import org.hl7.fhir.r4.model.CodeType import org.hl7.fhir.r4.model.CodeableConcept import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.ContactPoint +import org.hl7.fhir.r4.model.DateTimeType import org.hl7.fhir.r4.model.Identifier import org.hl7.fhir.r4.model.ResourceType import org.hl7.fhir.r4.model.UriType 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 8d5130356d..87fccb26a7 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 @@ -17,13 +17,14 @@ package com.google.android.fhir.search import android.os.Build +import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.runBlocking -import org.hl7.fhir.r4.model.DateTimeType import org.hl7.fhir.r4.model.CodeType import org.hl7.fhir.r4.model.CodeableConcept import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.ContactPoint +import org.hl7.fhir.r4.model.DateTimeType import org.hl7.fhir.r4.model.Identifier import org.hl7.fhir.r4.model.Immunization import org.hl7.fhir.r4.model.Patient @@ -543,7 +544,7 @@ class SearchTest { WHERE resourceType = ? AND index_name = ? AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ? ) - """.trimIndent() + """.trimIndent() ) assertThat(query.args) @@ -583,7 +584,7 @@ class SearchTest { WHERE resourceType = ? AND index_name = ? AND ? <= index_from ) - """.trimIndent() + """.trimIndent() ) assertThat(query.args) @@ -620,7 +621,7 @@ class SearchTest { WHERE resourceType = ? AND index_name = ? AND ? >= index_to ) - """.trimIndent() + """.trimIndent() ) assertThat(query.args) @@ -657,7 +658,7 @@ class SearchTest { WHERE resourceType = ? AND index_name = ? AND index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ? ) - """.trimIndent() + """.trimIndent() ) assertThat(query.args) @@ -697,7 +698,7 @@ class SearchTest { WHERE resourceType = ? AND index_name = ? AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ? ) - """.trimIndent() + """.trimIndent() ) assertThat(query.args) @@ -737,7 +738,7 @@ class SearchTest { WHERE resourceType = ? AND index_name = ? AND ? <= index_from ) - """.trimIndent() + """.trimIndent() ) assertThat(query.args) @@ -774,7 +775,7 @@ class SearchTest { WHERE resourceType = ? AND index_name = ? AND ? <= index_from ) - """.trimIndent() + """.trimIndent() ) assertThat(query.args) @@ -811,7 +812,7 @@ class SearchTest { WHERE resourceType = ? AND index_name = ? AND ? >= index_to ) - """.trimIndent() + """.trimIndent() ) assertThat(query.args) @@ -848,7 +849,7 @@ class SearchTest { WHERE resourceType = ? AND index_name = ? AND ? >= index_to ) - """.trimIndent() + """.trimIndent() ) assertThat(query.args) From daf5506ac8498d6f5962263e993bd17c6305924a Mon Sep 17 00:00:00 2001 From: epicadk Date: Thu, 3 Jun 2021 18:08:44 +0530 Subject: [PATCH 12/25] address review comments --- .../android/fhir/db/impl/DatabaseImplTest.kt | 341 +++++++++++++ .../google/android/fhir/search/MoreSearch.kt | 40 +- .../google/android/fhir/search/SearchDsl.kt | 2 +- .../android/fhir/search/MoreSearchTest.kt | 451 ------------------ .../google/android/fhir/search/SearchTest.kt | 58 +-- 5 files changed, 364 insertions(+), 528 deletions(-) delete mode 100644 engine/src/test/java/com/google/android/fhir/search/MoreSearchTest.kt 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 c872339349..c4db4af567 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 @@ -18,6 +18,7 @@ package com.google.android.fhir.db.impl import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.FhirServices import com.google.android.fhir.db.ResourceNotFoundInDbException import com.google.android.fhir.db.impl.entities.LocalChangeEntity @@ -28,10 +29,12 @@ import com.google.android.fhir.search.Search import com.google.android.fhir.search.StringFilterModifier import com.google.android.fhir.search.getQuery import com.google.android.fhir.sync.FhirDataSource +import com.google.common.truth.Truth import com.google.common.truth.Truth.assertThat import java.math.BigDecimal import kotlinx.coroutines.runBlocking import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.DateTimeType import org.hl7.fhir.r4.model.DecimalType import org.hl7.fhir.r4.model.Enumerations import org.hl7.fhir.r4.model.HumanName @@ -505,6 +508,344 @@ class DatabaseImplTest { assertThat(res).hasSize(0) } + @Test + fun search_date_starts_after() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-25") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-10") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-03") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-23T10:00:00") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.STARTS_AFTER + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(2) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } + ) + .isTrue() + } + @Test + fun search_date_ends_before() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-25") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-10") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-03") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-23T10:00:00") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.ENDS_BEFORE + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(2) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-14").value.time } + ) + .isTrue() + } + + @Test + fun search_date_not_equals() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-25") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.NOT_EQUAL + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(2) + Truth.assertThat( + res.all { + it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time || + it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time + } + ) + .isTrue() + } + + @Test + fun search_date_equals() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-25") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.EQUAL + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(2) + Truth.assertThat( + res.all { + it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time && + it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time + } + ) + .isTrue() + } + @Test + fun search_date_greater() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-13") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.GREATERTHAN + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(1) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time } + ) + .isTrue() + } + @Test + fun search_date_greater_or_equal() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-13") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.GREATERTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(3) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } + ) + .isTrue() + } + @Test + fun search_date_less() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-13") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(1) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time } + ) + .isTrue() + } + + @Test + fun search_date_less_or_equal() { + val patient1 = + Patient().apply { + id = "1" + deceased = DateTimeType("2013-03-13") + } + val patient2 = + Patient().apply { + id = "2" + deceased = DateTimeType("2013-03-14") + } + val patient3 = + Patient().apply { + id = "3" + deceased = DateTimeType("2013-03-15") + } + val patient4 = + Patient().apply { + id = "4" + deceased = DateTimeType("2013-03-14T23:59:59.999") + } + val res = runBlocking { + database.insert(patient1, patient2, patient3, patient4) + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + } + Truth.assertThat(res).hasSize(3) + Truth.assertThat( + res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-15").value.time } + ) + .isTrue() + } + private companion object { const val TEST_PATIENT_1_ID = "test_patient_1" val TEST_PATIENT_1 = Patient() 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 12ec196cf6..6450ef09e5 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 @@ -122,21 +122,12 @@ fun ReferenceFilter.query(type: ResourceType): SearchQuery { } fun DateFilter.query(type: ResourceType): SearchQuery { - val tsHigh = value!!.precision.add(value!!.value, 1).time - var useHigh = false + var tsHigh: Long? = null val condition = when (this.prefix) { - ParamPrefixEnum.APPROXIMATE -> { - val rangeHigh = value!!.precision.add(value!!.value, 10).time - val rangeLow = value!!.precision.add(value!!.value, -10).time - return SearchQuery( - """SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?""", - listOf(type.name, parameter.paramName, rangeLow, rangeHigh, rangeLow, rangeHigh) - ) - } - ParamPrefixEnum.STARTS_AFTER -> "<= index_from".also { useHigh = true } + ParamPrefixEnum.APPROXIMATE -> TODO("Not Implemented") + ParamPrefixEnum.STARTS_AFTER -> + "<= index_from".also { tsHigh = value!!.precision.add(value!!.value, 1).time } ParamPrefixEnum.ENDS_BEFORE -> ">= index_to" ParamPrefixEnum.NOT_EQUAL -> return SearchQuery( @@ -147,12 +138,12 @@ fun DateFilter.query(type: ResourceType): SearchQuery { type.name, parameter.paramName, value!!.value.time, - tsHigh - 1, + value!!.precision.add(value!!.value, 1).time - 1, value!!.value.time, - tsHigh - 1 + value!!.precision.add(value!!.value, 1).time - 1 ) ) - ParamPrefixEnum.EQUAL, null -> + ParamPrefixEnum.EQUAL -> return SearchQuery( """SELECT resourceId FROM DateIndexEntity WHERE resourceType = ? AND index_name = ? @@ -161,23 +152,24 @@ fun DateFilter.query(type: ResourceType): SearchQuery { type.name, parameter.paramName, value!!.value.time, - tsHigh - 1, + value!!.precision.add(value!!.value, 1).time - 1, value!!.value.time, - tsHigh - 1 + value!!.precision.add(value!!.value, 1).time - 1 ) ) - ParamPrefixEnum.GREATERTHAN -> "<= index_from".also { useHigh = true } + ParamPrefixEnum.GREATERTHAN -> + "<= index_from".also { tsHigh = value!!.precision.add(value!!.value, 1).time } ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> "<= index_from" ParamPrefixEnum.LESSTHAN -> ">= index_to" - ParamPrefixEnum.LESSTHAN_OR_EQUALS -> ">= index_to".also { useHigh = true } + ParamPrefixEnum.LESSTHAN_OR_EQUALS -> + ">= index_to".also { tsHigh = value!!.precision.add(value!!.value, 1).time } } return SearchQuery( """ SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND ? $condition - """, - listOf(type.name, parameter.paramName, if (!useHigh) value!!.value.time else tsHigh) + WHERE resourceType = ? AND index_name = ? AND ? $condition + """, + listOf(type.name, parameter.paramName, tsHigh ?: value!!.value.time) ) } diff --git a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt index ef2dfc2022..9ea6738f88 100644 --- a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -109,7 +109,7 @@ data class StringFilter( @SearchDslMarker data class DateFilter( val parameter: DateClientParam, - var prefix: ParamPrefixEnum? = null, + var prefix: ParamPrefixEnum = ParamPrefixEnum.EQUAL, var value: DateTimeType? = null ) diff --git a/engine/src/test/java/com/google/android/fhir/search/MoreSearchTest.kt b/engine/src/test/java/com/google/android/fhir/search/MoreSearchTest.kt deleted file mode 100644 index 1ac413d932..0000000000 --- a/engine/src/test/java/com/google/android/fhir/search/MoreSearchTest.kt +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.search - -import android.os.Build -import androidx.test.core.app.ApplicationProvider -import ca.uhn.fhir.rest.param.ParamPrefixEnum -import com.google.android.fhir.FhirServices -import com.google.android.fhir.sync.FhirDataSource -import com.google.common.truth.Truth -import kotlinx.coroutines.runBlocking -import org.hl7.fhir.r4.model.Bundle -import org.hl7.fhir.r4.model.DateTimeType -import org.hl7.fhir.r4.model.OperationOutcome -import org.hl7.fhir.r4.model.Patient -import org.hl7.fhir.r4.model.Resource -import org.hl7.fhir.r4.model.ResourceType -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config - -@RunWith(RobolectricTestRunner::class) -@Config(sdk = [Build.VERSION_CODES.P]) -class MoreSearchTest { - private val dataSource = - object : FhirDataSource { - override suspend fun loadData(path: String): Bundle { - return Bundle() - } - - override suspend fun insert( - resourceType: String, - resourceId: String, - payload: String - ): Resource { - return Patient() - } - - override suspend fun update( - resourceType: String, - resourceId: String, - payload: String - ): OperationOutcome { - return OperationOutcome() - } - - override suspend fun delete(resourceType: String, resourceId: String): OperationOutcome { - return OperationOutcome() - } - } - private val services = - FhirServices.builder(dataSource, ApplicationProvider.getApplicationContext()).inMemory().build() - private val database = services.database - - @Test - fun search_date_approximate() { - val patient1 = - Patient().apply { - id = "1" - deceased = DateTimeType("2013-03-13") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-10") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-03") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-23T10:00:00") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.APPROXIMATE - value = DateTimeType("2013-03-14") - } - } - .getQuery() - ) - } - Truth.assertThat(res).hasSize(3) - Truth.assertThat( - res.all { - it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-04").value.time && - it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-24").value.time - } - ) - .isTrue() - } - @Test - fun search_date_starts_after() { - val patient1 = - Patient().apply { - id = "1" - deceased = DateTimeType("2013-03-25") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-10") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-03") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-23T10:00:00") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.STARTS_AFTER - value = DateTimeType("2013-03-14") - } - } - .getQuery() - ) - } - Truth.assertThat(res).hasSize(2) - Truth.assertThat( - res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } - ) - .isTrue() - } - @Test - fun search_date_ends_before() { - val patient1 = - Patient().apply { - id = "1" - deceased = DateTimeType("2013-03-25") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-10") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-03") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-23T10:00:00") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.ENDS_BEFORE - value = DateTimeType("2013-03-14") - } - } - .getQuery() - ) - } - Truth.assertThat(res).hasSize(2) - Truth.assertThat( - res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-14").value.time } - ) - .isTrue() - } - - @Test - fun search_date_not_equals() { - val patient1 = - Patient().apply { - id = "1" - deceased = DateTimeType("2013-03-25") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.NOT_EQUAL - value = DateTimeType("2013-03-14") - } - } - .getQuery() - ) - } - Truth.assertThat(res).hasSize(2) - Truth.assertThat( - res.all { - it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time || - it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time - } - ) - .isTrue() - } - - @Test - fun search_date_equals() { - val patient1 = - Patient().apply { - id = "1" - deceased = DateTimeType("2013-03-25") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.EQUAL - value = DateTimeType("2013-03-14") - } - } - .getQuery() - ) - } - Truth.assertThat(res).hasSize(2) - Truth.assertThat( - res.all { - it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time && - it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time - } - ) - .isTrue() - } - @Test - fun search_date_greater() { - val patient1 = - Patient().apply { - id = "1" - deceased = DateTimeType("2013-03-13") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.GREATERTHAN - value = DateTimeType("2013-03-14") - } - } - .getQuery() - ) - } - Truth.assertThat(res).hasSize(1) - Truth.assertThat( - res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time } - ) - .isTrue() - } - @Test - fun search_date_greater_or_equal() { - val patient1 = - Patient().apply { - id = "1" - deceased = DateTimeType("2013-03-13") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.GREATERTHAN_OR_EQUALS - value = DateTimeType("2013-03-14") - } - } - .getQuery() - ) - } - Truth.assertThat(res).hasSize(3) - Truth.assertThat( - res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } - ) - .isTrue() - } - @Test - fun search_date_less() { - val patient1 = - Patient().apply { - id = "1" - deceased = DateTimeType("2013-03-13") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.LESSTHAN - value = DateTimeType("2013-03-14") - } - } - .getQuery() - ) - } - Truth.assertThat(res).hasSize(1) - Truth.assertThat( - res.all { it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time } - ) - .isTrue() - } - - @Test - fun search_date_less_or_equal() { - val patient1 = - Patient().apply { - id = "1" - deceased = DateTimeType("2013-03-13") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS - value = DateTimeType("2013-03-14") - } - } - .getQuery() - ) - } - Truth.assertThat(res).hasSize(3) - Truth.assertThat( - res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-15").value.time } - ) - .isTrue() - } -} 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 87fccb26a7..ffe514e531 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 @@ -521,46 +521,6 @@ class SearchTest { ) } - @Test - fun search_date_approximate() { - val query = - Search(ResourceType.Patient) - .apply { - filter(Patient.BIRTHDATE) { - prefix = ParamPrefixEnum.APPROXIMATE - value = DateTimeType("2013-03-14") - } - } - .getQuery() - - assertThat(query.query) - .isEqualTo( - """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ? - ) - """.trimIndent() - ) - - assertThat(query.args) - .isEqualTo( - listOf( - ResourceType.Patient.name, - ResourceType.Patient.name, - Patient.BIRTHDATE.paramName, - DateTimeType("2013-03-04").value.time, - DateTimeType("2013-03-24").value.time, - DateTimeType("2013-03-04").value.time, - DateTimeType("2013-03-24").value.time - ) - ) - } - @Test fun search_date_starts_after() { val query = @@ -581,8 +541,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND ? <= index_from + WHERE resourceType = ? AND index_name = ? AND ? <= index_from ) """.trimIndent() ) @@ -618,8 +577,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND ? >= index_to + WHERE resourceType = ? AND index_name = ? AND ? >= index_to ) """.trimIndent() ) @@ -735,8 +693,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND ? <= index_from + WHERE resourceType = ? AND index_name = ? AND ? <= index_from ) """.trimIndent() ) @@ -772,8 +729,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND ? <= index_from + WHERE resourceType = ? AND index_name = ? AND ? <= index_from ) """.trimIndent() ) @@ -809,8 +765,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND ? >= index_to + WHERE resourceType = ? AND index_name = ? AND ? >= index_to ) """.trimIndent() ) @@ -846,8 +801,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND ? >= index_to + WHERE resourceType = ? AND index_name = ? AND ? >= index_to ) """.trimIndent() ) From ca58df51fb30e1223e8fdc66207a4d6682e1c600 Mon Sep 17 00:00:00 2001 From: epicadk Date: Thu, 3 Jun 2021 23:51:20 +0530 Subject: [PATCH 13/25] Apply spotless --- .../android/fhir/db/impl/DatabaseImplTest.kt | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 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 8d8063bbde..68c2779668 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 @@ -28,9 +28,8 @@ import com.google.android.fhir.search.Order import com.google.android.fhir.search.Search import com.google.android.fhir.search.StringFilterModifier import com.google.android.fhir.search.getQuery -import com.google.android.fhir.sync.FhirDataSource -import com.google.common.truth.Truth import com.google.android.fhir.sync.DataSource +import com.google.common.truth.Truth import com.google.common.truth.Truth.assertThat import java.math.BigDecimal import kotlinx.coroutines.runBlocking @@ -544,8 +543,8 @@ class DatabaseImplTest { .getQuery() ) } - Truth.assertThat(res).hasSize(2) - Truth.assertThat( + assertThat(res).hasSize(2) + assertThat( res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } ) .isTrue() @@ -585,8 +584,8 @@ class DatabaseImplTest { .getQuery() ) } - Truth.assertThat(res).hasSize(2) - Truth.assertThat( + assertThat(res).hasSize(2) + assertThat( res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-14").value.time } ) .isTrue() @@ -627,8 +626,8 @@ class DatabaseImplTest { .getQuery() ) } - Truth.assertThat(res).hasSize(2) - Truth.assertThat( + assertThat(res).hasSize(2) + assertThat( res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time || it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time @@ -672,8 +671,8 @@ class DatabaseImplTest { .getQuery() ) } - Truth.assertThat(res).hasSize(2) - Truth.assertThat( + assertThat(res).hasSize(2) + assertThat( res.all { it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time && it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time @@ -716,8 +715,8 @@ class DatabaseImplTest { .getQuery() ) } - Truth.assertThat(res).hasSize(1) - Truth.assertThat( + assertThat(res).hasSize(1) + assertThat( res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time } ) .isTrue() @@ -757,8 +756,8 @@ class DatabaseImplTest { .getQuery() ) } - Truth.assertThat(res).hasSize(3) - Truth.assertThat( + assertThat(res).hasSize(3) + assertThat( res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } ) .isTrue() @@ -798,8 +797,8 @@ class DatabaseImplTest { .getQuery() ) } - Truth.assertThat(res).hasSize(1) - Truth.assertThat( + assertThat(res).hasSize(1) + assertThat( res.all { it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time } ) .isTrue() @@ -840,8 +839,8 @@ class DatabaseImplTest { .getQuery() ) } - Truth.assertThat(res).hasSize(3) - Truth.assertThat( + assertThat(res).hasSize(3) + assertThat( res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-15").value.time } ) .isTrue() From fe0174e62bb4855f832cd806d8019e94f3b2c98a Mon Sep 17 00:00:00 2001 From: epicadk Date: Fri, 4 Jun 2021 08:54:19 +0530 Subject: [PATCH 14/25] Apply spotless --- .../java/com/google/android/fhir/db/impl/DatabaseImplTest.kt | 1 - 1 file changed, 1 deletion(-) 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 68c2779668..043f9c5d45 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 @@ -29,7 +29,6 @@ import com.google.android.fhir.search.Search import com.google.android.fhir.search.StringFilterModifier import com.google.android.fhir.search.getQuery import com.google.android.fhir.sync.DataSource -import com.google.common.truth.Truth import com.google.common.truth.Truth.assertThat import java.math.BigDecimal import kotlinx.coroutines.runBlocking From 93d80f5401b658c879650d7c4b45d895f944fae4 Mon Sep 17 00:00:00 2001 From: epicadk Date: Mon, 7 Jun 2021 23:33:28 +0530 Subject: [PATCH 15/25] address review comments --- .../google/android/fhir/search/MoreSearch.kt | 80 +++++++++---------- .../google/android/fhir/search/SearchTest.kt | 6 +- 2 files changed, 38 insertions(+), 48 deletions(-) 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 2ceacd8d07..3067ca9869 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 @@ -20,6 +20,7 @@ import ca.uhn.fhir.rest.gclient.NumberClientParam import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.db.Database +import org.hl7.fhir.r4.model.DateTimeType import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.ResourceType @@ -126,54 +127,14 @@ fun ReferenceFilter.query(type: ResourceType): SearchQuery { } fun DateFilter.query(type: ResourceType): SearchQuery { - var tsHigh: Long? = null - val condition = - when (this.prefix) { - ParamPrefixEnum.APPROXIMATE -> TODO("Not Implemented") - ParamPrefixEnum.STARTS_AFTER -> - "<= index_from".also { tsHigh = value!!.precision.add(value!!.value, 1).time } - ParamPrefixEnum.ENDS_BEFORE -> ">= index_to" - ParamPrefixEnum.NOT_EQUAL -> - return SearchQuery( - """SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ?""", - listOf( - type.name, - parameter.paramName, - value!!.value.time, - value!!.precision.add(value!!.value, 1).time - 1, - value!!.value.time, - value!!.precision.add(value!!.value, 1).time - 1 - ) - ) - ParamPrefixEnum.EQUAL -> - return SearchQuery( - """SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?""", - listOf( - type.name, - parameter.paramName, - value!!.value.time, - value!!.precision.add(value!!.value, 1).time - 1, - value!!.value.time, - value!!.precision.add(value!!.value, 1).time - 1 - ) - ) - ParamPrefixEnum.GREATERTHAN -> - "<= index_from".also { tsHigh = value!!.precision.add(value!!.value, 1).time } - ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> "<= index_from" - ParamPrefixEnum.LESSTHAN -> ">= index_to" - ParamPrefixEnum.LESSTHAN_OR_EQUALS -> - ">= index_to".also { tsHigh = value!!.precision.add(value!!.value, 1).time } - } + val value = value!! + val conditionParamPair = getConditionParamPair(prefix, value) return SearchQuery( """ SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ? $condition + WHERE resourceType = ? AND index_name = ? AND ${conditionParamPair.first} """, - listOf(type.name, parameter.paramName, tsHigh ?: value!!.value.time) + listOf(type.name, parameter.paramName) + conditionParamPair.second ) } @@ -203,3 +164,34 @@ val Order?.sqlString: String Order.DESCENDING -> "DESC" null -> "" } + +private fun getConditionParamPair(prefix: ParamPrefixEnum, value: DateTimeType): Pair> { + return when (prefix) { + ParamPrefixEnum.APPROXIMATE -> TODO("Not Implemented") + ParamPrefixEnum.STARTS_AFTER -> + "? <= index_from" to listOf(value.precision.add(value.value, 1).time) + ParamPrefixEnum.ENDS_BEFORE -> "? >= index_to" to listOf(value.value.time) + ParamPrefixEnum.NOT_EQUAL -> + "index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ?" to + listOf( + value.value.time, + value.precision.add(value.value, 1).time - 1, + value.value.time, + value.precision.add(value.value, 1).time - 1 + ) + ParamPrefixEnum.EQUAL -> + "index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?" to + listOf( + value.value.time, + value.precision.add(value.value, 1).time - 1, + value.value.time, + value.precision.add(value.value, 1).time - 1 + ) + ParamPrefixEnum.GREATERTHAN -> + "? <= index_from" to listOf(value.precision.add(value.value, 1).time) + ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> "? <= index_from" to listOf(value.value.time) + ParamPrefixEnum.LESSTHAN -> "? >= index_to" to listOf(value.value.time) + ParamPrefixEnum.LESSTHAN_OR_EQUALS -> + "? >= index_to" to listOf(value.precision.add(value.value, 1).time) + } +} 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 2219b4db3b..5f73223f20 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 @@ -628,8 +628,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ? + WHERE resourceType = ? AND index_name = ? AND index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ? ) """.trimIndent() ) @@ -668,8 +667,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? - AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ? + WHERE resourceType = ? AND index_name = ? AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ? ) """.trimIndent() ) From 2610cce320155cac93e02fb726604b8fe372ef15 Mon Sep 17 00:00:00 2001 From: epicadk Date: Mon, 14 Jun 2021 11:44:51 +0530 Subject: [PATCH 16/25] Apply spotless --- .../java/com/google/android/fhir/search/MoreSearch.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 d0353a1f62..c78a387b15 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 @@ -20,8 +20,8 @@ import ca.uhn.fhir.rest.gclient.NumberClientParam import ca.uhn.fhir.rest.gclient.StringClientParam import ca.uhn.fhir.rest.param.ParamPrefixEnum import com.google.android.fhir.db.Database -import org.hl7.fhir.r4.model.DateTimeType import java.math.BigDecimal +import org.hl7.fhir.r4.model.DateTimeType import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.ResourceType @@ -183,7 +183,10 @@ val Order?.sqlString: String null -> "" } -private fun getConditionParamPair(prefix: ParamPrefixEnum, value: DateTimeType): Pair> { +private fun getConditionParamPair( + prefix: ParamPrefixEnum, + value: DateTimeType +): Pair> { return when (prefix) { ParamPrefixEnum.APPROXIMATE -> TODO("Not Implemented") ParamPrefixEnum.STARTS_AFTER -> @@ -212,7 +215,7 @@ private fun getConditionParamPair(prefix: ParamPrefixEnum, value: DateTimeType): ParamPrefixEnum.LESSTHAN_OR_EQUALS -> "? >= index_to" to listOf(value.precision.add(value.value, 1).time) } -} +} /** * Returns the condition and list of params required in NumberFilter.query see * https://www.hl7.org/fhir/search.html#number. From 38e4942e8ca8c765d7c181ae3dbbdb538e755585 Mon Sep 17 00:00:00 2001 From: epicadk Date: Wed, 16 Jun 2021 00:24:09 +0530 Subject: [PATCH 17/25] Improve tests --- .../android/fhir/db/impl/DatabaseImplTest.kt | 439 ++++++++++-------- 1 file changed, 234 insertions(+), 205 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 f27a4e8f0c..9ec2cad10b 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 @@ -966,30 +966,14 @@ class DatabaseImplTest { } @Test - fun search_date_starts_after() { - val patient1 = + fun search_date_starts_after() = runBlocking{ + val patient = Patient().apply { id = "1" - deceased = DateTimeType("2013-03-25") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-10") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-03") - } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-23T10:00:00") + deceased = DateTimeType("2013-03-23T10:00:00-05:30") } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( + database.insert(patient) + val result = database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -999,38 +983,43 @@ class DatabaseImplTest { } .getQuery() ) - } - assertThat(res).hasSize(2) assertThat( - res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } - ) - .isTrue() + result.single().id + ).isEqualTo("Patient/1") } + @Test - fun search_date_ends_before() { - val patient1 = + fun search_date_starts_after_noMatch() = runBlocking{ + val patient = Patient().apply { id = "1" - deceased = DateTimeType("2013-03-25") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-10") + deceased = DateTimeType("2013-03-13T10:00:00-05:30") } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-03") - } - val patient4 = + database.insert(patient) + val result = database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.STARTS_AFTER + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + assertThat( + result + ).isEmpty() + } + + @Test + fun search_date_ends_before() = runBlocking{ + val patient = Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-23T10:00:00") + id = "1" + deceased = DateTimeType("2013-03-13T01:00:00") } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( + database.insert(patient) + val result = database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1040,39 +1029,43 @@ class DatabaseImplTest { } .getQuery() ) - } - assertThat(res).hasSize(2) assertThat( - res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-14").value.time } - ) - .isTrue() + result.single().id + ).isEqualTo("Patient/1") } @Test - fun search_date_not_equals() { - val patient1 = + fun search_date_ends_before_noMatch() = runBlocking{ + val patient = Patient().apply { id = "1" - deceased = DateTimeType("2013-03-25") + deceased = DateTimeType("2014-03-13T01:00:00") } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") - } - val patient4 = + database.insert(patient) + val result = database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.ENDS_BEFORE + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + assertThat( + result + ).isEmpty() + } + + @Test + fun search_date_not_equals() = runBlocking{ + val patient = Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") + id = "1" + deceased = DateTimeType("2013-03-13T10:00:00-05:30") } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( + database.insert(patient) + val result = database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1082,42 +1075,40 @@ class DatabaseImplTest { } .getQuery() ) - } - assertThat(res).hasSize(2) - assertThat( - res.all { - it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time || - it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time - } - ) - .isTrue() + assertThat(result.single().id).isEqualTo("Patient/1") } @Test - fun search_date_equals() { - val patient1 = + fun search_date_not_equals_noMatch() = runBlocking{ + val patient = Patient().apply { id = "1" - deceased = DateTimeType("2013-03-25") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") + deceased = DateTimeType("2013-03-14T10:00:00-05:30") } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") - } - val patient4 = + database.insert(patient) + val result = database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.NOT_EQUAL + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + assertThat(result).isEmpty() + } + + + @Test + fun search_date_equals() = runBlocking{ + val patient = Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") + id = "1" + deceased = DateTimeType("2013-03-14T10:00:00-05:30") } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( + database.insert(patient) + val result = database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1127,41 +1118,45 @@ class DatabaseImplTest { } .getQuery() ) - } - assertThat(res).hasSize(2) assertThat( - res.all { - it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time && - it.deceasedDateTimeType.value.time < DateTimeType("2013-03-15").value.time - } + result.single().id ) - .isTrue() + .isEqualTo("Patient/1") } + @Test - fun search_date_greater() { - val patient1 = + fun search_date_equals_noMatch() = runBlocking{ + val patient = Patient().apply { id = "1" - deceased = DateTimeType("2013-03-13") + deceased = DateTimeType("2013-03-13T10:00:00-05:30") } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = + database.insert(patient) + val result = database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.EQUAL + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + assertThat( + result + ) + .isEmpty() + } + + @Test + fun search_date_greater() = runBlocking { + val patient = Patient().apply { - id = "3" + id = "1" deceased = DateTimeType("2013-03-15") } - val patient4 = - Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") - } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( + database.insert(patient) + val result = database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1171,38 +1166,43 @@ class DatabaseImplTest { } .getQuery() ) - } - assertThat(res).hasSize(1) assertThat( - res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-15").value.time } - ) - .isTrue() + result.single().id + ).isEqualTo("Patient/1") } + @Test - fun search_date_greater_or_equal() { - val patient1 = + fun search_date_greater_noMatch() = runBlocking { + val patient = Patient().apply { id = "1" - deceased = DateTimeType("2013-03-13") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") + deceased = DateTimeType("2013-03-14T10:00:00-05:30") } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") - } - val patient4 = + database.insert(patient) + val result = database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.GREATERTHAN + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + assertThat( + result + ).isEmpty() + } + + @Test + fun search_date_greater_or_equal() = runBlocking{ + val patient = Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") + id = "1" + deceased = DateTimeType("2013-03-14T10:00:00-05:30") } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( + database.insert(patient) + val result = database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1212,38 +1212,42 @@ class DatabaseImplTest { } .getQuery() ) - } - assertThat(res).hasSize(3) assertThat( - res.all { it.deceasedDateTimeType.value.time >= DateTimeType("2013-03-14").value.time } - ) - .isTrue() + result.single().id + ).isEqualTo("Patient/1") } @Test - fun search_date_less() { - val patient1 = + fun search_date_greater_or_equal_noMatch() = runBlocking{ + val patient = Patient().apply { id = "1" - deceased = DateTimeType("2013-03-13") - } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = - Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") + deceased = DateTimeType("2013-03-13T10:00:00-05:30") } - val patient4 = + database.insert(patient) + val result = database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.GREATERTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + assertThat( + result + ).isEmpty() + } + + @Test + fun search_date_less() = runBlocking { + val patient = Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") + id = "1" + deceased = DateTimeType("2013-03-13") } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( + database.insert(patient) + val result = database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1253,54 +1257,79 @@ class DatabaseImplTest { } .getQuery() ) - } - assertThat(res).hasSize(1) assertThat( - res.all { it.deceasedDateTimeType.value.time < DateTimeType("2013-03-14").value.time } - ) - .isTrue() + result.single().id + ).isEqualTo("Patient/1") } @Test - fun search_date_less_or_equal() { - val patient1 = + fun search_date_less_noMatch() = runBlocking { + val patient = Patient().apply { id = "1" - deceased = DateTimeType("2013-03-13") + deceased = DateTimeType("2013-03-14T10:00:00-05:30") } - val patient2 = - Patient().apply { - id = "2" - deceased = DateTimeType("2013-03-14") - } - val patient3 = + database.insert(patient) + val result = database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + assertThat( + result + ).isEmpty() + } + + + @Test + fun search_date_less_or_equal() = runBlocking { + val patient = Patient().apply { - id = "3" - deceased = DateTimeType("2013-03-15") + id = "1" + deceased = DateTimeType("2013-03-14T10:00:00-05:30") } - val patient4 = + database.insert(patient) + val result = database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } + } + .getQuery() + ) + assertThat( + result.single().id + ).isEqualTo("Patient/1") + } + + @Test + fun search_date_less_or_equal_noMatch() = runBlocking { + val patient = Patient().apply { - id = "4" - deceased = DateTimeType("2013-03-14T23:59:59.999") + id = "1" + deceased = DateTimeType("2013-03-14T23:00:00-05:30") } - val res = runBlocking { - database.insert(patient1, patient2, patient3, patient4) - database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS - value = DateTimeType("2013-03-14") - } + database.insert(patient) + val result = database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") } - .getQuery() - ) - } - assertThat(res).hasSize(3) + } + .getQuery() + ) assertThat( - res.all { it.deceasedDateTimeType.value.time <= DateTimeType("2013-03-15").value.time } - ) - .isTrue() + result + ).isEmpty() } private companion object { From c166f67e8a4c7e5aff6b61461da4084441e68fd2 Mon Sep 17 00:00:00 2001 From: Aditya Kurkure <56596662+epicadk@users.noreply.github.com> Date: Wed, 16 Jun 2021 22:24:58 +0530 Subject: [PATCH 18/25] Update engine/src/main/java/com/google/android/fhir/search/MoreSearch.kt Co-authored-by: Jing Tang --- .../src/main/java/com/google/android/fhir/search/MoreSearch.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 c78a387b15..df3f138811 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 @@ -145,8 +145,7 @@ fun ReferenceFilter.query(type: ResourceType): SearchQuery { } fun DateFilter.query(type: ResourceType): SearchQuery { - val value = value!! - val conditionParamPair = getConditionParamPair(prefix, value) + val conditionParamPair = getConditionParamPair(prefix, value!!) return SearchQuery( """ SELECT resourceId FROM DateIndexEntity From f53a1d41cdb4a0f0c6a6bd8d03128198f1fd826e Mon Sep 17 00:00:00 2001 From: Aditya Kurkure <56596662+epicadk@users.noreply.github.com> Date: Wed, 16 Jun 2021 23:14:05 +0530 Subject: [PATCH 19/25] Update engine/src/main/java/com/google/android/fhir/search/MoreSearch.kt Co-authored-by: Jing Tang --- .../src/main/java/com/google/android/fhir/search/MoreSearch.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 df3f138811..efae246fee 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 @@ -210,7 +210,7 @@ private fun getConditionParamPair( ParamPrefixEnum.GREATERTHAN -> "? <= index_from" to listOf(value.precision.add(value.value, 1).time) ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> "? <= index_from" to listOf(value.value.time) - ParamPrefixEnum.LESSTHAN -> "? >= index_to" to listOf(value.value.time) + ParamPrefixEnum.LESSTHAN -> "? >= index_from" to listOf(value.value.time) ParamPrefixEnum.LESSTHAN_OR_EQUALS -> "? >= index_to" to listOf(value.precision.add(value.value, 1).time) } From c5bae83e39e24a3c8d28de44d67addc5dcc0642f Mon Sep 17 00:00:00 2001 From: Aditya Kurkure <56596662+epicadk@users.noreply.github.com> Date: Wed, 16 Jun 2021 23:14:33 +0530 Subject: [PATCH 20/25] Update engine/src/main/java/com/google/android/fhir/search/MoreSearch.kt Co-authored-by: Jing Tang --- .../src/main/java/com/google/android/fhir/search/MoreSearch.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 efae246fee..1fd6a32c42 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 @@ -208,7 +208,7 @@ private fun getConditionParamPair( value.precision.add(value.value, 1).time - 1 ) ParamPrefixEnum.GREATERTHAN -> - "? <= index_from" to listOf(value.precision.add(value.value, 1).time) + "? <= index_to" to listOf(value.precision.add(value.value, 1).time) ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> "? <= index_from" to listOf(value.value.time) ParamPrefixEnum.LESSTHAN -> "? >= index_from" to listOf(value.value.time) ParamPrefixEnum.LESSTHAN_OR_EQUALS -> From 9dbc124089e5859841a6cebdf2408384899318be Mon Sep 17 00:00:00 2001 From: epicadk Date: Thu, 17 Jun 2021 00:16:52 +0530 Subject: [PATCH 21/25] address review comments --- .../android/fhir/db/impl/DatabaseImplTest.kt | 286 +++++++++--------- .../google/android/fhir/search/MoreSearch.kt | 86 +++--- .../google/android/fhir/search/SearchDsl.kt | 1 + .../google/android/fhir/search/SearchTest.kt | 10 +- 4 files changed, 190 insertions(+), 193 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 9ec2cad10b..59b01e3d26 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 @@ -966,14 +966,15 @@ class DatabaseImplTest { } @Test - fun search_date_starts_after() = runBlocking{ + fun search_date_starts_after() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2013-03-23T10:00:00-05:30") } - database.insert(patient) - val result = database.search( + database.insert(patient) + val result = + database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -983,43 +984,41 @@ class DatabaseImplTest { } .getQuery() ) - assertThat( - result.single().id - ).isEqualTo("Patient/1") + assertThat(result.single().id).isEqualTo("Patient/1") } @Test - fun search_date_starts_after_noMatch() = runBlocking{ + fun search_date_starts_after_noMatch() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2013-03-13T10:00:00-05:30") } database.insert(patient) - val result = database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.STARTS_AFTER - value = DateTimeType("2013-03-14") + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.STARTS_AFTER + value = DateTimeType("2013-03-14") + } } - } - .getQuery() - ) - assertThat( - result - ).isEmpty() + .getQuery() + ) + assertThat(result).isEmpty() } @Test - fun search_date_ends_before() = runBlocking{ + fun search_date_ends_before() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2013-03-13T01:00:00") } - database.insert(patient) - val result = database.search( + database.insert(patient) + val result = + database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1029,43 +1028,41 @@ class DatabaseImplTest { } .getQuery() ) - assertThat( - result.single().id - ).isEqualTo("Patient/1") + assertThat(result.single().id).isEqualTo("Patient/1") } @Test - fun search_date_ends_before_noMatch() = runBlocking{ + fun search_date_ends_before_noMatch() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2014-03-13T01:00:00") } database.insert(patient) - val result = database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.ENDS_BEFORE - value = DateTimeType("2013-03-14") + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.ENDS_BEFORE + value = DateTimeType("2013-03-14") + } } - } - .getQuery() - ) - assertThat( - result - ).isEmpty() + .getQuery() + ) + assertThat(result).isEmpty() } @Test - fun search_date_not_equals() = runBlocking{ + fun search_date_not_equals() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2013-03-13T10:00:00-05:30") } - database.insert(patient) - val result = database.search( + database.insert(patient) + val result = + database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1079,36 +1076,37 @@ class DatabaseImplTest { } @Test - fun search_date_not_equals_noMatch() = runBlocking{ + fun search_date_not_equals_noMatch() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2013-03-14T10:00:00-05:30") } database.insert(patient) - val result = database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.NOT_EQUAL - value = DateTimeType("2013-03-14") + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.NOT_EQUAL + value = DateTimeType("2013-03-14") + } } - } - .getQuery() - ) + .getQuery() + ) assertThat(result).isEmpty() } - @Test - fun search_date_equals() = runBlocking{ + fun search_date_equals() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2013-03-14T10:00:00-05:30") } - database.insert(patient) - val result = database.search( + database.insert(patient) + val result = + database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1118,34 +1116,29 @@ class DatabaseImplTest { } .getQuery() ) - assertThat( - result.single().id - ) - .isEqualTo("Patient/1") + assertThat(result.single().id).isEqualTo("Patient/1") } @Test - fun search_date_equals_noMatch() = runBlocking{ + fun search_date_equals_noMatch() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2013-03-13T10:00:00-05:30") } database.insert(patient) - val result = database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.EQUAL - value = DateTimeType("2013-03-14") + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.EQUAL + value = DateTimeType("2013-03-14") + } } - } - .getQuery() - ) - assertThat( - result - ) - .isEmpty() + .getQuery() + ) + assertThat(result).isEmpty() } @Test @@ -1155,8 +1148,9 @@ class DatabaseImplTest { id = "1" deceased = DateTimeType("2013-03-15") } - database.insert(patient) - val result = database.search( + database.insert(patient) + val result = + database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1166,9 +1160,7 @@ class DatabaseImplTest { } .getQuery() ) - assertThat( - result.single().id - ).isEqualTo("Patient/1") + assertThat(result.single().id).isEqualTo("Patient/1") } @Test @@ -1179,30 +1171,30 @@ class DatabaseImplTest { deceased = DateTimeType("2013-03-14T10:00:00-05:30") } database.insert(patient) - val result = database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.GREATERTHAN - value = DateTimeType("2013-03-14") + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.GREATERTHAN + value = DateTimeType("2013-03-14") + } } - } - .getQuery() - ) - assertThat( - result - ).isEmpty() + .getQuery() + ) + assertThat(result).isEmpty() } @Test - fun search_date_greater_or_equal() = runBlocking{ + fun search_date_greater_or_equal() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2013-03-14T10:00:00-05:30") } - database.insert(patient) - val result = database.search( + database.insert(patient) + val result = + database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1212,31 +1204,28 @@ class DatabaseImplTest { } .getQuery() ) - assertThat( - result.single().id - ).isEqualTo("Patient/1") + assertThat(result.single().id).isEqualTo("Patient/1") } @Test - fun search_date_greater_or_equal_noMatch() = runBlocking{ + fun search_date_greater_or_equal_noMatch() = runBlocking { val patient = Patient().apply { id = "1" deceased = DateTimeType("2013-03-13T10:00:00-05:30") } database.insert(patient) - val result = database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.GREATERTHAN_OR_EQUALS - value = DateTimeType("2013-03-14") + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.GREATERTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } } - } - .getQuery() - ) - assertThat( - result - ).isEmpty() + .getQuery() + ) + assertThat(result).isEmpty() } @Test @@ -1246,8 +1235,9 @@ class DatabaseImplTest { id = "1" deceased = DateTimeType("2013-03-13") } - database.insert(patient) - val result = database.search( + database.insert(patient) + val result = + database.search( Search(ResourceType.Patient) .apply { filter(Patient.DEATH_DATE) { @@ -1257,9 +1247,7 @@ class DatabaseImplTest { } .getQuery() ) - assertThat( - result.single().id - ).isEqualTo("Patient/1") + assertThat(result.single().id).isEqualTo("Patient/1") } @Test @@ -1270,22 +1258,20 @@ class DatabaseImplTest { deceased = DateTimeType("2013-03-14T10:00:00-05:30") } database.insert(patient) - val result = database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.LESSTHAN - value = DateTimeType("2013-03-14") + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN + value = DateTimeType("2013-03-14") + } } - } - .getQuery() - ) - assertThat( - result - ).isEmpty() + .getQuery() + ) + assertThat(result).isEmpty() } - @Test fun search_date_less_or_equal() = runBlocking { val patient = @@ -1294,19 +1280,18 @@ class DatabaseImplTest { deceased = DateTimeType("2013-03-14T10:00:00-05:30") } database.insert(patient) - val result = database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS - value = DateTimeType("2013-03-14") + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } } - } - .getQuery() - ) - assertThat( - result.single().id - ).isEqualTo("Patient/1") + .getQuery() + ) + assertThat(result.single().id).isEqualTo("Patient/1") } @Test @@ -1317,19 +1302,18 @@ class DatabaseImplTest { deceased = DateTimeType("2013-03-14T23:00:00-05:30") } database.insert(patient) - val result = database.search( - Search(ResourceType.Patient) - .apply { - filter(Patient.DEATH_DATE) { - prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS - value = DateTimeType("2013-03-14") + val result = + database.search( + Search(ResourceType.Patient) + .apply { + filter(Patient.DEATH_DATE) { + prefix = ParamPrefixEnum.LESSTHAN_OR_EQUALS + value = DateTimeType("2013-03-14") + } } - } - .getQuery() - ) - assertThat( - result - ).isEmpty() + .getQuery() + ) + assertThat(result).isEmpty() } private companion object { 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 1fd6a32c42..b80293cc88 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 @@ -128,9 +128,9 @@ fun NumberFilter.query(type: ResourceType): SearchQuery { return SearchQuery( """ SELECT resourceId FROM NumberIndexEntity - WHERE resourceType = ? AND index_name = ? AND ${conditionParamPair.first} + WHERE resourceType = ? AND index_name = ? AND ${conditionParamPair.condition} """, - listOf(type.name, parameter.paramName) + conditionParamPair.second + listOf(type.name, parameter.paramName) + conditionParamPair.params ) } @@ -149,9 +149,9 @@ fun DateFilter.query(type: ResourceType): SearchQuery { return SearchQuery( """ SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ${conditionParamPair.first} + WHERE resourceType = ? AND index_name = ? AND ${conditionParamPair.condition} """, - listOf(type.name, parameter.paramName) + conditionParamPair.second + listOf(type.name, parameter.paramName) + conditionParamPair.params ) } @@ -185,34 +185,27 @@ val Order?.sqlString: String private fun getConditionParamPair( prefix: ParamPrefixEnum, value: DateTimeType -): Pair> { +): ConditionParam { return when (prefix) { ParamPrefixEnum.APPROXIMATE -> TODO("Not Implemented") - ParamPrefixEnum.STARTS_AFTER -> - "? <= index_from" to listOf(value.precision.add(value.value, 1).time) - ParamPrefixEnum.ENDS_BEFORE -> "? >= index_to" to listOf(value.value.time) + ParamPrefixEnum.STARTS_AFTER -> ConditionParam("index_from >= ?", listOf(value.getTsHigh() + 1)) + ParamPrefixEnum.ENDS_BEFORE -> ConditionParam("? >= index_to", listOf(value.value.time)) ParamPrefixEnum.NOT_EQUAL -> - "index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ?" to - listOf( - value.value.time, - value.precision.add(value.value, 1).time - 1, - value.value.time, - value.precision.add(value.value, 1).time - 1 - ) + ConditionParam( + "index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ?", + listOf(value.value.time, value.getTsHigh(), value.value.time, value.getTsHigh()) + ) ParamPrefixEnum.EQUAL -> - "index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?" to - listOf( - value.value.time, - value.precision.add(value.value, 1).time - 1, - value.value.time, - value.precision.add(value.value, 1).time - 1 - ) - ParamPrefixEnum.GREATERTHAN -> - "? <= index_to" to listOf(value.precision.add(value.value, 1).time) - ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> "? <= index_from" to listOf(value.value.time) - ParamPrefixEnum.LESSTHAN -> "? >= index_from" to listOf(value.value.time) + ConditionParam( + "index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?", + listOf(value.value.time, value.getTsHigh(), value.value.time, value.getTsHigh()) + ) + ParamPrefixEnum.GREATERTHAN -> ConditionParam("index_to >= ?", listOf(value.getTsHigh() + 1)) + ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> + ConditionParam("index_from >= ?", listOf(value.value.time)) + ParamPrefixEnum.LESSTHAN -> ConditionParam("index_from <= ?", listOf(value.value.time)) ParamPrefixEnum.LESSTHAN_OR_EQUALS -> - "? >= index_to" to listOf(value.precision.add(value.value, 1).time) + ConditionParam("index_to <= ?", listOf(value.precision.add(value.value, 1).time)) } } /** @@ -222,7 +215,7 @@ private fun getConditionParamPair( private fun getConditionParamPair( prefix: ParamPrefixEnum?, value: BigDecimal -): Pair> { +): ConditionParam { // Ends_Before and Starts_After are not used with integer values. see // https://www.hl7.org/fhir/search.html#prefix require( @@ -232,29 +225,37 @@ private fun getConditionParamPair( return when (prefix) { ParamPrefixEnum.EQUAL, null -> { val precision = value.getRange() - "index_value >= ? AND index_value < ?" to + ConditionParam( + "index_value >= ? AND index_value < ?", listOf((value - precision).toDouble(), (value + precision).toDouble()) + ) } - ParamPrefixEnum.GREATERTHAN -> "index_value > ?" to listOf(value.toDouble()) - ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> "index_value >= ?" to listOf(value.toDouble()) - ParamPrefixEnum.LESSTHAN -> "index_value < ?" to listOf(value.toDouble()) - ParamPrefixEnum.LESSTHAN_OR_EQUALS -> "index_value <= ?" to listOf(value.toDouble()) + ParamPrefixEnum.GREATERTHAN -> ConditionParam("index_value > ?", listOf(value.toDouble())) + ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> + ConditionParam("index_value >= ?", listOf(value.toDouble())) + ParamPrefixEnum.LESSTHAN -> ConditionParam("index_value < ?", listOf(value.toDouble())) + ParamPrefixEnum.LESSTHAN_OR_EQUALS -> + ConditionParam("index_value <= ?", listOf(value.toDouble())) ParamPrefixEnum.NOT_EQUAL -> { val precision = value.getRange() - "index_value < ? OR index_value >= ?" to + ConditionParam( + "index_value < ? OR index_value >= ?", listOf((value - precision).toDouble(), (value + precision).toDouble()) + ) } ParamPrefixEnum.ENDS_BEFORE -> { - "index_value < ?" to listOf(value.toDouble()) + ConditionParam("index_value < ?", listOf(value.toDouble())) } ParamPrefixEnum.STARTS_AFTER -> { - "index_value > ?" to listOf(value.toDouble()) + ConditionParam("index_value > ?", listOf(value.toDouble())) } // Approximate to a 10% range see https://www.hl7.org/fhir/search.html#prefix ParamPrefixEnum.APPROXIMATE -> { val range = value.divide(BigDecimal(10)) - "index_value >= ? AND index_value <= ?" to + ConditionParam( + "index_value >= ? AND index_value <= ?", listOf((value - range).toDouble(), (value + range).toDouble()) + ) } } } @@ -276,3 +277,14 @@ private fun BigDecimal.getRange(): BigDecimal { BigDecimal(5) } } + +/** + * Returns the upper bound of the range of the Date's epoch Timestamp. The value is related to the + * precision of the DateTimeType + * + * For example 2001-01-01 includes all values on the given day and thus this functions will return + * 978373799 ( which is one second less than the epoch of 2001-01-02) + */ +private fun DateTimeType.getTsHigh(): Long = precision.add(value, 1).time - 1 + +private data class ConditionParam(val condition: String, val params: List) diff --git a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt index cf5912c10f..042a9a908b 100644 --- a/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt +++ b/engine/src/main/java/com/google/android/fhir/search/SearchDsl.kt @@ -60,6 +60,7 @@ data class Search(val type: ResourceType, var count: Int? = null, var from: Int? filter.init() dateFilter.add(filter) } + fun filter(filter: TokenClientParam, coding: Coding) = tokenFilters.add(TokenFilter(parameter = filter, uri = coding.system, code = coding.code)) 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 ded14b81e0..41dec0d7b6 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 @@ -558,7 +558,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ? <= index_from + WHERE resourceType = ? AND index_name = ? AND index_from >= ? ) """.trimIndent() ) @@ -708,7 +708,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ? <= index_from + WHERE resourceType = ? AND index_name = ? AND index_to >= ? ) """.trimIndent() ) @@ -744,7 +744,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ? <= index_from + WHERE resourceType = ? AND index_name = ? AND index_from >= ? ) """.trimIndent() ) @@ -780,7 +780,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ? >= index_to + WHERE resourceType = ? AND index_name = ? AND index_from <= ? ) """.trimIndent() ) @@ -816,7 +816,7 @@ class SearchTest { WHERE a.resourceType = ? AND a.resourceId IN ( SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ? >= index_to + WHERE resourceType = ? AND index_name = ? AND index_to <= ? ) """.trimIndent() ) From cda990a9f743ebe0486f1dc1f45f3398d4fe0af2 Mon Sep 17 00:00:00 2001 From: epicadk Date: Thu, 17 Jun 2021 15:14:31 +0530 Subject: [PATCH 22/25] address review comments --- .../google/android/fhir/search/MoreSearch.kt | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) 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 b80293cc88..d4f5ca5f06 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 @@ -186,28 +186,34 @@ private fun getConditionParamPair( prefix: ParamPrefixEnum, value: DateTimeType ): ConditionParam { + val (start, end) = value.rangeEpochMillis return when (prefix) { ParamPrefixEnum.APPROXIMATE -> TODO("Not Implemented") - ParamPrefixEnum.STARTS_AFTER -> ConditionParam("index_from >= ?", listOf(value.getTsHigh() + 1)) - ParamPrefixEnum.ENDS_BEFORE -> ConditionParam("? >= index_to", listOf(value.value.time)) + ParamPrefixEnum.STARTS_AFTER -> ConditionParam("index_from >= ?", end + 1) + ParamPrefixEnum.ENDS_BEFORE -> ConditionParam("? >= index_to", start) ParamPrefixEnum.NOT_EQUAL -> ConditionParam( "index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ?", - listOf(value.value.time, value.getTsHigh(), value.value.time, value.getTsHigh()) + start, + end, + start, + end ) ParamPrefixEnum.EQUAL -> ConditionParam( "index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ?", - listOf(value.value.time, value.getTsHigh(), value.value.time, value.getTsHigh()) + start, + end, + start, + end ) - ParamPrefixEnum.GREATERTHAN -> ConditionParam("index_to >= ?", listOf(value.getTsHigh() + 1)) - ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> - ConditionParam("index_from >= ?", listOf(value.value.time)) - ParamPrefixEnum.LESSTHAN -> ConditionParam("index_from <= ?", listOf(value.value.time)) - ParamPrefixEnum.LESSTHAN_OR_EQUALS -> - ConditionParam("index_to <= ?", listOf(value.precision.add(value.value, 1).time)) + ParamPrefixEnum.GREATERTHAN -> ConditionParam("index_to >= ?", end + 1) + ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> ConditionParam("index_from >= ?", start) + ParamPrefixEnum.LESSTHAN -> ConditionParam("index_from <= ?", start) + ParamPrefixEnum.LESSTHAN_OR_EQUALS -> ConditionParam("index_to <= ?", end + 1) } } + /** * Returns the condition and list of params required in NumberFilter.query see * https://www.hl7.org/fhir/search.html#number. @@ -279,12 +285,16 @@ private fun BigDecimal.getRange(): BigDecimal { } /** - * Returns the upper bound of the range of the Date's epoch Timestamp. The value is related to the - * precision of the DateTimeType + * The range of the range of the Date's epoch Timestamp. The value is related to the precision of + * the DateTimeType * * For example 2001-01-01 includes all values on the given day and thus this functions will return - * 978373799 ( which is one second less than the epoch of 2001-01-02) + * 978307200 (epoch timestamp of 2001-01-01) and 978393599 ( which is one second less than the epoch + * of 2001-01-02) */ -private fun DateTimeType.getTsHigh(): Long = precision.add(value, 1).time - 1 +private val DateTimeType.rangeEpochMillis + get() = value.time to precision.add(value, 1).time - 1 -private data class ConditionParam(val condition: String, val params: List) +private data class ConditionParam(val condition: String, val params: List) { + constructor(condition: String, vararg params: T) : this(condition, params.asList()) +} From 63bc9e8e9b52533249d9790714de0ac972da55a5 Mon Sep 17 00:00:00 2001 From: epicadk Date: Tue, 22 Jun 2021 00:13:27 +0530 Subject: [PATCH 23/25] address review comments --- .../google/android/fhir/search/MoreSearch.kt | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) 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 d4f5ca5f06..7a577b9e1d 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 @@ -233,34 +233,35 @@ private fun getConditionParamPair( val precision = value.getRange() ConditionParam( "index_value >= ? AND index_value < ?", - listOf((value - precision).toDouble(), (value + precision).toDouble()) + (value - precision).toDouble(), + (value + precision).toDouble() ) } - ParamPrefixEnum.GREATERTHAN -> ConditionParam("index_value > ?", listOf(value.toDouble())) - ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> - ConditionParam("index_value >= ?", listOf(value.toDouble())) - ParamPrefixEnum.LESSTHAN -> ConditionParam("index_value < ?", listOf(value.toDouble())) - ParamPrefixEnum.LESSTHAN_OR_EQUALS -> - ConditionParam("index_value <= ?", listOf(value.toDouble())) + ParamPrefixEnum.GREATERTHAN -> ConditionParam("index_value > ?", value.toDouble()) + ParamPrefixEnum.GREATERTHAN_OR_EQUALS -> ConditionParam("index_value >= ?", value.toDouble()) + ParamPrefixEnum.LESSTHAN -> ConditionParam("index_value < ?", value.toDouble()) + ParamPrefixEnum.LESSTHAN_OR_EQUALS -> ConditionParam("index_value <= ?", value.toDouble()) ParamPrefixEnum.NOT_EQUAL -> { val precision = value.getRange() ConditionParam( "index_value < ? OR index_value >= ?", - listOf((value - precision).toDouble(), (value + precision).toDouble()) + (value - precision).toDouble(), + (value + precision).toDouble() ) } ParamPrefixEnum.ENDS_BEFORE -> { - ConditionParam("index_value < ?", listOf(value.toDouble())) + ConditionParam("index_value < ?", value.toDouble()) } ParamPrefixEnum.STARTS_AFTER -> { - ConditionParam("index_value > ?", listOf(value.toDouble())) + ConditionParam("index_value > ?", value.toDouble()) } // Approximate to a 10% range see https://www.hl7.org/fhir/search.html#prefix ParamPrefixEnum.APPROXIMATE -> { val range = value.divide(BigDecimal(10)) ConditionParam( "index_value >= ? AND index_value <= ?", - listOf((value - range).toDouble(), (value + range).toDouble()) + (value - range).toDouble(), + (value + range).toDouble() ) } } From cf68cf07fa374d4ebeaf30c8ae90803472cb6c52 Mon Sep 17 00:00:00 2001 From: epicadk Date: Tue, 22 Jun 2021 00:23:40 +0530 Subject: [PATCH 24/25] address review comments --- .../google/android/fhir/search/SearchTest.kt | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 deletions(-) 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 41dec0d7b6..91947e214e 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 @@ -553,13 +553,13 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND index_from >= ? - ) + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND index_from >= ? + ) """.trimIndent() ) @@ -589,13 +589,13 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND ? >= index_to - ) + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND ? >= index_to + ) """.trimIndent() ) @@ -625,13 +625,13 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ? - ) + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND index_from NOT BETWEEN ? AND ? OR index_to NOT BETWEEN ? AND ? + ) """.trimIndent() ) @@ -664,13 +664,13 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ? - ) + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND index_from BETWEEN ? AND ? AND index_to BETWEEN ? AND ? + ) """.trimIndent() ) @@ -703,13 +703,13 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND index_to >= ? - ) + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND index_to >= ? + ) """.trimIndent() ) @@ -739,13 +739,13 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND index_from >= ? - ) + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND index_from >= ? + ) """.trimIndent() ) @@ -775,13 +775,13 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND index_from <= ? - ) + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND index_from <= ? + ) """.trimIndent() ) @@ -811,13 +811,13 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM DateIndexEntity - WHERE resourceType = ? AND index_name = ? AND index_to <= ? - ) + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM DateIndexEntity + WHERE resourceType = ? AND index_name = ? AND index_to <= ? + ) """.trimIndent() ) @@ -880,12 +880,12 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - LEFT JOIN NumberIndexEntity b - ON a.resourceType = b.resourceType AND a.resourceId = b.resourceId AND b.index_name = ? - WHERE a.resourceType = ? - ORDER BY b.index_value ASC + SELECT a.serializedResource + FROM ResourceEntity a + LEFT JOIN NumberIndexEntity b + ON a.resourceType = b.resourceType AND a.resourceId = b.resourceId AND b.index_name = ? + WHERE a.resourceType = ? + ORDER BY b.index_value ASC """.trimIndent() ) } @@ -954,13 +954,13 @@ class SearchTest { assertThat(query.query) .isEqualTo( """ - SELECT a.serializedResource - FROM ResourceEntity a - WHERE a.resourceType = ? - AND a.resourceId IN ( - SELECT resourceId FROM NumberIndexEntity - WHERE resourceType = ? AND index_name = ? AND index_value >= ? AND index_value < ? - ) + SELECT a.serializedResource + FROM ResourceEntity a + WHERE a.resourceType = ? + AND a.resourceId IN ( + SELECT resourceId FROM NumberIndexEntity + WHERE resourceType = ? AND index_name = ? AND index_value >= ? AND index_value < ? + ) """.trimIndent() ) From 1c6e4a350dd1a85fa763fcbf6ebe2d6cdd90488c Mon Sep 17 00:00:00 2001 From: epicadk Date: Tue, 22 Jun 2021 00:31:12 +0530 Subject: [PATCH 25/25] address review comments --- .../src/main/java/com/google/android/fhir/search/MoreSearch.kt | 2 ++ 1 file changed, 2 insertions(+) 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 7a577b9e1d..b2c73180c7 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 @@ -189,6 +189,8 @@ private fun getConditionParamPair( val (start, end) = value.rangeEpochMillis return when (prefix) { ParamPrefixEnum.APPROXIMATE -> TODO("Not Implemented") + // see https://github.com/google/android-fhir/issues/568 + // https://www.hl7.org/fhir/search.html#prefix ParamPrefixEnum.STARTS_AFTER -> ConditionParam("index_from >= ?", end + 1) ParamPrefixEnum.ENDS_BEFORE -> ConditionParam("? >= index_to", start) ParamPrefixEnum.NOT_EQUAL ->