Improve Period range indexing (#1873)

* Fix #1871 - Handle period range searches better

* Add changelog

* FIx tests

* Add test logging

* Address review comments
This commit is contained in:
James Agnew 2020-05-28 08:56:43 -04:00 committed by GitHub
parent 97a1bd40a1
commit 7f72305f57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 853 additions and 427 deletions

View File

@ -0,0 +1,7 @@
---
type: add
issue: 1871
title: "In the JPA server, when indexing Date SearchParameters where the value being indexed is a
FHIR Period that is missing either a lower bound or an upper bound, a default value representing an
extreme 'beginning of time' or 'end of time' is now used. This allows range searches to return more
accurate results."

View File

@ -522,3 +522,72 @@ The following columns are common to **all HFJ_SPIDX_xxx tables**.
</tbody> </tbody>
</table> </table>
# HFJ_SPIDX_DATE: Date Search Parameters
For any FHIR Search Parameter of type *date* that generates a database index, a row in the *HFJ_SPIDX_DATE* table will be created.
## Columns
The following columns are common to **all HFJ_SPIDX_xxx tables**.
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Relationships</th>
<th>Datatype</th>
<th>Nullable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>SP_VALUE_LOW</td>
<td></td>
<td>Timestamp</td>
<td>Nullable</td>
<td>
This is the lower bound of the date in question.
<ul>
<li>For a point in time date to millisecond precision (such as an Instant with a value of <code>2020-05-26T15:00:00.000</code>) this represents the exact value.</li>
<li>For an instant value with lower precision, this represents the start of the possible range denoted by the value. For example, for a value of <code>2020-05-26</code> this represents <code>2020-05-26T00:00:00.000</code>.</li>
<li>For a Period with a lower (start) value present, this column contains that value.</li>
<li>For a Period with no lower (start) value present, this column contains a timestamp representing the "start of time".</li>
</ul>
</td>
</tr>
<tr>
<td>SP_VALUE_HIGH</td>
<td></td>
<td>Timestamp</td>
<td>Nullable</td>
<td>
This is the upper bound of the date in question.
<ul>
<li>For a point in time date to millisecond precision (such as an Instant with a value of <code>2020-05-26T15:00:00.000</code>) this represents the exact value.</li>
<li>For an instant value with lower precision, this represents the start of the possible range denoted by the value. For example, for a value of <code>2020-05-26</code> this represents <code>2020-05-26T23:59:59.999</code>.</li>
<li>For a Period with an upper (end) value present, this column contains that value.</li>
<li>For a Period with no upper (end) value present, this column contains a timestamp representing the "end of time".</li>
</ul>
</td>
</tr>
<tr>
<td>SP_VALUE_LOW_DATE_ORDINAL</td>
<td></td>
<td>Integer</td>
<td>Nullable</td>
<td>
This column contains the same Timestamp as <code>SP_VALUE_LOW</code>, but truncated to Date precision and formatted as an integer in the format "YYYYMMDD".
</td>
</tr>
<tr>
<td>SP_VALUE_HIGH_DATE_ORDINAL</td>
<td></td>
<td>Integer</td>
<td>Nullable</td>
<td>
This column contains the same Timestamp as <code>SP_VALUE_HIGH</code>, but truncated to Date precision and formatted as an integer in the format "YYYYMMDD".
</td>
</tr>
</tbody>
</table>

View File

@ -432,6 +432,145 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
} }
@Test
public void testDatePeriodParamEndOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params;
List<Encounter> encs;
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
// encs = toList(ourEncounterDao.search(params));
// assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartAndEnd() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("03");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-03");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-02", "2001-01-06"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-05"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-05", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("01");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
}
@Test @Test
public void testSearchCompositeParam() { public void testSearchCompositeParam() {
Observation o1 = new Observation(); Observation o1 = new Observation();

View File

@ -584,143 +584,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
} }
} }
@Test
public void testDatePeriodParamEndOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params;
List<Encounter> encs;
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
// encs = toList(ourEncounterDao.search(params));
// assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartAndEnd() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("03");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-03");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-02", "2001-01-06"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-05"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-05", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("01");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new IdentifierDt("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test @Test
public void testDeleteFailsIfIncomingLinks() { public void testDeleteFailsIfIncomingLinks() {

View File

@ -959,6 +959,145 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
} }
@Test
public void testDatePeriodParamEndOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params;
List<Encounter> encs;
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
// encs = toList(ourEncounterDao.search(params));
// assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartAndEnd() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("03");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-03");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-02", "2001-01-06"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-05"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-05", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("01");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
}
/** /**
* #222 * #222
*/ */

