diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java index 67628a122a1..26b673168db 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java @@ -99,7 +99,7 @@ public class DaoConfig { private int myMaximumExpansionSize = 5000; private int myMaximumSearchResultCountInTransaction = DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION; private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC; - private Long myReuseCachedSearchResultsForMillis; + private Long myReuseCachedSearchResultsForMillis = DEFAULT_REUSE_CACHED_SEARCH_RESULTS_FOR_MILLIS; private boolean mySchedulingDisabled; private boolean mySubscriptionEnabled; private long mySubscriptionPollDelay = 1000; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index c5e51cc3533..525737a1969 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -334,117 +334,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { - @Test - public void testSearchReusesResultsEnabled() throws Exception { - List resources = new ArrayList(); - for (int i = 0; i < 50; i++) { - Organization org = new Organization(); - org.setName("HELLO"); - resources.add(org); - } - ourClient.transaction().withResources(resources).prettyPrint().encodedXml().execute(); - - myDaoConfig.setReuseCachedSearchResultsForMillis(1000L); - - Bundle result1 = ourClient - .search() - .forResource("Organization") - .where(Organization.NAME.matches().value("HELLO")) - .count(5) - .returnBundle(Bundle.class) - .execute(); - - final String uuid1 = toSearchUuidFromLinkNext(result1); - Search search1 = newTxTemplate().execute(new TransactionCallback() { - @Override - public Search doInTransaction(TransactionStatus theStatus) { - return mySearchEntityDao.findByUuid(uuid1); - } - }); - Date lastReturned1 = search1.getSearchLastReturned(); - - Bundle result2 = ourClient - .search() - .forResource("Organization") - .where(Organization.NAME.matches().value("HELLO")) - .count(5) - .returnBundle(Bundle.class) - .execute(); - - final String uuid2 = toSearchUuidFromLinkNext(result2); - Search search2 = newTxTemplate().execute(new TransactionCallback() { - @Override - public Search doInTransaction(TransactionStatus theStatus) { - return mySearchEntityDao.findByUuid(uuid2); - } - }); - Date lastReturned2 = search2.getSearchLastReturned(); - - assertTrue(lastReturned2.getTime() > lastReturned1.getTime()); - - Thread.sleep(1500); - - Bundle result3 = ourClient - .search() - .forResource("Organization") - .where(Organization.NAME.matches().value("HELLO")) - .count(5) - .returnBundle(Bundle.class) - .execute(); - - String uuid3 = toSearchUuidFromLinkNext(result3); - - assertEquals(uuid1, uuid2); - assertNotEquals(uuid1, uuid3); - } - - @Test - public void testSearchReusesResultsDisabled() throws Exception { - List resources = new ArrayList(); - for (int i = 0; i < 50; i++) { - Organization org = new Organization(); - org.setName("HELLO"); - resources.add(org); - } - ourClient.transaction().withResources(resources).prettyPrint().encodedXml().execute(); - - myDaoConfig.setReuseCachedSearchResultsForMillis(null); - - Bundle result1 = ourClient - .search() - .forResource("Organization") - .where(Organization.NAME.matches().value("HELLO")) - .count(5) - .returnBundle(Bundle.class) - .execute(); - - final String uuid1 = toSearchUuidFromLinkNext(result1); - - Bundle result2 = ourClient - .search() - .forResource("Organization") - .where(Organization.NAME.matches().value("HELLO")) - .count(5) - .returnBundle(Bundle.class) - .execute(); - - final String uuid2 = toSearchUuidFromLinkNext(result2); - - Bundle result3 = ourClient - .search() - .forResource("Organization") - .where(Organization.NAME.matches().value("HELLO")) - .count(5) - .returnBundle(Bundle.class) - .execute(); - - String uuid3 = toSearchUuidFromLinkNext(result3); - - assertNotEquals(uuid1, uuid2); - assertNotEquals(uuid1, uuid3); - } - - /** * See #438 */ @@ -517,6 +406,24 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { Validate.notNull(input); ourClient.create().resource(input).execute().getResource(); } + + + @Test + public void testCreateConditional() { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); + patient.addName().setFamily("Tester").addGiven("Raghad"); + + MethodOutcome output1 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute(); + + patient = new Patient(); + patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); + patient.addName().setFamily("Tester").addGiven("Raghad"); + + MethodOutcome output2 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute(); + + assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart()); + } @Test public void testCreateIncludesRequestValidatorInterceptorOutcome() throws IOException { @@ -548,23 +455,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } } - @Test - public void testCreateConditional() { - Patient patient = new Patient(); - patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); - patient.addName().setFamily("Tester").addGiven("Raghad"); - - MethodOutcome output1 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute(); - - patient = new Patient(); - patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); - patient.addName().setFamily("Tester").addGiven("Raghad"); - - MethodOutcome output2 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute(); - - assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart()); - } - @Test @Ignore public void testCreateQuestionnaireResponseWithValidation() throws IOException { @@ -888,17 +778,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } } - @Test - public void testDeleteReturnsOperationOutcome() { - Patient p = new Patient(); - p.addName().setFamily("FAM"); - IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless(); - - IBaseOperationOutcome resp = ourClient.delete().resourceById(id).execute(); - OperationOutcome oo = (OperationOutcome) resp; - assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s) in ")); - } - @Test public void testDeleteResourceConditional1() throws IOException { String methodName = "testDeleteResourceConditional1"; @@ -1027,24 +906,15 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } - /** - * See issue #52 - */ @Test - public void testProcedureRequestResources() throws Exception { - IGenericClient client = ourClient; - - int initialSize = client.search().forResource(ProcedureRequest.class).returnBundle(Bundle.class).execute().getEntry().size(); - - ProcedureRequest res = new ProcedureRequest(); - res.addIdentifier().setSystem("urn:foo").setValue("123"); - - client.create().resource(res).execute(); - - int newSize = client.search().forResource(ProcedureRequest.class).returnBundle(Bundle.class).execute().getEntry().size(); - - assertEquals(1, newSize - initialSize); + public void testDeleteReturnsOperationOutcome() { + Patient p = new Patient(); + p.addName().setFamily("FAM"); + IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless(); + IBaseOperationOutcome resp = ourClient.delete().resourceById(id).execute(); + OperationOutcome oo = (OperationOutcome) resp; + assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s) in ")); } /** @@ -1285,31 +1155,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } - // private void delete(String theResourceType, String theParamName, String theParamValue) { - // Bundle resources; - // do { - // IQuery forResource = ourClient.search().forResource(theResourceType); - // if (theParamName != null) { - // forResource = forResource.where(new StringClientParam(theParamName).matches().value(theParamValue)); - // } - // resources = forResource.execute(); - // for (IResource next : resources.toListOfResources()) { - // ourLog.info("Deleting resource: {}", next.getId()); - // ourClient.delete().resource(next).execute(); - // } - // } while (resources.size() > 0); - // } - // - // private void deleteToken(String theResourceType, String theParamName, String theParamSystem, String theParamValue) - // { - // Bundle resources = ourClient.search().forResource(theResourceType).where(new - // TokenClientParam(theParamName).exactly().systemAndCode(theParamSystem, theParamValue)).execute(); - // for (IResource next : resources.toListOfResources()) { - // ourLog.info("Deleting resource: {}", next.getId()); - // ourClient.delete().resource(next).execute(); - // } - // } - /** * See #147"Patient" */ @@ -1432,6 +1277,31 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } + // private void delete(String theResourceType, String theParamName, String theParamValue) { + // Bundle resources; + // do { + // IQuery forResource = ourClient.search().forResource(theResourceType); + // if (theParamName != null) { + // forResource = forResource.where(new StringClientParam(theParamName).matches().value(theParamValue)); + // } + // resources = forResource.execute(); + // for (IResource next : resources.toListOfResources()) { + // ourLog.info("Deleting resource: {}", next.getId()); + // ourClient.delete().resource(next).execute(); + // } + // } while (resources.size() > 0); + // } + // + // private void deleteToken(String theResourceType, String theParamName, String theParamSystem, String theParamValue) + // { + // Bundle resources = ourClient.search().forResource(theResourceType).where(new + // TokenClientParam(theParamName).exactly().systemAndCode(theParamSystem, theParamValue)).execute(); + // for (IResource next : resources.toListOfResources()) { + // ourLog.info("Deleting resource: {}", next.getId()); + // ourClient.delete().resource(next).execute(); + // } + // } + @Test public void testEverythingPatientOperation() throws Exception { String methodName = "testEverythingOperation"; @@ -2271,6 +2141,26 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } + /** + * See issue #52 + */ + @Test + public void testProcedureRequestResources() throws Exception { + IGenericClient client = ourClient; + + int initialSize = client.search().forResource(ProcedureRequest.class).returnBundle(Bundle.class).execute().getEntry().size(); + + ProcedureRequest res = new ProcedureRequest(); + res.addIdentifier().setSystem("urn:foo").setValue("123"); + + client.create().resource(res).execute(); + + int newSize = client.search().forResource(ProcedureRequest.class).returnBundle(Bundle.class).execute().getEntry().size(); + + assertEquals(1, newSize - initialSize); + + } + /** * Test for issue #60 */ @@ -2813,6 +2703,147 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { assertTrue(new InstantDt(value) + " should be before " + new InstantDt(after), value.before(after)); } + @Test + public void testSearchReusesNoParams() throws Exception { + List resources = new ArrayList(); + for (int i = 0; i < 50; i++) { + Organization org = new Organization(); + org.setName("HELLO"); + resources.add(org); + } + ourClient.transaction().withResources(resources).prettyPrint().encodedXml().execute(); + + myDaoConfig.setReuseCachedSearchResultsForMillis(1000L); + + Bundle result1 = ourClient + .search() + .forResource("Organization") + .returnBundle(Bundle.class) + .execute(); + + final String uuid1 = toSearchUuidFromLinkNext(result1); + + Bundle result2 = ourClient + .search() + .forResource("Organization") + .returnBundle(Bundle.class) + .execute(); + + final String uuid2 = toSearchUuidFromLinkNext(result2); + + assertEquals(uuid1, uuid2); + } + + @Test + public void testSearchReusesResultsDisabled() throws Exception { + List resources = new ArrayList(); + for (int i = 0; i < 50; i++) { + Organization org = new Organization(); + org.setName("HELLO"); + resources.add(org); + } + ourClient.transaction().withResources(resources).prettyPrint().encodedXml().execute(); + + myDaoConfig.setReuseCachedSearchResultsForMillis(null); + + Bundle result1 = ourClient + .search() + .forResource("Organization") + .where(Organization.NAME.matches().value("HELLO")) + .count(5) + .returnBundle(Bundle.class) + .execute(); + + final String uuid1 = toSearchUuidFromLinkNext(result1); + + Bundle result2 = ourClient + .search() + .forResource("Organization") + .where(Organization.NAME.matches().value("HELLO")) + .count(5) + .returnBundle(Bundle.class) + .execute(); + + final String uuid2 = toSearchUuidFromLinkNext(result2); + + Bundle result3 = ourClient + .search() + .forResource("Organization") + .where(Organization.NAME.matches().value("HELLO")) + .count(5) + .returnBundle(Bundle.class) + .execute(); + + String uuid3 = toSearchUuidFromLinkNext(result3); + + assertNotEquals(uuid1, uuid2); + assertNotEquals(uuid1, uuid3); + } + + @Test + public void testSearchReusesResultsEnabled() throws Exception { + List resources = new ArrayList(); + for (int i = 0; i < 50; i++) { + Organization org = new Organization(); + org.setName("HELLO"); + resources.add(org); + } + ourClient.transaction().withResources(resources).prettyPrint().encodedXml().execute(); + + myDaoConfig.setReuseCachedSearchResultsForMillis(1000L); + + Bundle result1 = ourClient + .search() + .forResource("Organization") + .where(Organization.NAME.matches().value("HELLO")) + .count(5) + .returnBundle(Bundle.class) + .execute(); + + final String uuid1 = toSearchUuidFromLinkNext(result1); + Search search1 = newTxTemplate().execute(new TransactionCallback() { + @Override + public Search doInTransaction(TransactionStatus theStatus) { + return mySearchEntityDao.findByUuid(uuid1); + } + }); + Date lastReturned1 = search1.getSearchLastReturned(); + + Bundle result2 = ourClient + .search() + .forResource("Organization") + .where(Organization.NAME.matches().value("HELLO")) + .count(5) + .returnBundle(Bundle.class) + .execute(); + + final String uuid2 = toSearchUuidFromLinkNext(result2); + Search search2 = newTxTemplate().execute(new TransactionCallback() { + @Override + public Search doInTransaction(TransactionStatus theStatus) { + return mySearchEntityDao.findByUuid(uuid2); + } + }); + Date lastReturned2 = search2.getSearchLastReturned(); + + assertTrue(lastReturned2.getTime() > lastReturned1.getTime()); + + Thread.sleep(1500); + + Bundle result3 = ourClient + .search() + .forResource("Organization") + .where(Organization.NAME.matches().value("HELLO")) + .count(5) + .returnBundle(Bundle.class) + .execute(); + + String uuid3 = toSearchUuidFromLinkNext(result3); + + assertEquals(uuid1, uuid2); + assertNotEquals(uuid1, uuid3); + } + /** * See #316 */ diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java b/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java index 9021194c009..c1b3eab2817 100644 --- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java +++ b/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java @@ -31,6 +31,7 @@ public class ExampleServerIT { @Test public void testCreateAndRead() throws IOException { + ourLog.info("Base URL is: http://localhost:" + ourPort + "/baseDstu3"); String methodName = "testCreateResourceConditional"; Patient pt = new Patient(); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 7e9e0431999..e25409d2be8 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -7,6 +7,40 @@ + +
+
    +
  • + Searches with multiple search parameters of different + datatypes (e.g. find patients by name and date of birth) + were previously joined in Java code, now the join is + performed by the database which is faster +
  • +
  • + Searches which returned lots of results previously has all + results streamed into memory before anything was returned to + the client. This is particularly slow if you do a search for + (say) "get me all patients" since potentially thousands or + even millions of patients' IDs were loaded into memory + before anything gets returned to the client. HAPI FHIR + now has a multithreaded search coordinator which returns + results to the client as soon as they are available +
  • +
+

+ Existing users should delete the + HFJ_SEARCH, + HFJ_SEARCH_INCLUDE, + and + HFJ_SEARCH_RESULT + tables from your database before upgrading. + ]]> +
AuthorizationInterceptor did not correctly handle paging requests (e.g. requests for the second page of results for a search operation).