View File

@ -870,143 +870,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
} }
} }
@Test
public void testDatePeriodParamEndOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params;
List<Encounter> encs;
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
// encs = toList(ourEncounterDao.search(params));
// assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartAndEnd() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("03");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-03");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-02", "2001-01-06"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-05"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-05", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("01");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test @Test
public void testDeleteFailsIfIncomingLinks() { public void testDeleteFailsIfIncomingLinks() {

View File

@ -530,7 +530,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
map = new SearchParameterMap(); map = new SearchParameterMap();
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
map.add(DiagnosticReport.SP_PERFORMER, new ReferenceParam( "CareTeam").setChain(PARAM_TYPE)); map.add(DiagnosticReport.SP_PERFORMER, new ReferenceParam("CareTeam").setChain(PARAM_TYPE));
results = myDiagnosticReportDao.search(map); results = myDiagnosticReportDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results); ids = toUnqualifiedVersionlessIdValues(results);
assertThat(ids.toString(), ids, contains(drId1.getValue())); assertThat(ids.toString(), ids, contains(drId1.getValue()));
@ -1690,6 +1690,338 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
} }
@Test
public void testDateRangeOnPeriod_SearchByDateTime_NoUpperBound() {
Encounter enc = new Encounter();
enc.getPeriod().getStartElement().setValueAsString("2020-05-26T12:00:00Z");
String id1 = myEncounterDao.create(enc).getId().toUnqualifiedVersionless().getValue();
runInTransaction(() -> {
ourLog.info("Date indexes:\n * {}", myResourceIndexedSearchParamDateDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
// ge -> above the lower bound
SearchParameterMap map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("ge2020-05-26T13:00:00Z"));
myCaptureQueriesListener.clear();
IBundleProvider results = myEncounterDao.search(map);
List<String> ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// ge -> Below the lower bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("ge2020-05-26T11:00:00Z"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// le -> above the lower bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("le2020-05-26T13:00:00Z"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// le -> Below the lower bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("le2020-05-26T11:00:00Z"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, empty());
}
@Test
public void testDateRangeOnPeriod_SearchByDate_NoUpperBound() {
Encounter enc = new Encounter();
enc.getPeriod().getStartElement().setValueAsString("2020-05-26T12:00:00Z");
String id1 = myEncounterDao.create(enc).getId().toUnqualifiedVersionless().getValue();
runInTransaction(() -> {
ourLog.info("Date indexes:\n * {}", myResourceIndexedSearchParamDateDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
// ge -> above the lower bound
SearchParameterMap map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("ge2020-05-27"));
myCaptureQueriesListener.clear();
IBundleProvider results = myEncounterDao.search(map);
List<String> ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// ge -> Below the lower bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("ge2020-05-25"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// le -> above the lower bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("le2020-05-27"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// le -> Below the lower bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("le2020-05-25"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, empty());
}
@Test
public void testDateRangeOnPeriod_SearchByDateTime_NoLowerBound() {
Encounter enc = new Encounter();
enc.getPeriod().getEndElement().setValueAsString("2020-05-26T12:00:00Z");
String id1 = myEncounterDao.create(enc).getId().toUnqualifiedVersionless().getValue();
runInTransaction(() -> {
ourLog.info("Date indexes:\n * {}", myResourceIndexedSearchParamDateDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
// le -> above the upper bound
SearchParameterMap map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("le2020-05-26T13:00:00Z"));
myCaptureQueriesListener.clear();
IBundleProvider results = myEncounterDao.search(map);
List<String> ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// le -> Below the upper bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("le2020-05-26T11:00:00Z"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// ge -> above the upper bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("ge2020-05-26T13:00:00Z"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, empty());
// ge -> Below the upper bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("ge2020-05-26T11:00:00Z"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
}
@Test
public void testDateRangeOnPeriod_SearchByDate_NoLowerBound() {
Encounter enc = new Encounter();
enc.getPeriod().getEndElement().setValueAsString("2020-05-26T12:00:00Z");
String id1 = myEncounterDao.create(enc).getId().toUnqualifiedVersionless().getValue();
runInTransaction(() -> {
ourLog.info("Date indexes:\n * {}", myResourceIndexedSearchParamDateDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
// le -> above the upper bound
SearchParameterMap map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("le2020-05-27"));
myCaptureQueriesListener.clear();
IBundleProvider results = myEncounterDao.search(map);
List<String> ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// le -> Below the upper bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("le2020-05-25"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
// ge -> above the upper bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("ge2020-05-27"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, empty());
// ge -> Below the upper bound
map = SearchParameterMap.newSynchronous();
map.add(Encounter.SP_DATE, new DateParam("ge2020-05-25"));
myCaptureQueriesListener.clear();
results = myEncounterDao.search(map);
ids = toUnqualifiedVersionlessIdValues(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, contains(id1));
}
@Test
public void testDatePeriodParamEndOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params;
List<Encounter> encs;
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartAndEnd() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("03");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-03");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-02", "2001-01-06"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-05"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-05", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("01");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
}
/** /**
* See #1174 * See #1174
*/ */

View File

@ -311,7 +311,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
* 20 should be prefetched since that's the initial page size * 20 should be prefetched since that's the initial page size
*/ */
await().until(()-> runInTransaction(()->{ await().until(() -> runInTransaction(() -> {
Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")); Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException(""));
return search.getNumFound() >= 200; return search.getNumFound() >= 200;
})); }));
@ -371,8 +371,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
* 20 should be prefetched since that's the initial page size * 20 should be prefetched since that's the initial page size
*/ */
await().until(()->{ await().until(() -> {
return runInTransaction(()->{ return runInTransaction(() -> {
return mySearchEntityDao return mySearchEntityDao
.findByUuidAndFetchIncludes(uuid) .findByUuidAndFetchIncludes(uuid)
.orElseThrow(() -> new InternalErrorException("")) .orElseThrow(() -> new InternalErrorException(""))
@ -507,8 +507,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
* 20 should be prefetched since that's the initial page size * 20 should be prefetched since that's the initial page size
*/ */
await().until(()->{ await().until(() -> {
return runInTransaction(()->{ return runInTransaction(() -> {
Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")); Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException(""));
return search.getNumFound() >= 50; return search.getNumFound() >= 50;
}); });
@ -547,8 +547,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
/* /*
* 20 should be prefetched since that's the initial page size * 20 should be prefetched since that's the initial page size
*/ */
await().until(()->{ await().until(() -> {
return runInTransaction(()->{ return runInTransaction(() -> {
Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")); Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException(""));
return search.getNumFound() == 20; return search.getNumFound() == 20;
}); });
@ -611,7 +611,12 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
* 20 should be prefetched since that's the initial page size * 20 should be prefetched since that's the initial page size
*/ */
waitForSize(20, () -> runInTransaction(() -> mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")).getNumFound())); waitForSize(
20,
10000,
() -> runInTransaction(() -> mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")).getNumFound()),
() -> "Wanted 20: " + runInTransaction(() -> mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")).toString()));
runInTransaction(() -> { runInTransaction(() -> {
Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")); Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException(""));
assertEquals(20, search.getNumFound()); assertEquals(20, search.getNumFound());
@ -673,7 +678,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
assertEquals("Patient/PT00000", ids.get(0)); assertEquals("Patient/PT00000", ids.get(0));
assertEquals(1, ids.size()); assertEquals(1, ids.size());
await().until(()-> runInTransaction(()-> mySearchEntityDao await().until(() -> runInTransaction(() -> mySearchEntityDao
.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")) .findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException(""))
.getStatus() == SearchStatusEnum.FINISHED)); .getStatus() == SearchStatusEnum.FINISHED));
@ -821,7 +826,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
search.getResources(0, 20); search.getResources(0, 20);
ourLog.info("** Done retrieving resources"); ourLog.info("** Done retrieving resources");
await().until(()->myCaptureQueriesListener.countSelectQueries() == 4); await().until(() -> myCaptureQueriesListener.countSelectQueries() == 4);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(4, myCaptureQueriesListener.countSelectQueries()); assertEquals(4, myCaptureQueriesListener.countSelectQueries());

View File

@ -1118,143 +1118,6 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
} }
} }
@Test
public void testDatePeriodParamEndOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params;
List<Encounter> encs;
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
// encs = toList(ourEncounterDao.search(params));
// assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "02"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartAndEnd() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("03");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
enc.getPeriod().getEndElement().setValueAsString("2001-01-03");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-02", "2001-01-06"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-05"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-05", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "03"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test
public void testDatePeriodParamStartOnly() {
{
Encounter enc = new Encounter();
enc.addIdentifier().setSystem("testDatePeriodParam").setValue("01");
enc.getPeriod().getStartElement().setValueAsString("2001-01-02");
myEncounterDao.create(enc, mySrd);
}
SearchParameterMap params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
List<Encounter> encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-01", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-03"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(1, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam(null, "2001-01-01"));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
params = new SearchParameterMap();
params.add(Encounter.SP_DATE, new DateRangeParam("2001-01-03", null));
params.add(Encounter.SP_IDENTIFIER, new TokenParam("testDatePeriodParam", "01"));
encs = toList(myEncounterDao.search(params));
assertEquals(0, encs.size());
}
@Test @Test
public void testDeleteFailsIfIncomingLinks() { public void testDeleteFailsIfIncomingLinks() {

View File

@ -20,13 +20,17 @@ package ca.uhn.fhir.jpa.model.entity;
* #L% * #L%
*/ */
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu2.model.Subscription; import org.hl7.fhir.dstu2.model.Subscription;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.DateTimeType;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -50,6 +54,21 @@ public class ModelConfig {
"http://hl7.org/fhir/StructureDefinition/*"))); "http://hl7.org/fhir/StructureDefinition/*")));
public static final String DEFAULT_WEBSOCKET_CONTEXT_PATH = "/websocket"; public static final String DEFAULT_WEBSOCKET_CONTEXT_PATH = "/websocket";
/*
* <p>
* Note the following database documented limitations:
* <ul>
* <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
* <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
* <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
* <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
* <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
* </ul>
* </p>
*/
protected static final String DEFAULT_PERIOD_INDEX_START_OF_TIME = "1001-01-01";
protected static final String DEFAULT_PERIOD_INDEX_END_OF_TIME = "9000-01-01";
/** /**
* update setter javadoc if default changes * update setter javadoc if default changes
*/ */
@ -67,11 +86,15 @@ public class ModelConfig {
private boolean myUseOrdinalDatesForDayPrecisionSearches = true; private boolean myUseOrdinalDatesForDayPrecisionSearches = true;
private boolean mySuppressStringIndexingInTokens = false; private boolean mySuppressStringIndexingInTokens = false;
private IPrimitiveType<Date> myPeriodIndexStartOfTime;
private IPrimitiveType<Date> myPeriodIndexEndOfTime;
/** /**
* Constructor * Constructor
*/ */
public ModelConfig() { public ModelConfig() {
super(); setPeriodIndexStartOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_START_OF_TIME));
setPeriodIndexEndOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_END_OF_TIME));
} }
/** /**
@ -373,8 +396,8 @@ public class ModelConfig {
/** /**
* <p> * <p>
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in * Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using * {@link ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}. * precision of {@link TemporalPrecisionEnum#DAY}.
* <p> * <p>
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an * For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
* integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()} * integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
@ -392,8 +415,8 @@ public class ModelConfig {
/** /**
* <p> * <p>
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in * Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using * {@link ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}. * precision of {@link TemporalPrecisionEnum#DAY}.
* <p> * <p>
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an * For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
* ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()} * ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
@ -417,6 +440,7 @@ public class ModelConfig {
* <li>Coding.display</li> * <li>Coding.display</li>
* <li>Identifier.use.text</li> * <li>Identifier.use.text</li>
* </ul> * </ul>
*
* @since 5.0.0 * @since 5.0.0
*/ */
public boolean isSuppressStringIndexingInTokens() { public boolean isSuppressStringIndexingInTokens() {
@ -432,12 +456,124 @@ public class ModelConfig {
* <li>Coding.display</li> * <li>Coding.display</li>
* <li>Identifier.use.text</li> * <li>Identifier.use.text</li>
* </ul> * </ul>
*
* @since 5.0.0 * @since 5.0.0
*/ */
public void setSuppressStringIndexingInTokens(boolean theSuppressStringIndexingInTokens) { public void setSuppressStringIndexingInTokens(boolean theSuppressStringIndexingInTokens) {
mySuppressStringIndexingInTokens = theSuppressStringIndexingInTokens; mySuppressStringIndexingInTokens = theSuppressStringIndexingInTokens;
} }
/**
* When indexing a Period (e.g. Encounter.period) where the period has an upper bound
* but not a lower bound, a canned "start of time" value can be used as the lower bound
* in order to allow range searches to correctly identify all values in the range.
* <p>
* The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
* is probably good enough for almost any application, but this can be changed if
* needed.
* </p>
* <p>
* Note the following database documented limitations:
* <ul>
* <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
* <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
* <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
* <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
* <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
* </ul>
* </p>
*
* @see #getPeriodIndexEndOfTime()
* @since 5.1.0
*/
public IPrimitiveType<Date> getPeriodIndexStartOfTime() {
return myPeriodIndexStartOfTime;
}
/**
* When indexing a Period (e.g. Encounter.period) where the period has an upper bound
* but not a lower bound, a canned "start of time" value can be used as the lower bound
* in order to allow range searches to correctly identify all values in the range.
* <p>
* The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
* is probably good enough for almost any application, but this can be changed if
* needed.
* </p>
* <p>
* Note the following database documented limitations:
* <ul>
* <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
* <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
* <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
* <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
* <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
* </ul>
* </p>
*
* @see #getPeriodIndexEndOfTime()
* @since 5.1.0
*/
public void setPeriodIndexStartOfTime(IPrimitiveType<Date> thePeriodIndexStartOfTime) {
Validate.notNull(thePeriodIndexStartOfTime, "thePeriodIndexStartOfTime must not be null");
myPeriodIndexStartOfTime = thePeriodIndexStartOfTime;
}
/**
* When indexing a Period (e.g. Encounter.period) where the period has a lower bound
* but not an upper bound, a canned "end of time" value can be used as the upper bound
* in order to allow range searches to correctly identify all values in the range.
* <p>
* The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
* is probably good enough for almost any application, but this can be changed if
* needed.
* </p>
* <p>
* Note the following database documented limitations:
* <ul>
* <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
* <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
* <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
* <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
* <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
* </ul>
* </p>
*
* @see #getPeriodIndexStartOfTime()
* @since 5.1.0
*/
public IPrimitiveType<Date> getPeriodIndexEndOfTime() {
return myPeriodIndexEndOfTime;
}
/**
* When indexing a Period (e.g. Encounter.period) where the period has an upper bound
* but not a lower bound, a canned "start of time" value can be used as the lower bound
* in order to allow range searches to correctly identify all values in the range.
* <p>
* The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
* is probably good enough for almost any application, but this can be changed if
* needed.
* </p>
* <p>
* Note the following database documented limitations:
* <ul>
* <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
* <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
* <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
* <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
* <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
* </ul>
* </p>
*
* @see #getPeriodIndexStartOfTime()
* @since 5.1.0
*/
public void setPeriodIndexEndOfTime(IPrimitiveType<Date> thePeriodIndexEndOfTime) {
Validate.notNull(thePeriodIndexEndOfTime, "thePeriodIndexEndOfTime must not be null");
myPeriodIndexEndOfTime = thePeriodIndexEndOfTime;
}
private static void validateTreatBaseUrlsAsLocal(String theUrl) { private static void validateTreatBaseUrlsAsLocal(String theUrl) {
Validate.notBlank(theUrl, "Base URL must not be null or empty"); Validate.notBlank(theUrl, "Base URL must not be null or empty");

View File

@ -677,6 +677,16 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String endAsString = extractValueAsString(myPeriodEndValueChild, theValue); String endAsString = extractValueAsString(myPeriodEndValueChild, theValue);
if (start != null || end != null) { if (start != null || end != null) {
if (start == null) {
start = myModelConfig.getPeriodIndexStartOfTime().getValue();
startAsString = myModelConfig.getPeriodIndexStartOfTime().getValueAsString();
}
if (end == null) {
end = myModelConfig.getPeriodIndexEndOfTime().getValue();
endAsString = myModelConfig.getPeriodIndexEndOfTime().getValueAsString();
}
ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(myPartitionSettings, theResourceType, theSearchParam.getName(), start, startAsString, end, endAsString, startAsString); ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(myPartitionSettings, theResourceType, theSearchParam.getName(), start, startAsString, end, endAsString, startAsString);
theParams.add(nextEntity); theParams.add(nextEntity);
} }