From c2c6e0b440274400ce6a1aaf9292cba31fba62ef Mon Sep 17 00:00:00 2001 From: Tadgh Date: Fri, 27 Aug 2021 12:01:10 -0400 Subject: [PATCH 01/30] Force the caller to run the bundle task if we are operating with <= 1 pool size --- .../uhn/fhir/jpa/dao/BaseTransactionProcessor.java | 13 +++++++++---- .../ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java | 4 ++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java index 3bcf236007b..fb66c22e0d5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java @@ -154,7 +154,7 @@ public abstract class BaseTransactionProcessor { private InMemoryResourceMatcher myInMemoryResourceMatcher; private ThreadPoolTaskExecutor myExecutor ; - + @VisibleForTesting public void setDaoConfig(DaoConfig theDaoConfig) { myDaoConfig = theDaoConfig; @@ -349,7 +349,12 @@ public abstract class BaseTransactionProcessor { for (int i=0; i { private CountDownLatch myCompletedLatch; - private ServletRequestDetails myRequestDetails; + private RequestDetails myRequestDetails; private IBase myNextReqEntry; private Map myResponseMap; private int myResponseOrder; @@ -1565,7 +1570,7 @@ public abstract class BaseTransactionProcessor { protected BundleTask(CountDownLatch theCompletedLatch, RequestDetails theRequestDetails, Map theResponseMap, int theResponseOrder, IBase theNextReqEntry, boolean theNestedMode) { this.myCompletedLatch = theCompletedLatch; - this.myRequestDetails = (ServletRequestDetails)theRequestDetails; + this.myRequestDetails = theRequestDetails; this.myNextReqEntry = theNextReqEntry; this.myResponseMap = theResponseMap; this.myResponseOrder = theResponseOrder; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java index a1b7bb0ae52..d5428cc9fd1 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java @@ -117,12 +117,16 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { myDaoConfig.setAllowInlineMatchUrlReferences(false); myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete()); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED); + myDaoConfig.setBundleBatchPoolSize(new DaoConfig().getBundleBatchPoolSize()); + myDaoConfig.setBundleBatchMaxPoolSize(new DaoConfig().getBundleBatchMaxPoolSize()); } @BeforeEach public void beforeDisableResultReuse() { myInterceptorRegistry.registerInterceptor(myInterceptor); myDaoConfig.setReuseCachedSearchResultsForMillis(null); + myDaoConfig.setBundleBatchPoolSize(1); + myDaoConfig.setBundleBatchMaxPoolSize(1); } private Bundle createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb theVerb) { From faf4dfd056af79f250e2d7a82d1e4435a1ae5da3 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Fri, 27 Aug 2021 12:04:56 -0400 Subject: [PATCH 02/30] fix other test class --- .../java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java | 3 --- .../ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java index fb66c22e0d5..825e424471c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java @@ -174,9 +174,6 @@ public abstract class BaseTransactionProcessor { ourLog.trace("Starting transaction processor"); myExecutor = new ThreadPoolTaskExecutor(); myExecutor.setThreadNamePrefix("bundle_batch_"); - // For single thread set the value to 1 - //myExecutor.setCorePoolSize(1); - //myExecutor.setMaxPoolSize(1); myExecutor.setCorePoolSize(myDaoConfig.getBundleBatchPoolSize()); myExecutor.setMaxPoolSize(myDaoConfig.getBundleBatchMaxPoolSize()); myExecutor.setQueueCapacity(DaoConfig.DEFAULT_BUNDLE_BATCH_QUEUE_CAPACITY); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index fc330a0e49d..a97890cbe02 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -116,12 +116,16 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete()); myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields()); myDaoConfig.setMaximumDeleteConflictQueryCount(new DaoConfig().getMaximumDeleteConflictQueryCount()); + myDaoConfig.setBundleBatchPoolSize(new DaoConfig().getBundleBatchPoolSize()); + myDaoConfig.setBundleBatchMaxPoolSize(new DaoConfig().getBundleBatchMaxPoolSize()); } @BeforeEach public void beforeDisableResultReuse() { myDaoConfig.setReuseCachedSearchResultsForMillis(null); + myDaoConfig.setBundleBatchPoolSize(1); + myDaoConfig.setBundleBatchMaxPoolSize(1); } private Bundle createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb theVerb) { From 9bdef1a9fad8b372448ad5500c9258abee4c8322 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 28 Aug 2021 13:24:09 -0400 Subject: [PATCH 03/30] Fix search of :not modifier --- .../fhir/jpa/search/builder/QueryStack.java | 50 +++- .../ResourceProviderSearchModifierR4Test.java | 248 ++++++++++++++++++ 2 files changed, 286 insertions(+), 12 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderSearchModifierR4Test.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java index 253b07e33b6..71857a9e0b5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java @@ -988,9 +988,12 @@ public class QueryStack { String theSpnamePrefix, RuntimeSearchParam theSearchParam, List theList, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) { - List tokens = new ArrayList<>(); + List tokens = new ArrayList<>(); + + boolean paramInverted = false; + + TokenParamModifier modifier = null; for (IQueryParameterType nextOr : theList) { - if (nextOr instanceof TokenParam) { if (!((TokenParam) nextOr).isEmpty()) { TokenParam id = (TokenParam) nextOr; @@ -1009,17 +1012,23 @@ public class QueryStack { } return createPredicateString(theSourceJoinColumn, theResourceName, theSpnamePrefix, theSearchParam, theList, null, theRequestPartitionId); + } + + modifier = id.getModifier(); + // for all :not modifier, create a token list, and remove the :not modifier + if (modifier != null && modifier == TokenParamModifier.NOT) { + IQueryParameterType notToken = new TokenParam(((TokenParam) nextOr).getSystem(), ((TokenParam) nextOr).getValue()); + tokens.add(notToken); + paramInverted = true; + } else { + tokens.add(nextOr); } - - tokens.add(nextOr); - } } else { tokens.add(nextOr); } - } if (tokens.isEmpty()) { @@ -1027,14 +1036,31 @@ public class QueryStack { } String paramName = getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName()); + Condition predicate; + BaseJoiningPredicateBuilder join; + + if (paramInverted) { + SearchQueryBuilder sqlBuilder = mySqlBuilder.newChildSqlBuilder(); + TokenPredicateBuilder tokenSelector = sqlBuilder.addTokenPredicateBuilder(null); + sqlBuilder.addPredicate(tokenSelector.createPredicateToken(tokens, theResourceName, theSpnamePrefix, theSearchParam, theRequestPartitionId)); + SelectQuery sql = sqlBuilder.getSelect(); + Expression subSelect = new Subquery(sql); + + join = mySqlBuilder.getOrCreateFirstPredicateBuilder(); + predicate = new InCondition(join.getResourceIdColumn(), subSelect).setNegate(true); + + } else { + + TokenPredicateBuilder tokenJoin = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.TOKEN, theSourceJoinColumn, paramName, () -> mySqlBuilder.addTokenPredicateBuilder(theSourceJoinColumn)).getResult(); - TokenPredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.TOKEN, theSourceJoinColumn, paramName, () -> mySqlBuilder.addTokenPredicateBuilder(theSourceJoinColumn)).getResult(); + if (theList.get(0).getMissing() != null) { + return tokenJoin.createPredicateParamMissingForNonReference(theResourceName, paramName, theList.get(0).getMissing(), theRequestPartitionId); + } - if (theList.get(0).getMissing() != null) { - return join.createPredicateParamMissingForNonReference(theResourceName, paramName, theList.get(0).getMissing(), theRequestPartitionId); - } - - Condition predicate = join.createPredicateToken(tokens, theResourceName, theSpnamePrefix, theSearchParam, theOperation, theRequestPartitionId); + predicate = tokenJoin.createPredicateToken(tokens, theResourceName, theSpnamePrefix, theSearchParam, theOperation, theRequestPartitionId); + join = tokenJoin; + } + return join.combineWithRequestPartitionIdPredicate(theRequestPartitionId, predicate); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderSearchModifierR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderSearchModifierR4Test.java new file mode 100644 index 00000000000..22210d36e56 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderSearchModifierR4Test.java @@ -0,0 +1,248 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Observation.ObservationStatus; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Quantity; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.parser.StrictErrorHandler; +import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; + + +public class ResourceProviderSearchModifierR4Test extends BaseResourceProviderR4Test { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderSearchModifierR4Test.class); + private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor(); + + @Override + @AfterEach + public void after() throws Exception { + super.after(); + + myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete()); + myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); + myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); + myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo()); + myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds()); + myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches()); + myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields()); + + myClient.unregisterInterceptor(myCapturingInterceptor); + } + + @BeforeEach + @Override + public void before() throws Exception { + super.before(); + myFhirCtx.setParserErrorHandler(new StrictErrorHandler()); + + myDaoConfig.setAllowMultipleDelete(true); + myClient.registerInterceptor(myCapturingInterceptor); + myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds()); + } + + @BeforeEach + public void beforeDisableResultReuse() { + myDaoConfig.setReuseCachedSearchResultsForMillis(null); + } + + @Test + public void testSearch_SingleCode_not_modifier() throws Exception { + + List obsList = createObs(10, false); + + String uri = ourServerBase + "/Observation?code:not=2345-3"; + List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); + + assertEquals(9, ids.size()); + assertEquals(obsList.get(0).toString(), ids.get(0)); + assertEquals(obsList.get(1).toString(), ids.get(1)); + assertEquals(obsList.get(2).toString(), ids.get(2)); + assertEquals(obsList.get(4).toString(), ids.get(3)); + assertEquals(obsList.get(5).toString(), ids.get(4)); + assertEquals(obsList.get(6).toString(), ids.get(5)); + assertEquals(obsList.get(7).toString(), ids.get(6)); + assertEquals(obsList.get(8).toString(), ids.get(7)); + assertEquals(obsList.get(9).toString(), ids.get(8)); + } + + @Test + public void testSearch_SingleCode_multiple_not_modifier() throws Exception { + + List obsList = createObs(10, false); + + String uri = ourServerBase + "/Observation?code:not=2345-3&code:not=2345-7&code:not=2345-9"; + List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); + + assertEquals(7, ids.size()); + assertEquals(obsList.get(0).toString(), ids.get(0)); + assertEquals(obsList.get(1).toString(), ids.get(1)); + assertEquals(obsList.get(2).toString(), ids.get(2)); + assertEquals(obsList.get(4).toString(), ids.get(3)); + assertEquals(obsList.get(5).toString(), ids.get(4)); + assertEquals(obsList.get(6).toString(), ids.get(5)); + assertEquals(obsList.get(8).toString(), ids.get(6)); + } + + @Test + public void testSearch_SingleCode_mix_modifier() throws Exception { + + List obsList = createObs(10, false); + + // Observation?code:not=2345-3&code:not=2345-7&code:not=2345-9 + // slower than Observation?code:not=2345-3&code=2345-7&code:not=2345-9 + String uri = ourServerBase + "/Observation?code:not=2345-3&code=2345-7&code:not=2345-9"; + List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); + + assertEquals(1, ids.size()); + assertEquals(obsList.get(7).toString(), ids.get(0)); + } + + @Test + public void testSearch_SingleCode_or_not_modifier() throws Exception { + + List obsList = createObs(10, false); + + String uri = ourServerBase + "/Observation?code:not=2345-3,2345-7,2345-9"; + List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); + + assertEquals(7, ids.size()); + assertEquals(obsList.get(0).toString(), ids.get(0)); + assertEquals(obsList.get(1).toString(), ids.get(1)); + assertEquals(obsList.get(2).toString(), ids.get(2)); + assertEquals(obsList.get(4).toString(), ids.get(3)); + assertEquals(obsList.get(5).toString(), ids.get(4)); + assertEquals(obsList.get(6).toString(), ids.get(5)); + assertEquals(obsList.get(8).toString(), ids.get(6)); + } + + @Test + public void testSearch_MultiCode_not_modifier() throws Exception { + + List obsList = createObs(10, true); + + String uri = ourServerBase + "/Observation?code:not=2345-3"; + List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); + + assertEquals(8, ids.size()); + assertEquals(obsList.get(0).toString(), ids.get(0)); + assertEquals(obsList.get(1).toString(), ids.get(1)); + assertEquals(obsList.get(4).toString(), ids.get(2)); + assertEquals(obsList.get(5).toString(), ids.get(3)); + assertEquals(obsList.get(6).toString(), ids.get(4)); + assertEquals(obsList.get(7).toString(), ids.get(5)); + assertEquals(obsList.get(8).toString(), ids.get(6)); + assertEquals(obsList.get(9).toString(), ids.get(7)); + } + + @Test + public void testSearch_MultiCode_multiple_not_modifier() throws Exception { + + List obsList = createObs(10, true); + + String uri = ourServerBase + "/Observation?code:not=2345-3&code:not=2345-4"; + List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); + + assertEquals(7, ids.size()); + assertEquals(obsList.get(0).toString(), ids.get(0)); + assertEquals(obsList.get(1).toString(), ids.get(1)); + assertEquals(obsList.get(5).toString(), ids.get(2)); + assertEquals(obsList.get(6).toString(), ids.get(3)); + assertEquals(obsList.get(7).toString(), ids.get(4)); + assertEquals(obsList.get(8).toString(), ids.get(5)); + assertEquals(obsList.get(9).toString(), ids.get(6)); + } + + @Test + public void testSearch_MultiCode_mix_modifier() throws Exception { + + List obsList = createObs(10, true); + + String uri = ourServerBase + "/Observation?code:not=2345-3&code=2345-7&code:not=2345-9"; + List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); + + assertEquals(2, ids.size()); + assertEquals(obsList.get(6).toString(), ids.get(0)); + assertEquals(obsList.get(7).toString(), ids.get(1)); + } + + @Test + public void testSearch_MultiCode_or_not_modifier() throws Exception { + + List obsList = createObs(10, true); + + String uri = ourServerBase + "/Observation?code:not=2345-3,2345-7,2345-9"; + List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); + + assertEquals(4, ids.size()); + assertEquals(obsList.get(0).toString(), ids.get(0)); + assertEquals(obsList.get(1).toString(), ids.get(1)); + assertEquals(obsList.get(4).toString(), ids.get(2)); + assertEquals(obsList.get(5).toString(), ids.get(3)); + } + + + private List searchAndReturnUnqualifiedVersionlessIdValues(String uri) throws IOException { + List ids; + HttpGet get = new HttpGet(uri); + + try (CloseableHttpResponse response = ourHttpClient.execute(get)) { + String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info("Response was: {}", resp); + Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, resp); + ourLog.info("Bundle: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + ids = toUnqualifiedVersionlessIdValues(bundle); + } + return ids; + } + + private List createObs(int obsNum, boolean isMultiple) { + + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester").addGiven("Joe"); + IIdType pid = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + + List obsIds = new ArrayList<>(); + IIdType obsId = null; + for (int i=0; i Date: Sat, 28 Aug 2021 13:27:40 -0400 Subject: [PATCH 04/30] Update the comment --- .../java/ca/uhn/fhir/jpa/search/builder/QueryStack.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java index 71857a9e0b5..17595ff89a6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java @@ -991,8 +991,8 @@ public class QueryStack { List tokens = new ArrayList<>(); boolean paramInverted = false; - TokenParamModifier modifier = null; + for (IQueryParameterType nextOr : theList) { if (nextOr instanceof TokenParam) { if (!((TokenParam) nextOr).isEmpty()) { @@ -1015,7 +1015,7 @@ public class QueryStack { } modifier = id.getModifier(); - // for all :not modifier, create a token list, and remove the :not modifier + // for :not modifier, create a token and remove the :not modifier if (modifier != null && modifier == TokenParamModifier.NOT) { IQueryParameterType notToken = new TokenParam(((TokenParam) nextOr).getSystem(), ((TokenParam) nextOr).getValue()); tokens.add(notToken); @@ -1024,9 +1024,7 @@ public class QueryStack { tokens.add(nextOr); } } - } else { - tokens.add(nextOr); } } From 3f128be34da9e6a92cabe68f583b5515a3ddd59f Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 28 Aug 2021 13:29:00 -0400 Subject: [PATCH 05/30] Update the code --- .../main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java index 17595ff89a6..068c06c5fe9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java @@ -1017,8 +1017,7 @@ public class QueryStack { modifier = id.getModifier(); // for :not modifier, create a token and remove the :not modifier if (modifier != null && modifier == TokenParamModifier.NOT) { - IQueryParameterType notToken = new TokenParam(((TokenParam) nextOr).getSystem(), ((TokenParam) nextOr).getValue()); - tokens.add(notToken); + tokens.add(new TokenParam(((TokenParam) nextOr).getSystem(), ((TokenParam) nextOr).getValue())); paramInverted = true; } else { tokens.add(nextOr); From 3c0a197407cf6e4c5621e2f48e8b8e9ea532dc5c Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 28 Aug 2021 16:23:09 -0400 Subject: [PATCH 06/30] Added changelog --- .../2837-search-with-not-modifier-on-multiple-codes.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2837-search-with-not-modifier-on-multiple-codes.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2837-search-with-not-modifier-on-multiple-codes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2837-search-with-not-modifier-on-multiple-codes.yaml new file mode 100644 index 00000000000..fed478a7120 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2837-search-with-not-modifier-on-multiple-codes.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 2837 +title: "The :not modifier does not currently work for observations with multiple codes for the search. This is fixed." From f04ff3fd0af2f81368e3b043e6d7efdadddd38a2 Mon Sep 17 00:00:00 2001 From: Ben Li-Sauerwine Date: Tue, 31 Aug 2021 02:04:27 -0400 Subject: [PATCH 07/30] Adds failing test for expunging and recreating a Patient with a profile in its meta field. --- .../fhir/jpa/dao/expunge/ExpungeHookTest.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeHookTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeHookTest.java index a13947beb62..8a9f3106569 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeHookTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeHookTest.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.test.concurrency.PointcutLatch; import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.dstu3.model.Meta; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -40,6 +41,8 @@ public class ExpungeHookTest extends BaseJpaDstu3Test { @BeforeEach public void before() { myDaoConfig.setExpungeEnabled(true); + myDaoConfig.setResourceClientIdStrategy(DaoConfig.ClientIdStrategyEnum.ALPHANUMERIC); + myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true); myInterceptorService.registerAnonymousInterceptor(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING, myEverythingLatch); myInterceptorService.registerAnonymousInterceptor(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, myExpungeResourceLatch); } @@ -65,6 +68,43 @@ public class ExpungeHookTest extends BaseJpaDstu3Test { assertPatientGone(id); } + @Test + public void expungeEverythingAndRecreate() throws InterruptedException { + // Create a patient. + Patient thePatient = new Patient(); + thePatient.setId("ABC123"); + Meta theMeta = new Meta(); + theMeta.addProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"); + thePatient.setMeta(theMeta); + myPatientDao.update(thePatient, mySrd); + + IIdType id = myPatientDao.update(thePatient, mySrd).getId(); + assertNotNull(myPatientDao.read(id)); + + // Expunge it directly. + myPatientDao.delete(id); + ExpungeOptions options = new ExpungeOptions(); + options.setExpungeEverything(true); + options.setExpungeDeletedResources(true); + options.setExpungeOldVersions(true); + myPatientDao.expunge(id.toUnqualifiedVersionless(), options, mySrd); + assertPatientGone(id); + + // Create it a second time. + myPatientDao.update(thePatient, mySrd); + assertNotNull(myPatientDao.read(id)); + + // Expunge everything with the service. + myEverythingLatch.setExpectedCount(1); + myExpungeService.expunge(null, null, null, options, mySrd); + myEverythingLatch.awaitExpected(); + assertPatientGone(id); + + // Create it a third time. + myPatientDao.update(thePatient, mySrd); + assertNotNull(myPatientDao.read(id)); + } + private void assertPatientGone(IIdType theId) { try { myPatientDao.read(theId); From 7534ab58f3db6a421c7fce9c7ba3733693cd95fa Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 09:58:38 -0400 Subject: [PATCH 08/30] Add failing test --- .../fhir/jpa/dao/TransactionProcessor.java | 4 +- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 80 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 2 +- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java index 748ffa6352b..bc73ea4a623 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java @@ -261,7 +261,7 @@ public class TransactionProcessor extends BaseTransactionProcessor { // No matches .filter(match -> !match.myResolved) .forEach(match -> { - ourLog.warn("Was unable to match url {} from database", match.myRequestUrl); + ourLog.debug("Was unable to match url {} from database", match.myRequestUrl); theTransactionDetails.addResolvedMatchUrl(match.myRequestUrl, TransactionDetails.NOT_FOUND); }); } @@ -337,7 +337,7 @@ public class TransactionProcessor extends BaseTransactionProcessor { } private void setSearchToResolvedAndPrefetchFoundResourcePid(TransactionDetails theTransactionDetails, List idsToPreFetch, ResourceIndexedSearchParamToken nextResult, MatchUrlToResolve nextSearchParameterMap) { - ourLog.warn("Matched url {} from database", nextSearchParameterMap.myRequestUrl); + ourLog.debug("Matched url {} from database", nextSearchParameterMap.myRequestUrl); idsToPreFetch.add(nextResult.getResourcePid()); myMatchResourceUrlService.matchUrlResolved(theTransactionDetails, nextSearchParameterMap.myResourceDefinition.getName(), nextSearchParameterMap.myRequestUrl, new ResourcePersistentId(nextResult.getResourcePid())); theTransactionDetails.addResolvedMatchUrl(nextSearchParameterMap.myRequestUrl, new ResourcePersistentId(nextResult.getResourcePid())); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 51a43daf31e..c7782f16389 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -20,6 +20,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceLink; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.model.util.UcumServiceUtil; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; @@ -150,12 +151,14 @@ import java.util.stream.Collectors; import static ca.uhn.fhir.rest.api.Constants.PARAM_TYPE; import static org.apache.commons.lang3.StringUtils.countMatches; +import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasSize; @@ -1356,6 +1359,83 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertThat(actual, contains(id)); } + @Test + public void testDuplicateConditionalCreatesOnToken() { + String bundle = "{\n" + + " \"resourceType\": \"Bundle\",\n" + + " \"type\": \"transaction\",\n" + + " \"entry\": [ {\n" + + " \"fullUrl\": \"urn:uuid:33b76421-1c91-471f-ae1c-e7486e804f18\",\n" + + " \"resource\": {\n" + + " \"resourceType\": \"Organization\",\n" + + " \"identifier\": [ {\n" + + " \"system\": \"https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-health-care-facility-id\",\n" + + " \"value\": \"3972\"\n" + + " } ]\n" + + " },\n" + + " \"request\": {\n" + + " \"method\": \"POST\",\n" + + " \"url\": \"/Organization\",\n" + + " \"ifNoneExist\": \"Organization?identifier=https%3A%2F%2Ffhir.infoway-inforoute.ca%2FNamingSystem%2Fca-on-health-care-facility-id|3972\"\n" + + " }\n" + + " }, {\n" + + " \"fullUrl\": \"urn:uuid:65d2bf18-543e-4d05-b66b-07cee541172f\",\n" + + " \"resource\": {\n" + + " \"resourceType\": \"Organization\",\n" + + " \"identifier\": [ {\n" + + " \"system\": \"https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-health-care-facility-id\",\n" + + " \"value\": \"3972\"\n" + + " } ]\n" + + " },\n" + + " \"request\": {\n" + + " \"method\": \"POST\",\n" + + " \"url\": \"/Organization\",\n" + + " \"ifNoneExist\": \"Organization?identifier=https%3A%2F%2Ffhir.infoway-inforoute.ca%2FNamingSystem%2Fca-on-health-care-facility-id|3972\"\n" + + " }\n" + + " }, {\n" + + " \"fullUrl\": \"urn:uuid:2a4635e2-e678-4ed7-9a92-901d67787434\",\n" + + " \"resource\": {\n" + + " \"resourceType\": \"ServiceRequest\",\n" + + " \"identifier\": [ {\n" + + " \"system\": \"https://corhealth-ontario.ca/NamingSystem/service-request-id\",\n" + + " \"value\": \"1\"\n" + + " } ],\n" + + " \"performer\": [ {\n" + + " \"reference\": \"urn:uuid:65d2bf18-543e-4d05-b66b-07cee541172f\",\n" + + " \"type\": \"Organization\"\n" + + " } ]\n" + + " },\n" + + " \"request\": {\n" + + " \"method\": \"PUT\",\n" + + " \"url\": \"/ServiceRequest?identifier=https%3A%2F%2Fcorhealth-ontario.ca%2FNamingSystem%2Fservice-request-id|1\"\n" + + " }\n" + + " } ]\n" + + "}"; + + Bundle bundle1 = (Bundle) myFhirCtx.newJsonParser().parseResource(bundle); + Bundle duplicateBundle = (Bundle) myFhirCtx.newJsonParser().parseResource(bundle); + ourLog.error("TRANS 1"); + Bundle bundleResponse = mySystemDao.transaction(new SystemRequestDetails(), bundle1); + bundleResponse.getEntry().stream() + .forEach( entry -> { + assertThat(entry.getResponse().getStatus(), is(equalTo("201 Created"))); + }); + + IBundleProvider search = myOrganizationDao.search(new SearchParameterMap().setLoadSynchronous(true)); + assertEquals(1, search.getAllResources().size()); + + //Running the bundle again should just result in 0 new resources created, as the org should already exist, and there is no update to the SR. + ourLog.error("TRANS 2"); + bundleResponse= mySystemDao.transaction(new SystemRequestDetails(), duplicateBundle); + bundleResponse.getEntry().stream() + .forEach( entry -> { + assertThat(entry.getResponse().getStatus(), is(equalTo("200 OK"))); + }); + + search = myOrganizationDao.search(new SearchParameterMap().setLoadSynchronous(true), new SystemRequestDetails()); + assertEquals(1, search.getAllResources().size()); + } + @Test public void testIndexNoDuplicatesToken() { Patient res = new Patient(); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml index ac75e0d04be..36a83af6599 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml +++ b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml @@ -42,7 +42,7 @@ - + From 0f6ae50105b3ba2e7c6e57f276b9400d5c1a46d9 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 10:47:23 -0400 Subject: [PATCH 09/30] Fix bug in hashToSearchMap --- .../fhir/jpa/dao/TransactionProcessor.java | 22 ++++-- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 74 ++++--------------- .../duplicate-conditional-create.json | 66 +++++++++++++++++ 3 files changed, 94 insertions(+), 68 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/duplicate-conditional-create.json diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java index bc73ea4a623..c8e25d517e3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java @@ -244,17 +244,21 @@ public class TransactionProcessor extends BaseTransactionProcessor { if (orPredicates.size() > 1) { cq.where(cb.or(orPredicates.toArray(EMPTY_PREDICATE_ARRAY))); - Map hashToSearchMap = buildHashToSearchMap(searchParameterMapsToResolve); + Map> hashToSearchMap = buildHashToSearchMap(searchParameterMapsToResolve); TypedQuery query = myEntityManager.createQuery(cq); List results = query.getResultList(); for (ResourceIndexedSearchParamToken nextResult : results) { - Optional matchedSearch = Optional.ofNullable(hashToSearchMap.get(nextResult.getHashSystemAndValue())); + Optional> matchedSearch = Optional.ofNullable(hashToSearchMap.get(nextResult.getHashSystemAndValue())); if (!matchedSearch.isPresent()) { matchedSearch = Optional.ofNullable(hashToSearchMap.get(nextResult.getHashValue())); } - matchedSearch.ifPresent(matchUrlToResolve -> setSearchToResolvedAndPrefetchFoundResourcePid(theTransactionDetails, idsToPreFetch, nextResult, matchUrlToResolve)); + matchedSearch.ifPresent(matchUrlsToResolve -> { + matchUrlsToResolve.forEach(matchUrl -> { + setSearchToResolvedAndPrefetchFoundResourcePid(theTransactionDetails, idsToPreFetch, nextResult, matchUrl); + }); + }); } //For each SP Map which did not return a result, tag it as not found. searchParameterMapsToResolve.stream() @@ -322,15 +326,19 @@ public class TransactionProcessor extends BaseTransactionProcessor { return hashPredicate; } - private Map buildHashToSearchMap(List searchParameterMapsToResolve) { - Map hashToSearch = new HashMap<>(); + private Map> buildHashToSearchMap(List searchParameterMapsToResolve) { + Map> hashToSearch = new HashMap<>(); //Build a lookup map so we don't have to iterate over the searches repeatedly. for (MatchUrlToResolve nextSearchParameterMap : searchParameterMapsToResolve) { if (nextSearchParameterMap.myHashSystemAndValue != null) { - hashToSearch.put(nextSearchParameterMap.myHashSystemAndValue, nextSearchParameterMap); + List matchUrlsToResolve = hashToSearch.getOrDefault(nextSearchParameterMap.myHashSystemAndValue, new ArrayList<>()); + matchUrlsToResolve.add(nextSearchParameterMap); + hashToSearch.put(nextSearchParameterMap.myHashSystemAndValue, matchUrlsToResolve); } if (nextSearchParameterMap.myHashValue!= null) { - hashToSearch.put(nextSearchParameterMap.myHashValue, nextSearchParameterMap); + List matchUrlsToResolve = hashToSearch.getOrDefault(nextSearchParameterMap.myHashValue, new ArrayList<>()); + matchUrlsToResolve.add(nextSearchParameterMap); + hashToSearch.put(nextSearchParameterMap.myHashValue, matchUrlsToResolve); } } return hashToSearch; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index c7782f16389..71b3e7bcdc2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -127,6 +127,7 @@ import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; @@ -1360,74 +1361,25 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { } @Test - public void testDuplicateConditionalCreatesOnToken() { - String bundle = "{\n" + - " \"resourceType\": \"Bundle\",\n" + - " \"type\": \"transaction\",\n" + - " \"entry\": [ {\n" + - " \"fullUrl\": \"urn:uuid:33b76421-1c91-471f-ae1c-e7486e804f18\",\n" + - " \"resource\": {\n" + - " \"resourceType\": \"Organization\",\n" + - " \"identifier\": [ {\n" + - " \"system\": \"https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-health-care-facility-id\",\n" + - " \"value\": \"3972\"\n" + - " } ]\n" + - " },\n" + - " \"request\": {\n" + - " \"method\": \"POST\",\n" + - " \"url\": \"/Organization\",\n" + - " \"ifNoneExist\": \"Organization?identifier=https%3A%2F%2Ffhir.infoway-inforoute.ca%2FNamingSystem%2Fca-on-health-care-facility-id|3972\"\n" + - " }\n" + - " }, {\n" + - " \"fullUrl\": \"urn:uuid:65d2bf18-543e-4d05-b66b-07cee541172f\",\n" + - " \"resource\": {\n" + - " \"resourceType\": \"Organization\",\n" + - " \"identifier\": [ {\n" + - " \"system\": \"https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-health-care-facility-id\",\n" + - " \"value\": \"3972\"\n" + - " } ]\n" + - " },\n" + - " \"request\": {\n" + - " \"method\": \"POST\",\n" + - " \"url\": \"/Organization\",\n" + - " \"ifNoneExist\": \"Organization?identifier=https%3A%2F%2Ffhir.infoway-inforoute.ca%2FNamingSystem%2Fca-on-health-care-facility-id|3972\"\n" + - " }\n" + - " }, {\n" + - " \"fullUrl\": \"urn:uuid:2a4635e2-e678-4ed7-9a92-901d67787434\",\n" + - " \"resource\": {\n" + - " \"resourceType\": \"ServiceRequest\",\n" + - " \"identifier\": [ {\n" + - " \"system\": \"https://corhealth-ontario.ca/NamingSystem/service-request-id\",\n" + - " \"value\": \"1\"\n" + - " } ],\n" + - " \"performer\": [ {\n" + - " \"reference\": \"urn:uuid:65d2bf18-543e-4d05-b66b-07cee541172f\",\n" + - " \"type\": \"Organization\"\n" + - " } ]\n" + - " },\n" + - " \"request\": {\n" + - " \"method\": \"PUT\",\n" + - " \"url\": \"/ServiceRequest?identifier=https%3A%2F%2Fcorhealth-ontario.ca%2FNamingSystem%2Fservice-request-id|1\"\n" + - " }\n" + - " } ]\n" + - "}"; + @DisplayName("Duplicate Conditional Creates all resolve to the same match") + public void testDuplicateConditionalCreatesOnToken() throws IOException { + String inputString = IOUtils.toString(getClass().getResourceAsStream("/duplicate-conditional-create.json"), StandardCharsets.UTF_8); + Bundle firstBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, inputString); - Bundle bundle1 = (Bundle) myFhirCtx.newJsonParser().parseResource(bundle); - Bundle duplicateBundle = (Bundle) myFhirCtx.newJsonParser().parseResource(bundle); - ourLog.error("TRANS 1"); - Bundle bundleResponse = mySystemDao.transaction(new SystemRequestDetails(), bundle1); - bundleResponse.getEntry().stream() - .forEach( entry -> { - assertThat(entry.getResponse().getStatus(), is(equalTo("201 Created"))); - }); + //Before you ask, yes, this has to be separately parsed. The reason for this is that the parameters passed to mySystemDao.transaction are _not_ immutable, so we cannot + //simply reuse the original bundle object. + Bundle duplicateBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, inputString); + + Bundle bundleResponse = mySystemDao.transaction(new SystemRequestDetails(), firstBundle); + bundleResponse.getEntry() + .forEach( entry -> assertThat(entry.getResponse().getStatus(), is(equalTo("201 Created")))); IBundleProvider search = myOrganizationDao.search(new SearchParameterMap().setLoadSynchronous(true)); assertEquals(1, search.getAllResources().size()); //Running the bundle again should just result in 0 new resources created, as the org should already exist, and there is no update to the SR. - ourLog.error("TRANS 2"); bundleResponse= mySystemDao.transaction(new SystemRequestDetails(), duplicateBundle); - bundleResponse.getEntry().stream() + bundleResponse.getEntry() .forEach( entry -> { assertThat(entry.getResponse().getStatus(), is(equalTo("200 OK"))); }); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/duplicate-conditional-create.json b/hapi-fhir-jpaserver-base/src/test/resources/duplicate-conditional-create.json new file mode 100644 index 00000000000..26ea0369f1f --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/duplicate-conditional-create.json @@ -0,0 +1,66 @@ +{ + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "fullUrl": "urn:uuid:4cd35592-5d4d-462b-8483-e404c023d316", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "system": "https://fhir.tester.ca/NamingSystem/ca-on-health-care-facility-id", + "value": "3972" + } + ] + }, + "request": { + "method": "POST", + "url": "/Organization", + "ifNoneExist": "Organization?identifier=https://fhir.tester.ca/NamingSystem/ca-on-health-care-facility-id|3972" + } + }, + { + "fullUrl": "urn:uuid:02643c1d-94d1-4991-a063-036fa0f57ec2", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "system": "https://fhir.tester.ca/NamingSystem/ca-on-health-care-facility-id", + "value": "3972" + } + ] + }, + "request": { + "method": "POST", + "url": "/Organization", + "ifNoneExist": "Organization?identifier=https://fhir.tester.ca/NamingSystem/ca-on-health-care-facility-id|3972" + } + }, + { + "fullUrl": "urn:uuid:8271e94f-e08b-498e-ad6d-751928c3ff99", + "resource": { + "resourceType": "ServiceRequest", + "identifier": [ + { + "system": "https://fhir-tester.ca/NamingSystem/service-request-id", + "value": "1" + } + ], + "performer": [ + { + "reference": "urn:uuid:4cd35592-5d4d-462b-8483-e404c023d316", + "type": "Organization" + }, + { + "reference": "urn:uuid:02643c1d-94d1-4991-a063-036fa0f57ec2", + "type": "Organization" + } + ] + }, + "request": { + "method": "PUT", + "url": "/ServiceRequest?identifier=https://fhir-tester.ca/NamingSystem/service-request-id|1" + } + } + ] +} From 37c88392b4db6762c694e69d38d2d2d12b801f32 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 10:53:14 -0400 Subject: [PATCH 10/30] Add changelog --- .../changelog/5_6_0/2933-fix-double-conditional-create.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml new file mode 100644 index 00000000000..49727c0125c --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 2933 +jira: SMILE-3056 +title: "Fixed a regression which causes transactions with multiple identical ifNoneExist clauses to create duplicate data." + From 2f6d8c7b35e1ba1c42d2d7fc78d93fc03e0eecec Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 10:56:31 -0400 Subject: [PATCH 11/30] Revert logging --- hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml index 36a83af6599..ac75e0d04be 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml +++ b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml @@ -42,7 +42,7 @@ - + From cc2e968595d8f285926e640049cf0e1e3ed9eae1 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 11:04:20 -0400 Subject: [PATCH 12/30] Add version.yaml --- .../resources/ca/uhn/hapi/fhir/changelog/5_5_1/version.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_1/version.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_1/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_1/version.yaml new file mode 100644 index 00000000000..41c2e9375cf --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_1/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2021-08-30" +codename: "Quasar" From c9f5dfd6d339e232ff89c9e9484b3a050d6725e8 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 11:33:06 -0400 Subject: [PATCH 13/30] Swap to SyncTaskExecutor --- .../jpa/dao/BaseTransactionProcessor.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java index 825e424471c..b7308c75eda 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java @@ -94,6 +94,8 @@ import org.hl7.fhir.r4.model.Task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.task.SyncTaskExecutor; +import org.springframework.core.task.TaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -153,7 +155,7 @@ public abstract class BaseTransactionProcessor { @Autowired private InMemoryResourceMatcher myInMemoryResourceMatcher; - private ThreadPoolTaskExecutor myExecutor ; + private TaskExecutor myExecutor ; @VisibleForTesting public void setDaoConfig(DaoConfig theDaoConfig) { @@ -172,13 +174,19 @@ public abstract class BaseTransactionProcessor { @PostConstruct public void start() { ourLog.trace("Starting transaction processor"); - myExecutor = new ThreadPoolTaskExecutor(); - myExecutor.setThreadNamePrefix("bundle_batch_"); - myExecutor.setCorePoolSize(myDaoConfig.getBundleBatchPoolSize()); - myExecutor.setMaxPoolSize(myDaoConfig.getBundleBatchMaxPoolSize()); - myExecutor.setQueueCapacity(DaoConfig.DEFAULT_BUNDLE_BATCH_QUEUE_CAPACITY); + if (myDaoConfig.getBundleBatchPoolSize() > 1) { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setThreadNamePrefix("bundle_batch_"); + executor.setCorePoolSize(myDaoConfig.getBundleBatchPoolSize()); + executor.setMaxPoolSize(myDaoConfig.getBundleBatchMaxPoolSize()); + executor.setQueueCapacity(DaoConfig.DEFAULT_BUNDLE_BATCH_QUEUE_CAPACITY); + executor.initialize(); + myExecutor = executor; - myExecutor.initialize(); + } else { + SyncTaskExecutor executor = new SyncTaskExecutor(); + myExecutor = executor; + } } public BUNDLE transaction(RequestDetails theRequestDetails, BUNDLE theRequest, boolean theNestedMode) { @@ -346,12 +354,7 @@ public abstract class BaseTransactionProcessor { for (int i=0; i { + public class BundleTask implements Runnable { private CountDownLatch myCompletedLatch; private RequestDetails myRequestDetails; @@ -1575,10 +1578,8 @@ public abstract class BaseTransactionProcessor { } @Override - public Void call() { - + public void run() { BaseServerResponseExceptionHolder caughtEx = new BaseServerResponseExceptionHolder(); - try { IBaseBundle subRequestBundle = myVersionAdapter.createBundle(org.hl7.fhir.r4.model.Bundle.BundleType.TRANSACTION.toCode()); myVersionAdapter.addEntry(subRequestBundle, (IBase) myNextReqEntry); @@ -1611,7 +1612,6 @@ public abstract class BaseTransactionProcessor { // checking for the parallelism ourLog.debug("processing bacth for {} is completed", myVersionAdapter.getEntryRequestUrl((IBase)myNextReqEntry)); myCompletedLatch.countDown(); - return null; } } } From 9cba9411c4595958021c8ef47faaf26f5612e51a Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 09:58:38 -0400 Subject: [PATCH 14/30] Add failing test --- .../fhir/jpa/dao/TransactionProcessor.java | 4 +- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 80 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 2 +- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java index 748ffa6352b..bc73ea4a623 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java @@ -261,7 +261,7 @@ public class TransactionProcessor extends BaseTransactionProcessor { // No matches .filter(match -> !match.myResolved) .forEach(match -> { - ourLog.warn("Was unable to match url {} from database", match.myRequestUrl); + ourLog.debug("Was unable to match url {} from database", match.myRequestUrl); theTransactionDetails.addResolvedMatchUrl(match.myRequestUrl, TransactionDetails.NOT_FOUND); }); } @@ -337,7 +337,7 @@ public class TransactionProcessor extends BaseTransactionProcessor { } private void setSearchToResolvedAndPrefetchFoundResourcePid(TransactionDetails theTransactionDetails, List idsToPreFetch, ResourceIndexedSearchParamToken nextResult, MatchUrlToResolve nextSearchParameterMap) { - ourLog.warn("Matched url {} from database", nextSearchParameterMap.myRequestUrl); + ourLog.debug("Matched url {} from database", nextSearchParameterMap.myRequestUrl); idsToPreFetch.add(nextResult.getResourcePid()); myMatchResourceUrlService.matchUrlResolved(theTransactionDetails, nextSearchParameterMap.myResourceDefinition.getName(), nextSearchParameterMap.myRequestUrl, new ResourcePersistentId(nextResult.getResourcePid())); theTransactionDetails.addResolvedMatchUrl(nextSearchParameterMap.myRequestUrl, new ResourcePersistentId(nextResult.getResourcePid())); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index c5cfa742aa0..ad456ccebe7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -20,6 +20,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceLink; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.model.util.UcumServiceUtil; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; @@ -151,12 +152,14 @@ import java.util.stream.Collectors; import static ca.uhn.fhir.rest.api.Constants.PARAM_TYPE; import static org.apache.commons.lang3.StringUtils.countMatches; +import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasSize; @@ -1357,6 +1360,83 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertThat(actual, contains(id)); } + @Test + public void testDuplicateConditionalCreatesOnToken() { + String bundle = "{\n" + + " \"resourceType\": \"Bundle\",\n" + + " \"type\": \"transaction\",\n" + + " \"entry\": [ {\n" + + " \"fullUrl\": \"urn:uuid:33b76421-1c91-471f-ae1c-e7486e804f18\",\n" + + " \"resource\": {\n" + + " \"resourceType\": \"Organization\",\n" + + " \"identifier\": [ {\n" + + " \"system\": \"https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-health-care-facility-id\",\n" + + " \"value\": \"3972\"\n" + + " } ]\n" + + " },\n" + + " \"request\": {\n" + + " \"method\": \"POST\",\n" + + " \"url\": \"/Organization\",\n" + + " \"ifNoneExist\": \"Organization?identifier=https%3A%2F%2Ffhir.infoway-inforoute.ca%2FNamingSystem%2Fca-on-health-care-facility-id|3972\"\n" + + " }\n" + + " }, {\n" + + " \"fullUrl\": \"urn:uuid:65d2bf18-543e-4d05-b66b-07cee541172f\",\n" + + " \"resource\": {\n" + + " \"resourceType\": \"Organization\",\n" + + " \"identifier\": [ {\n" + + " \"system\": \"https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-health-care-facility-id\",\n" + + " \"value\": \"3972\"\n" + + " } ]\n" + + " },\n" + + " \"request\": {\n" + + " \"method\": \"POST\",\n" + + " \"url\": \"/Organization\",\n" + + " \"ifNoneExist\": \"Organization?identifier=https%3A%2F%2Ffhir.infoway-inforoute.ca%2FNamingSystem%2Fca-on-health-care-facility-id|3972\"\n" + + " }\n" + + " }, {\n" + + " \"fullUrl\": \"urn:uuid:2a4635e2-e678-4ed7-9a92-901d67787434\",\n" + + " \"resource\": {\n" + + " \"resourceType\": \"ServiceRequest\",\n" + + " \"identifier\": [ {\n" + + " \"system\": \"https://corhealth-ontario.ca/NamingSystem/service-request-id\",\n" + + " \"value\": \"1\"\n" + + " } ],\n" + + " \"performer\": [ {\n" + + " \"reference\": \"urn:uuid:65d2bf18-543e-4d05-b66b-07cee541172f\",\n" + + " \"type\": \"Organization\"\n" + + " } ]\n" + + " },\n" + + " \"request\": {\n" + + " \"method\": \"PUT\",\n" + + " \"url\": \"/ServiceRequest?identifier=https%3A%2F%2Fcorhealth-ontario.ca%2FNamingSystem%2Fservice-request-id|1\"\n" + + " }\n" + + " } ]\n" + + "}"; + + Bundle bundle1 = (Bundle) myFhirCtx.newJsonParser().parseResource(bundle); + Bundle duplicateBundle = (Bundle) myFhirCtx.newJsonParser().parseResource(bundle); + ourLog.error("TRANS 1"); + Bundle bundleResponse = mySystemDao.transaction(new SystemRequestDetails(), bundle1); + bundleResponse.getEntry().stream() + .forEach( entry -> { + assertThat(entry.getResponse().getStatus(), is(equalTo("201 Created"))); + }); + + IBundleProvider search = myOrganizationDao.search(new SearchParameterMap().setLoadSynchronous(true)); + assertEquals(1, search.getAllResources().size()); + + //Running the bundle again should just result in 0 new resources created, as the org should already exist, and there is no update to the SR. + ourLog.error("TRANS 2"); + bundleResponse= mySystemDao.transaction(new SystemRequestDetails(), duplicateBundle); + bundleResponse.getEntry().stream() + .forEach( entry -> { + assertThat(entry.getResponse().getStatus(), is(equalTo("200 OK"))); + }); + + search = myOrganizationDao.search(new SearchParameterMap().setLoadSynchronous(true), new SystemRequestDetails()); + assertEquals(1, search.getAllResources().size()); + } + @Test public void testIndexNoDuplicatesToken() { Patient res = new Patient(); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml index ac75e0d04be..36a83af6599 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml +++ b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml @@ -42,7 +42,7 @@ - + From ad6c6d67c50fff99724ae51b1115f9fb4e2e53b9 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 10:47:23 -0400 Subject: [PATCH 15/30] Fix bug in hashToSearchMap --- .../fhir/jpa/dao/TransactionProcessor.java | 22 ++++-- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 74 ++++--------------- .../duplicate-conditional-create.json | 66 +++++++++++++++++ 3 files changed, 94 insertions(+), 68 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/duplicate-conditional-create.json diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java index bc73ea4a623..c8e25d517e3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java @@ -244,17 +244,21 @@ public class TransactionProcessor extends BaseTransactionProcessor { if (orPredicates.size() > 1) { cq.where(cb.or(orPredicates.toArray(EMPTY_PREDICATE_ARRAY))); - Map hashToSearchMap = buildHashToSearchMap(searchParameterMapsToResolve); + Map> hashToSearchMap = buildHashToSearchMap(searchParameterMapsToResolve); TypedQuery query = myEntityManager.createQuery(cq); List results = query.getResultList(); for (ResourceIndexedSearchParamToken nextResult : results) { - Optional matchedSearch = Optional.ofNullable(hashToSearchMap.get(nextResult.getHashSystemAndValue())); + Optional> matchedSearch = Optional.ofNullable(hashToSearchMap.get(nextResult.getHashSystemAndValue())); if (!matchedSearch.isPresent()) { matchedSearch = Optional.ofNullable(hashToSearchMap.get(nextResult.getHashValue())); } - matchedSearch.ifPresent(matchUrlToResolve -> setSearchToResolvedAndPrefetchFoundResourcePid(theTransactionDetails, idsToPreFetch, nextResult, matchUrlToResolve)); + matchedSearch.ifPresent(matchUrlsToResolve -> { + matchUrlsToResolve.forEach(matchUrl -> { + setSearchToResolvedAndPrefetchFoundResourcePid(theTransactionDetails, idsToPreFetch, nextResult, matchUrl); + }); + }); } //For each SP Map which did not return a result, tag it as not found. searchParameterMapsToResolve.stream() @@ -322,15 +326,19 @@ public class TransactionProcessor extends BaseTransactionProcessor { return hashPredicate; } - private Map buildHashToSearchMap(List searchParameterMapsToResolve) { - Map hashToSearch = new HashMap<>(); + private Map> buildHashToSearchMap(List searchParameterMapsToResolve) { + Map> hashToSearch = new HashMap<>(); //Build a lookup map so we don't have to iterate over the searches repeatedly. for (MatchUrlToResolve nextSearchParameterMap : searchParameterMapsToResolve) { if (nextSearchParameterMap.myHashSystemAndValue != null) { - hashToSearch.put(nextSearchParameterMap.myHashSystemAndValue, nextSearchParameterMap); + List matchUrlsToResolve = hashToSearch.getOrDefault(nextSearchParameterMap.myHashSystemAndValue, new ArrayList<>()); + matchUrlsToResolve.add(nextSearchParameterMap); + hashToSearch.put(nextSearchParameterMap.myHashSystemAndValue, matchUrlsToResolve); } if (nextSearchParameterMap.myHashValue!= null) { - hashToSearch.put(nextSearchParameterMap.myHashValue, nextSearchParameterMap); + List matchUrlsToResolve = hashToSearch.getOrDefault(nextSearchParameterMap.myHashValue, new ArrayList<>()); + matchUrlsToResolve.add(nextSearchParameterMap); + hashToSearch.put(nextSearchParameterMap.myHashValue, matchUrlsToResolve); } } return hashToSearch; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index ad456ccebe7..a5707a9aa9c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -128,6 +128,7 @@ import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; @@ -1361,74 +1362,25 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { } @Test - public void testDuplicateConditionalCreatesOnToken() { - String bundle = "{\n" + - " \"resourceType\": \"Bundle\",\n" + - " \"type\": \"transaction\",\n" + - " \"entry\": [ {\n" + - " \"fullUrl\": \"urn:uuid:33b76421-1c91-471f-ae1c-e7486e804f18\",\n" + - " \"resource\": {\n" + - " \"resourceType\": \"Organization\",\n" + - " \"identifier\": [ {\n" + - " \"system\": \"https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-health-care-facility-id\",\n" + - " \"value\": \"3972\"\n" + - " } ]\n" + - " },\n" + - " \"request\": {\n" + - " \"method\": \"POST\",\n" + - " \"url\": \"/Organization\",\n" + - " \"ifNoneExist\": \"Organization?identifier=https%3A%2F%2Ffhir.infoway-inforoute.ca%2FNamingSystem%2Fca-on-health-care-facility-id|3972\"\n" + - " }\n" + - " }, {\n" + - " \"fullUrl\": \"urn:uuid:65d2bf18-543e-4d05-b66b-07cee541172f\",\n" + - " \"resource\": {\n" + - " \"resourceType\": \"Organization\",\n" + - " \"identifier\": [ {\n" + - " \"system\": \"https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-health-care-facility-id\",\n" + - " \"value\": \"3972\"\n" + - " } ]\n" + - " },\n" + - " \"request\": {\n" + - " \"method\": \"POST\",\n" + - " \"url\": \"/Organization\",\n" + - " \"ifNoneExist\": \"Organization?identifier=https%3A%2F%2Ffhir.infoway-inforoute.ca%2FNamingSystem%2Fca-on-health-care-facility-id|3972\"\n" + - " }\n" + - " }, {\n" + - " \"fullUrl\": \"urn:uuid:2a4635e2-e678-4ed7-9a92-901d67787434\",\n" + - " \"resource\": {\n" + - " \"resourceType\": \"ServiceRequest\",\n" + - " \"identifier\": [ {\n" + - " \"system\": \"https://corhealth-ontario.ca/NamingSystem/service-request-id\",\n" + - " \"value\": \"1\"\n" + - " } ],\n" + - " \"performer\": [ {\n" + - " \"reference\": \"urn:uuid:65d2bf18-543e-4d05-b66b-07cee541172f\",\n" + - " \"type\": \"Organization\"\n" + - " } ]\n" + - " },\n" + - " \"request\": {\n" + - " \"method\": \"PUT\",\n" + - " \"url\": \"/ServiceRequest?identifier=https%3A%2F%2Fcorhealth-ontario.ca%2FNamingSystem%2Fservice-request-id|1\"\n" + - " }\n" + - " } ]\n" + - "}"; + @DisplayName("Duplicate Conditional Creates all resolve to the same match") + public void testDuplicateConditionalCreatesOnToken() throws IOException { + String inputString = IOUtils.toString(getClass().getResourceAsStream("/duplicate-conditional-create.json"), StandardCharsets.UTF_8); + Bundle firstBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, inputString); - Bundle bundle1 = (Bundle) myFhirCtx.newJsonParser().parseResource(bundle); - Bundle duplicateBundle = (Bundle) myFhirCtx.newJsonParser().parseResource(bundle); - ourLog.error("TRANS 1"); - Bundle bundleResponse = mySystemDao.transaction(new SystemRequestDetails(), bundle1); - bundleResponse.getEntry().stream() - .forEach( entry -> { - assertThat(entry.getResponse().getStatus(), is(equalTo("201 Created"))); - }); + //Before you ask, yes, this has to be separately parsed. The reason for this is that the parameters passed to mySystemDao.transaction are _not_ immutable, so we cannot + //simply reuse the original bundle object. + Bundle duplicateBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, inputString); + + Bundle bundleResponse = mySystemDao.transaction(new SystemRequestDetails(), firstBundle); + bundleResponse.getEntry() + .forEach( entry -> assertThat(entry.getResponse().getStatus(), is(equalTo("201 Created")))); IBundleProvider search = myOrganizationDao.search(new SearchParameterMap().setLoadSynchronous(true)); assertEquals(1, search.getAllResources().size()); //Running the bundle again should just result in 0 new resources created, as the org should already exist, and there is no update to the SR. - ourLog.error("TRANS 2"); bundleResponse= mySystemDao.transaction(new SystemRequestDetails(), duplicateBundle); - bundleResponse.getEntry().stream() + bundleResponse.getEntry() .forEach( entry -> { assertThat(entry.getResponse().getStatus(), is(equalTo("200 OK"))); }); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/duplicate-conditional-create.json b/hapi-fhir-jpaserver-base/src/test/resources/duplicate-conditional-create.json new file mode 100644 index 00000000000..26ea0369f1f --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/duplicate-conditional-create.json @@ -0,0 +1,66 @@ +{ + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "fullUrl": "urn:uuid:4cd35592-5d4d-462b-8483-e404c023d316", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "system": "https://fhir.tester.ca/NamingSystem/ca-on-health-care-facility-id", + "value": "3972" + } + ] + }, + "request": { + "method": "POST", + "url": "/Organization", + "ifNoneExist": "Organization?identifier=https://fhir.tester.ca/NamingSystem/ca-on-health-care-facility-id|3972" + } + }, + { + "fullUrl": "urn:uuid:02643c1d-94d1-4991-a063-036fa0f57ec2", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "system": "https://fhir.tester.ca/NamingSystem/ca-on-health-care-facility-id", + "value": "3972" + } + ] + }, + "request": { + "method": "POST", + "url": "/Organization", + "ifNoneExist": "Organization?identifier=https://fhir.tester.ca/NamingSystem/ca-on-health-care-facility-id|3972" + } + }, + { + "fullUrl": "urn:uuid:8271e94f-e08b-498e-ad6d-751928c3ff99", + "resource": { + "resourceType": "ServiceRequest", + "identifier": [ + { + "system": "https://fhir-tester.ca/NamingSystem/service-request-id", + "value": "1" + } + ], + "performer": [ + { + "reference": "urn:uuid:4cd35592-5d4d-462b-8483-e404c023d316", + "type": "Organization" + }, + { + "reference": "urn:uuid:02643c1d-94d1-4991-a063-036fa0f57ec2", + "type": "Organization" + } + ] + }, + "request": { + "method": "PUT", + "url": "/ServiceRequest?identifier=https://fhir-tester.ca/NamingSystem/service-request-id|1" + } + } + ] +} From 7fc6a2f16b5ad7dc7b96a23024962d58f978edee Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 10:53:14 -0400 Subject: [PATCH 16/30] Add changelog --- .../changelog/5_6_0/2933-fix-double-conditional-create.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml new file mode 100644 index 00000000000..49727c0125c --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 2933 +jira: SMILE-3056 +title: "Fixed a regression which causes transactions with multiple identical ifNoneExist clauses to create duplicate data." + From dd5c4525f3511e4fcff5d64ac702f645aaacc073 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 10:56:31 -0400 Subject: [PATCH 17/30] Revert logging --- hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml index 36a83af6599..ac75e0d04be 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml +++ b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml @@ -42,7 +42,7 @@ - + From 993b2a2f476fdb5b2bec00edb6561304923515f0 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 11:41:17 -0400 Subject: [PATCH 18/30] Backport changelog --- .../fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml index 49727c0125c..b43658c202a 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2933-fix-double-conditional-create.yaml @@ -1,6 +1,7 @@ --- type: add issue: 2933 +backport: 5.5.1 jira: SMILE-3056 title: "Fixed a regression which causes transactions with multiple identical ifNoneExist clauses to create duplicate data." From 87986d3da8a10011c883b8efd6fe3cc2cf47d7db Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 11:42:26 -0400 Subject: [PATCH 19/30] Add versionenum --- hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index 207b072cc96..bae45ce7a0f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -75,6 +75,7 @@ public enum VersionEnum { V5_4_1, V5_4_2, V5_5_0, + V5_5_1, ; From e1272a3825d5595bfe7d0ac393a872382ef14230 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 11:43:17 -0400 Subject: [PATCH 20/30] Bump version of HAPI-FHIR --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 ++-- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jaxrsserver-example/pom.xml | 2 +- hapi-fhir-jpaserver-api/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- hapi-fhir-jpaserver-batch/pom.xml | 2 +- hapi-fhir-jpaserver-cql/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-migrate/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-spring-boot-autoconfigure/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- hapi-fhir-validation-resources-dstu2.1/pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 16 ++++++++-------- hapi-tinder-test/pom.xml | 2 +- pom.xml | 2 +- restful-server-example/pom.xml | 2 +- .../pom.xml | 2 +- tests/hapi-fhir-base-test-mindeps-client/pom.xml | 2 +- tests/hapi-fhir-base-test-mindeps-server/pom.xml | 2 +- 58 files changed, 66 insertions(+), 66 deletions(-) diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 5bef19e912c..ac605f553a1 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index f71644c24d5..175bec82ea9 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 3593d59fc1e..eb519e5ac01 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 5d141ed0870..2dec6d925fa 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -3,14 +3,14 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 5.5.0 + 5.5.1 pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index d55491e16a3..764bffdc649 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 9b9c776ddaa..c2398c507e7 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 5.5.0 + 5.5.1 ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index 337b6c2fa01..1d64ca375a8 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 515c86524f8..98d960582ab 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 849be33ec18..c0cb5b02945 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 4c3f41cdbd4..ef78ddc3625 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 89b9a18563d..bd66c15803c 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 85f5bf0f678..ecb02449845 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index a6221bc215d..2aeedf71e57 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index ab6ca7747ab..be5c8924dd7 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 2aa5e750a1c..347a602d24a 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-example/pom.xml b/hapi-fhir-jaxrsserver-example/pom.xml index a639f589b29..698604938f3 100644 --- a/hapi-fhir-jaxrsserver-example/pom.xml +++ b/hapi-fhir-jaxrsserver-example/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml diff --git a/hapi-fhir-jpaserver-api/pom.xml b/hapi-fhir-jpaserver-api/pom.xml index 9d293f09528..59b52688143 100644 --- a/hapi-fhir-jpaserver-api/pom.xml +++ b/hapi-fhir-jpaserver-api/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 7803c8a05cb..b9e24140cc4 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-batch/pom.xml b/hapi-fhir-jpaserver-batch/pom.xml index 0d18f5ec19b..82d781a6fc4 100644 --- a/hapi-fhir-jpaserver-batch/pom.xml +++ b/hapi-fhir-jpaserver-batch/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-cql/pom.xml b/hapi-fhir-jpaserver-cql/pom.xml index ad5a613caba..107cec959e5 100644 --- a/hapi-fhir-jpaserver-cql/pom.xml +++ b/hapi-fhir-jpaserver-cql/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 7501c1d3a99..1b6ab5e2eb0 100644 --- a/hapi-fhir-jpaserver-mdm/pom.xml +++ b/hapi-fhir-jpaserver-mdm/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-migrate/pom.xml b/hapi-fhir-jpaserver-migrate/pom.xml index 02e0f784b95..19d94aa41b7 100644 --- a/hapi-fhir-jpaserver-migrate/pom.xml +++ b/hapi-fhir-jpaserver-migrate/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 4611dfa87a4..40eebae0685 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index d54b133f41b..1a14b581c43 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 6b9c1acbd99..9d068148864 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 920aa9fc564..0a28c161578 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 4e59cf3d3a4..487db101f6f 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index a6bc599a030..90638114fef 100644 --- a/hapi-fhir-server-mdm/pom.xml +++ b/hapi-fhir-server-mdm/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index e15d3fc4ee2..e6b970796a4 100644 --- a/hapi-fhir-server-openapi/pom.xml +++ b/hapi-fhir-server-openapi/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 39fc59b2e28..1b95ea31b85 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index b6d98eca624..ae9164f5b74 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 0b3d030206c..378ea6e0032 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.5.0 + 5.5.1 hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index 1328c2189ff..34f2462191e 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.5.0 + 5.5.1 hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index 04d3deefcec..b776bc747ef 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.5.0 + 5.5.1 hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index 74ecf61c270..aae25dfdc2d 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 5.5.0 + 5.5.1 hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index d49dd34a8ea..b5882277d6e 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 06983de95d6..be4dc5c5b0a 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index d89a0a8fcf7..354d81c4513 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 0abf455f1e8..b7835b38512 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 8ebb5c5395d..e6f417f2276 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 363ca7cfc54..082b47cf53a 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 98d9be6d55b..02ee1e5ad68 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index b38fddd471d..34ed9894948 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 6db6e12abae..3f7fccbd1c3 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 2c53f7b8c3e..494814e6276 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index eaa98985cea..6e31ef2d9a0 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 718f1cfeb50..ae9d4ae1c82 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index 040d2f10a1d..1e89972c466 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index ca4fc60e376..0134800541e 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index 389b0eee6d4..1ea695712f5 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 056d1f2f158..22049b95f48 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0 + 5.5.1 ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index cf307f2497e..a3f5635977e 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 5.5.0 + 5.5.1 ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 5.5.0 + 5.5.1 ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 5.5.0 + 5.5.1 ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 5.5.0 + 5.5.1 ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 5.5.0 + 5.5.1 ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 5.5.0 + 5.5.1 ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 5.5.0 + 5.5.1 org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 53b48277dea..cee673c6deb 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml diff --git a/pom.xml b/pom.xml index 2428e886f62..3753100ca69 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 5.5.0 + 5.5.1 HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io diff --git a/restful-server-example/pom.xml b/restful-server-example/pom.xml index da11b4ab4cf..09520df3ba4 100644 --- a/restful-server-example/pom.xml +++ b/restful-server-example/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../pom.xml diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index cb1f01a0026..030aefd2b54 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index f748df5ff68..8245677d071 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 9a78104a2bf..33ff24cab3d 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.5.1 ../../pom.xml From b1515b5963c1b750d62b18d5c97097539233d5ab Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 31 Aug 2021 12:57:45 -0400 Subject: [PATCH 21/30] Lazy load executor so that tests work safely. --- .../jpa/dao/BaseTransactionProcessor.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java index b7308c75eda..91fb57f8621 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java @@ -174,19 +174,24 @@ public abstract class BaseTransactionProcessor { @PostConstruct public void start() { ourLog.trace("Starting transaction processor"); - if (myDaoConfig.getBundleBatchPoolSize() > 1) { - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setThreadNamePrefix("bundle_batch_"); - executor.setCorePoolSize(myDaoConfig.getBundleBatchPoolSize()); - executor.setMaxPoolSize(myDaoConfig.getBundleBatchMaxPoolSize()); - executor.setQueueCapacity(DaoConfig.DEFAULT_BUNDLE_BATCH_QUEUE_CAPACITY); - executor.initialize(); - myExecutor = executor; + } + public TaskExecutor getTaskExecutor() { + if (myExecutor == null) { + if (myDaoConfig.getBundleBatchPoolSize() > 1) { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setThreadNamePrefix("bundle_batch_"); + executor.setCorePoolSize(myDaoConfig.getBundleBatchPoolSize()); + executor.setMaxPoolSize(myDaoConfig.getBundleBatchMaxPoolSize()); + executor.setQueueCapacity(DaoConfig.DEFAULT_BUNDLE_BATCH_QUEUE_CAPACITY); + executor.initialize(); + myExecutor = executor; - } else { - SyncTaskExecutor executor = new SyncTaskExecutor(); - myExecutor = executor; + } else { + SyncTaskExecutor executor = new SyncTaskExecutor(); + myExecutor = executor; + } } + return myExecutor; } public BUNDLE transaction(RequestDetails theRequestDetails, BUNDLE theRequest, boolean theNestedMode) { @@ -354,7 +359,7 @@ public abstract class BaseTransactionProcessor { for (int i=0; i Date: Tue, 31 Aug 2021 12:58:00 -0400 Subject: [PATCH 22/30] Chnge privacy --- .../java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java index 91fb57f8621..08e6f003a7c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java @@ -175,7 +175,8 @@ public abstract class BaseTransactionProcessor { public void start() { ourLog.trace("Starting transaction processor"); } - public TaskExecutor getTaskExecutor() { + + private TaskExecutor getTaskExecutor() { if (myExecutor == null) { if (myDaoConfig.getBundleBatchPoolSize() > 1) { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); From a1fbeeacaa710cc2bf6bbb457425cf7989f0e7ca Mon Sep 17 00:00:00 2001 From: katie_smilecdr Date: Tue, 31 Aug 2021 15:11:06 -0400 Subject: [PATCH 23/30] [2935] Escape "%" in like expression --- ...935-Search-with-trailing-percent-sign.yaml | 5 +++ .../predicate/StringPredicateBuilder.java | 6 ++-- .../provider/r4/ResourceProviderR4Test.java | 32 ++++++++++++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2935-Search-with-trailing-percent-sign.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2935-Search-with-trailing-percent-sign.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2935-Search-with-trailing-percent-sign.yaml new file mode 100644 index 00000000000..5c99ab10b71 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2935-Search-with-trailing-percent-sign.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 2935 +jira: SMILE-3022 +title: "No resource returned when search with percent sign. Problem is now fixed" diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java index 88fd7cdae18..bc5b9a4c891 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java @@ -225,15 +225,15 @@ public class StringPredicateBuilder extends BaseSearchParamPredicateBuilder { } public static String createLeftAndRightMatchLikeExpression(String likeExpression) { - return "%" + likeExpression.replace("%", "[%]") + "%"; + return "%" + likeExpression.replace("%", "\\%") + "%"; } public static String createLeftMatchLikeExpression(String likeExpression) { - return likeExpression.replace("%", "[%]") + "%"; + return likeExpression.replace("%", "\\%") + "%"; } public static String createRightMatchLikeExpression(String likeExpression) { - return "%" + likeExpression.replace("%", "[%]"); + return "%" + likeExpression.replace("%", "\\%"); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index 73233bd5e84..b939c4b1536 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -295,7 +295,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myCaptureQueriesListener.logSelectQueries(); } - @Test public void testSearchWithContainsLowerCase() { myDaoConfig.setAllowContainsSearches(true); @@ -333,6 +332,37 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { } + @Test + public void testSearchWithPercentSign() { + myDaoConfig.setAllowContainsSearches(true); + + Patient pt1 = new Patient(); + pt1.addName().setFamily("Smith%"); + String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue(); + + Bundle output = myClient + .search() + .forResource("Patient") + .where(Patient.NAME.contains().value("Smith%")) + .returnBundle(Bundle.class) + .execute(); + List ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList()); + assertThat(ids, containsInAnyOrder(pt1id)); + + Patient pt2 = new Patient(); + pt2.addName().setFamily("Sm%ith"); + String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue(); + + output = myClient + .search() + .forResource("Patient") + .where(Patient.NAME.contains().value("Sm%ith")) + .returnBundle(Bundle.class) + .execute(); + ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList()); + assertThat(ids, containsInAnyOrder(pt2id)); + } + @Test public void testSearchWithDateInvalid() throws IOException { HttpGet get = new HttpGet(ourServerBase + "/Condition?onset-date=junk"); From 44b0d3e9e77ac77f5019ab63c02cee646a69fff9 Mon Sep 17 00:00:00 2001 From: Frank Tao <38163583+frankjtao@users.noreply.github.com> Date: Tue, 31 Aug 2021 18:18:40 -0400 Subject: [PATCH 24/30] Fixed $lookup with displayLanguage caching issue (#2932) * Fixed $lookup with displayLanguage caching issue * Added changelog and optimized the test cases --- ...2-lookup-display-language-cache-issue.yaml | 5 + .../fhir/jpa/term/BaseTermReadSvcImpl.java | 2 +- ...ceProviderR4CodeSystemDesignationTest.java | 196 ++++++++++++------ .../support/CachingValidationSupport.java | 2 +- 4 files changed, 142 insertions(+), 63 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2932-lookup-display-language-cache-issue.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2932-lookup-display-language-cache-issue.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2932-lookup-display-language-cache-issue.yaml new file mode 100644 index 00000000000..d966668766f --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2932-lookup-display-language-cache-issue.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 2923 +title: "$lookup operation cache was based on system and code, it becomes a defect + after adding displayLanguage support. Problem is now fixed." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 3c79196b2ef..a66bf88efe9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -2005,9 +2005,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { result.setCodeDisplay(code.getDisplay()); for (TermConceptDesignation next : code.getDesignations()) { - IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation(); // filter out the designation based on displayLanguage if any if (isDisplayLanguageMatch(theDisplayLanguage, next.getLanguage())) { + IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation(); designation.setLanguage(next.getLanguage()); designation.setUseSystem(next.getUseSystem()); designation.setUseCode(next.getUseCode()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemDesignationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemDesignationTest.java index 0d9c5eedf27..5f53a3afcd9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemDesignationTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemDesignationTest.java @@ -45,33 +45,15 @@ public class ResourceProviderR4CodeSystemDesignationTest extends BaseResourcePro String resp = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + //-- The designations should have de-AT and default language List parameterList = respParam.getParameter(); + + verifyParameterList(parameterList); List designationList = getDesignations(parameterList); - - assertEquals("display", respParam.getParameter().get(0).getName()); - assertEquals(("Systolic blood pressure 12 hour minimum"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - - assertEquals("abstract", respParam.getParameter().get(1).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(1).getValue()).getValue()); - - //-- designationList - assertEquals(2, designationList.size()); - - // 1. de-AT:Systolic blood pressure 12 hour minimum - ParametersParameterComponent designation = designationList.get(0); - assertEquals("language", designation.getPart().get(0).getName()); - assertEquals("de-AT", designation.getPart().get(0).getValue().toString()); - assertEquals("value", designation.getPart().get(2).getName()); - assertEquals("de-AT:Systolic blood pressure 12 hour minimum", designation.getPart().get(2).getValue().toString()); - - // 2. Systolic blood pressure 12 hour minimum (no language) - designation = designationList.get(1); - assertEquals("language", designation.getPart().get(0).getName()); - assertNull(designation.getPart().get(0).getValue()); - assertEquals("value", designation.getPart().get(2).getName()); - assertEquals("Systolic blood pressure 12 hour minimum", designation.getPart().get(2).getValue().toString()); - + // should be de-AT and default + assertEquals(2, designationList.size()); + verifyDesignationDeAT(designationList.get(0)); + verifyDesignationNoLanguage(designationList.get(1)); } @@ -89,25 +71,14 @@ public class ResourceProviderR4CodeSystemDesignationTest extends BaseResourcePro String resp = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + //-- The designations should have default language only List parameterList = respParam.getParameter(); + + verifyParameterList(parameterList); List designationList = getDesignations(parameterList); - - assertEquals("display", respParam.getParameter().get(0).getName()); - assertEquals(("Systolic blood pressure 12 hour minimum"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - - assertEquals("abstract", respParam.getParameter().get(1).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(1).getValue()).getValue()); - - //-- designationList - assertEquals(1, designationList.size()); - - // 1. Systolic blood pressure 12 hour minimum (no language) - ParametersParameterComponent designation = designationList.get(0); - assertEquals("language", designation.getPart().get(0).getName()); - assertNull(designation.getPart().get(0).getValue()); - assertEquals("value", designation.getPart().get(2).getName()); - assertEquals("Systolic blood pressure 12 hour minimum", designation.getPart().get(2).getValue().toString()); + // should be default only + assertEquals(1, designationList.size()); + verifyDesignationNoLanguage(designationList.get(0)); } @@ -124,41 +95,145 @@ public class ResourceProviderR4CodeSystemDesignationTest extends BaseResourcePro String resp = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + //-- The designations should have all languages and the default language List parameterList = respParam.getParameter(); + + verifyParameterList(parameterList); List designationList = getDesignations(parameterList); - - assertEquals("display", respParam.getParameter().get(0).getName()); - assertEquals(("Systolic blood pressure 12 hour minimum"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - - assertEquals("abstract", respParam.getParameter().get(1).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(1).getValue()).getValue()); + // designation should be fr-FR, De-AT and default + assertEquals(3, designationList.size()); + verifyDesignationfrFR(designationList.get(0)); + verifyDesignationDeAT(designationList.get(1)); + verifyDesignationNoLanguage(designationList.get(2)); - //-- designationList - assertEquals(3, designationList.size()); + } + + @Test + public void testLookupWithDisplayLanguageCaching() { - // 1. fr-FR:Systolic blood pressure 12 hour minimum - ParametersParameterComponent designation = designationList.get(0); + //-- first call with de-AT + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8494-7")) + .andParameter("system", new UriType(CS_ACME_URL)) + .andParameter("displayLanguage",new CodeType("de-AT")) + .execute(); + + String resp = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + //-- The designations should have de-AT and default language + List parameterList = respParam.getParameter(); + + verifyParameterList(parameterList); + List designationList = getDesignations(parameterList); + // should be de-AT and default + assertEquals(2, designationList.size()); + verifyDesignationDeAT(designationList.get(0)); + verifyDesignationNoLanguage(designationList.get(1)); + + //-- second call with zh-CN (not-exist) + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8494-7")) + .andParameter("system", new UriType(CS_ACME_URL)) + .andParameter("displayLanguage",new CodeType("zh-CN")) + .execute(); + + resp = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + //-- The designations should have default language only + parameterList = respParam.getParameter(); + + verifyParameterList(parameterList); + designationList = getDesignations(parameterList); + // should be default only + assertEquals(1, designationList.size()); + verifyDesignationNoLanguage(designationList.get(0)); + + //-- third call with no language + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8494-7")) + .andParameter("system", new UriType(CS_ACME_URL)) + .execute(); + + resp = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + //-- The designations should have all languages and the default language + parameterList = respParam.getParameter(); + + verifyParameterList(parameterList); + designationList = getDesignations(parameterList); + // designation should be fr-FR, De-AT and default + assertEquals(3, designationList.size()); + verifyDesignationfrFR(designationList.get(0)); + verifyDesignationDeAT(designationList.get(1)); + verifyDesignationNoLanguage(designationList.get(2)); + + //-- forth call with fr-FR + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8494-7")) + .andParameter("system", new UriType(CS_ACME_URL)) + .andParameter("displayLanguage",new CodeType("fr-FR")) + .execute(); + + resp = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + //-- The designations should have fr-FR languages and the default language + parameterList = respParam.getParameter(); + + verifyParameterList(parameterList); + designationList = getDesignations(parameterList); + // designation should be fr-FR, default + assertEquals(2, designationList.size()); + verifyDesignationfrFR(designationList.get(0)); + verifyDesignationNoLanguage(designationList.get(1)); + } + + + private void verifyParameterList(List parameterList) { + assertEquals("display", parameterList.get(0).getName()); + assertEquals(("Systolic blood pressure 12 hour minimum"), + ((StringType) parameterList.get(0).getValue()).getValue()); + + assertEquals("abstract", parameterList.get(1).getName()); + assertEquals(false, ((BooleanType) parameterList.get(1).getValue()).getValue()); + } + + private void verifyDesignationfrFR(ParametersParameterComponent designation) { assertEquals("language", designation.getPart().get(0).getName()); assertEquals("fr-FR", designation.getPart().get(0).getValue().toString()); assertEquals("value", designation.getPart().get(2).getName()); assertEquals("fr-FR:Systolic blood pressure 12 hour minimum", designation.getPart().get(2).getValue().toString()); - - // 2. de-AT:Systolic blood pressure 12 hour minimum - designation = designationList.get(1); + } + + private void verifyDesignationDeAT(ParametersParameterComponent designation) { assertEquals("language", designation.getPart().get(0).getName()); assertEquals("de-AT", designation.getPart().get(0).getValue().toString()); assertEquals("value", designation.getPart().get(2).getName()); assertEquals("de-AT:Systolic blood pressure 12 hour minimum", designation.getPart().get(2).getValue().toString()); - - // 3. Systolic blood pressure 12 hour minimum (no language) - designation = designationList.get(2); + } + + private void verifyDesignationNoLanguage(ParametersParameterComponent designation) { assertEquals("language", designation.getPart().get(0).getName()); assertNull(designation.getPart().get(0).getValue()); assertEquals("value", designation.getPart().get(2).getName()); assertEquals("Systolic blood pressure 12 hour minimum", designation.getPart().get(2).getValue().toString()); - } + private List getDesignations(List parameterList) { List designationList = new ArrayList<>(); @@ -168,6 +243,5 @@ public class ResourceProviderR4CodeSystemDesignationTest extends BaseResourcePro designationList.add(parameter); } return designationList; - } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java index ba5a427ab36..79f5402fb07 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java @@ -112,7 +112,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { - String key = "lookupCode " + theSystem + " " + theCode; + String key = "lookupCode " + theSystem + " " + theCode + " " + defaultIfBlank(theDisplayLanguage, "NO_LANG"); return loadFromCache(myLookupCodeCache, key, t -> super.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage)); } From 51fe5ec5e714a2a04be41d79e6e79329d4d0d1a8 Mon Sep 17 00:00:00 2001 From: Ben Li-Sauerwine Date: Tue, 31 Aug 2021 20:16:22 -0400 Subject: [PATCH 25/30] Remove extra update from test. --- .../test/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeHookTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeHookTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeHookTest.java index 8a9f3106569..8cd0e76e03c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeHookTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeHookTest.java @@ -76,7 +76,6 @@ public class ExpungeHookTest extends BaseJpaDstu3Test { Meta theMeta = new Meta(); theMeta.addProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"); thePatient.setMeta(theMeta); - myPatientDao.update(thePatient, mySrd); IIdType id = myPatientDao.update(thePatient, mySrd).getId(); assertNotNull(myPatientDao.read(id)); From 7001272e4b0a7b832620110579fd4c390fa8e53c Mon Sep 17 00:00:00 2001 From: Ben Li-Sauerwine Date: Wed, 1 Sep 2021 01:26:47 -0400 Subject: [PATCH 26/30] Clear out the memory cache whenever we call the ExpungeEverythingService. --- .../dao/expunge/ExpungeEverythingService.java | 104 +++++++++++------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java index 1fa83140368..24441e1d9fe 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java @@ -65,6 +65,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.ResourceTag; import ca.uhn.fhir.jpa.model.entity.SearchParamPresent; import ca.uhn.fhir.jpa.model.entity.TagDefinition; +import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; @@ -100,6 +101,9 @@ public class ExpungeEverythingService { private TransactionTemplate myTxTemplate; + @Autowired + private MemoryCacheService myMemoryCacheService; + @PostConstruct public void initTxTemplate() { myTxTemplate = new TransactionTemplate(myPlatformTransactionManager); @@ -122,37 +126,37 @@ public class ExpungeEverythingService { counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermCodeSystem.class.getSimpleName() + " d SET d.myCurrentVersion = null")); return null; }); - counter.addAndGet(expungeEverythingByType(NpmPackageVersionResourceEntity.class)); - counter.addAndGet(expungeEverythingByType(NpmPackageVersionEntity.class)); - counter.addAndGet(expungeEverythingByType(NpmPackageEntity.class)); - counter.addAndGet(expungeEverythingByType(SearchParamPresent.class)); - counter.addAndGet(expungeEverythingByType(BulkImportJobFileEntity.class)); - counter.addAndGet(expungeEverythingByType(BulkImportJobEntity.class)); - counter.addAndGet(expungeEverythingByType(ForcedId.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamDate.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamNumber.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamQuantity.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamQuantityNormalized.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamString.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamToken.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamUri.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamCoords.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedComboStringUnique.class)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedComboTokenNonUnique.class)); - counter.addAndGet(expungeEverythingByType(ResourceLink.class)); - counter.addAndGet(expungeEverythingByType(SearchResult.class)); - counter.addAndGet(expungeEverythingByType(SearchInclude.class)); - counter.addAndGet(expungeEverythingByType(TermValueSetConceptDesignation.class)); - counter.addAndGet(expungeEverythingByType(TermValueSetConcept.class)); - counter.addAndGet(expungeEverythingByType(TermValueSet.class)); - counter.addAndGet(expungeEverythingByType(TermConceptParentChildLink.class)); - counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElementTarget.class)); - counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElement.class)); - counter.addAndGet(expungeEverythingByType(TermConceptMapGroup.class)); - counter.addAndGet(expungeEverythingByType(TermConceptMap.class)); - counter.addAndGet(expungeEverythingByType(TermConceptProperty.class)); - counter.addAndGet(expungeEverythingByType(TermConceptDesignation.class)); - counter.addAndGet(expungeEverythingByType(TermConcept.class)); + counter.addAndGet(expungeEverythingByType(NpmPackageVersionResourceEntity.class, false)); + counter.addAndGet(expungeEverythingByType(NpmPackageVersionEntity.class, false)); + counter.addAndGet(expungeEverythingByType(NpmPackageEntity.class, false)); + counter.addAndGet(expungeEverythingByType(SearchParamPresent.class, false)); + counter.addAndGet(expungeEverythingByType(BulkImportJobFileEntity.class, false)); + counter.addAndGet(expungeEverythingByType(BulkImportJobEntity.class, false)); + counter.addAndGet(expungeEverythingByType(ForcedId.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamDate.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamNumber.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamQuantity.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamQuantityNormalized.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamString.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamToken.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamUri.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamCoords.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedComboStringUnique.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceIndexedComboTokenNonUnique.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceLink.class, false)); + counter.addAndGet(expungeEverythingByType(SearchResult.class, false)); + counter.addAndGet(expungeEverythingByType(SearchInclude.class, false)); + counter.addAndGet(expungeEverythingByType(TermValueSetConceptDesignation.class, false)); + counter.addAndGet(expungeEverythingByType(TermValueSetConcept.class, false)); + counter.addAndGet(expungeEverythingByType(TermValueSet.class, false)); + counter.addAndGet(expungeEverythingByType(TermConceptParentChildLink.class, false)); + counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElementTarget.class, false)); + counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElement.class, false)); + counter.addAndGet(expungeEverythingByType(TermConceptMapGroup.class, false)); + counter.addAndGet(expungeEverythingByType(TermConceptMap.class, false)); + counter.addAndGet(expungeEverythingByType(TermConceptProperty.class, false)); + counter.addAndGet(expungeEverythingByType(TermConceptDesignation.class, false)); + counter.addAndGet(expungeEverythingByType(TermConcept.class, false)); myTxTemplate.execute(t -> { for (TermCodeSystem next : myEntityManager.createQuery("SELECT c FROM " + TermCodeSystem.class.getName() + " c", TermCodeSystem.class).getResultList()) { next.setCurrentVersion(null); @@ -160,24 +164,42 @@ public class ExpungeEverythingService { } return null; }); - counter.addAndGet(expungeEverythingByType(TermCodeSystemVersion.class)); - counter.addAndGet(expungeEverythingByType(TermCodeSystem.class)); - counter.addAndGet(expungeEverythingByType(SubscriptionTable.class)); - counter.addAndGet(expungeEverythingByType(ResourceHistoryTag.class)); - counter.addAndGet(expungeEverythingByType(ResourceTag.class)); - counter.addAndGet(expungeEverythingByType(TagDefinition.class)); - counter.addAndGet(expungeEverythingByType(ResourceHistoryProvenanceEntity.class)); - counter.addAndGet(expungeEverythingByType(ResourceHistoryTable.class)); - counter.addAndGet(expungeEverythingByType(ResourceTable.class)); - counter.addAndGet(expungeEverythingByType(PartitionEntity.class)); + counter.addAndGet(expungeEverythingByType(TermCodeSystemVersion.class, false)); + counter.addAndGet(expungeEverythingByType(TermCodeSystem.class, false)); + counter.addAndGet(expungeEverythingByType(SubscriptionTable.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceHistoryTag.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceTag.class, false)); + counter.addAndGet(expungeEverythingByType(TagDefinition.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceHistoryProvenanceEntity.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceHistoryTable.class, false)); + counter.addAndGet(expungeEverythingByType(ResourceTable.class, false)); + counter.addAndGet(expungeEverythingByType(PartitionEntity.class, false)); myTxTemplate.execute(t -> { counter.addAndGet(doExpungeEverythingQuery("DELETE from " + Search.class.getSimpleName() + " d")); return null; }); + myTxTemplate.execute(t -> { + myMemoryCacheService.invalidateAllCaches(); + return null; + }); + ourLog.info("COMPLETED GLOBAL $expunge - Deleted {} rows", counter.get()); } + public int expungeEverythingByType(Class theEntityType, boolean purgeMemoryCache) { + int outcome = expungeEverythingByType(theEntityType); + + if (purgeMemoryCache) { + myTxTemplate.execute(t -> { + myMemoryCacheService.invalidateAllCaches(); + return null; + }); + } + + return outcome; + } + public int expungeEverythingByType(Class theEntityType) { int outcome = 0; From 0724e652bbc48cfc0a6e95c8df3e4e06e096b2e9 Mon Sep 17 00:00:00 2001 From: Ben Li-Sauerwine Date: Wed, 1 Sep 2021 15:04:31 -0400 Subject: [PATCH 27/30] Refactor to prevent adding additional signatures to expungeEverythingByType. --- .../dao/expunge/ExpungeEverythingService.java | 154 +++++++++--------- 1 file changed, 75 insertions(+), 79 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java index 24441e1d9fe..fbc828e47c3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java @@ -126,37 +126,37 @@ public class ExpungeEverythingService { counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermCodeSystem.class.getSimpleName() + " d SET d.myCurrentVersion = null")); return null; }); - counter.addAndGet(expungeEverythingByType(NpmPackageVersionResourceEntity.class, false)); - counter.addAndGet(expungeEverythingByType(NpmPackageVersionEntity.class, false)); - counter.addAndGet(expungeEverythingByType(NpmPackageEntity.class, false)); - counter.addAndGet(expungeEverythingByType(SearchParamPresent.class, false)); - counter.addAndGet(expungeEverythingByType(BulkImportJobFileEntity.class, false)); - counter.addAndGet(expungeEverythingByType(BulkImportJobEntity.class, false)); - counter.addAndGet(expungeEverythingByType(ForcedId.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamDate.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamNumber.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamQuantity.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamQuantityNormalized.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamString.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamToken.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamUri.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamCoords.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedComboStringUnique.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceIndexedComboTokenNonUnique.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceLink.class, false)); - counter.addAndGet(expungeEverythingByType(SearchResult.class, false)); - counter.addAndGet(expungeEverythingByType(SearchInclude.class, false)); - counter.addAndGet(expungeEverythingByType(TermValueSetConceptDesignation.class, false)); - counter.addAndGet(expungeEverythingByType(TermValueSetConcept.class, false)); - counter.addAndGet(expungeEverythingByType(TermValueSet.class, false)); - counter.addAndGet(expungeEverythingByType(TermConceptParentChildLink.class, false)); - counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElementTarget.class, false)); - counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElement.class, false)); - counter.addAndGet(expungeEverythingByType(TermConceptMapGroup.class, false)); - counter.addAndGet(expungeEverythingByType(TermConceptMap.class, false)); - counter.addAndGet(expungeEverythingByType(TermConceptProperty.class, false)); - counter.addAndGet(expungeEverythingByType(TermConceptDesignation.class, false)); - counter.addAndGet(expungeEverythingByType(TermConcept.class, false)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(NpmPackageVersionResourceEntity.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(NpmPackageVersionEntity.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(NpmPackageEntity.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(SearchParamPresent.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(BulkImportJobFileEntity.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(BulkImportJobEntity.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ForcedId.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamDate.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamNumber.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamQuantity.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamQuantityNormalized.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamString.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamToken.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamUri.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedSearchParamCoords.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedComboStringUnique.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceIndexedComboTokenNonUnique.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceLink.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(SearchResult.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(SearchInclude.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermValueSetConceptDesignation.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermValueSetConcept.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermValueSet.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptParentChildLink.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptMapGroupElementTarget.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptMapGroupElement.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptMapGroup.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptMap.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptProperty.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConceptDesignation.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermConcept.class)); myTxTemplate.execute(t -> { for (TermCodeSystem next : myEntityManager.createQuery("SELECT c FROM " + TermCodeSystem.class.getName() + " c", TermCodeSystem.class).getResultList()) { next.setCurrentVersion(null); @@ -164,70 +164,66 @@ public class ExpungeEverythingService { } return null; }); - counter.addAndGet(expungeEverythingByType(TermCodeSystemVersion.class, false)); - counter.addAndGet(expungeEverythingByType(TermCodeSystem.class, false)); - counter.addAndGet(expungeEverythingByType(SubscriptionTable.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceHistoryTag.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceTag.class, false)); - counter.addAndGet(expungeEverythingByType(TagDefinition.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceHistoryProvenanceEntity.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceHistoryTable.class, false)); - counter.addAndGet(expungeEverythingByType(ResourceTable.class, false)); - counter.addAndGet(expungeEverythingByType(PartitionEntity.class, false)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermCodeSystemVersion.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TermCodeSystem.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(SubscriptionTable.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceHistoryTag.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceTag.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(TagDefinition.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceHistoryProvenanceEntity.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceHistoryTable.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(ResourceTable.class)); + counter.addAndGet(expungeEverythingByTypeWithoutPurging(PartitionEntity.class)); myTxTemplate.execute(t -> { counter.addAndGet(doExpungeEverythingQuery("DELETE from " + Search.class.getSimpleName() + " d")); return null; }); - myTxTemplate.execute(t -> { - myMemoryCacheService.invalidateAllCaches(); - return null; - }); + purgeAllCaches(); ourLog.info("COMPLETED GLOBAL $expunge - Deleted {} rows", counter.get()); } - public int expungeEverythingByType(Class theEntityType, boolean purgeMemoryCache) { - int outcome = expungeEverythingByType(theEntityType); + private void purgeAllCaches() { + myTxTemplate.execute(t -> { + myMemoryCacheService.invalidateAllCaches(); + return null; + }); + } - if (purgeMemoryCache) { - myTxTemplate.execute(t -> { - myMemoryCacheService.invalidateAllCaches(); - return null; + private int expungeEverythingByTypeWithoutPurging(Class theEntityType) { + int outcome = 0; + while (true) { + StopWatch sw = new StopWatch(); + + @SuppressWarnings("ConstantConditions") + int count = myTxTemplate.execute(t -> { + CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(theEntityType); + cq.from(theEntityType); + TypedQuery query = myEntityManager.createQuery(cq); + query.setMaxResults(1000); + List results = query.getResultList(); + for (Object result : results) { + myEntityManager.remove(result); + } + return results.size(); }); - } + outcome += count; + if (count == 0) { + break; + } + + ourLog.info("Have deleted {} entities of type {} in {}", outcome, theEntityType.getSimpleName(), sw.toString()); + } return outcome; } public int expungeEverythingByType(Class theEntityType) { - - int outcome = 0; - while (true) { - StopWatch sw = new StopWatch(); - - @SuppressWarnings("ConstantConditions") - int count = myTxTemplate.execute(t -> { - CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(theEntityType); - cq.from(theEntityType); - TypedQuery query = myEntityManager.createQuery(cq); - query.setMaxResults(1000); - List results = query.getResultList(); - for (Object result : results) { - myEntityManager.remove(result); - } - return results.size(); - }); - - outcome += count; - if (count == 0) { - break; - } - - ourLog.info("Have deleted {} entities of type {} in {}", outcome, theEntityType.getSimpleName(), sw.toString()); - } - return outcome; + int result = expungeEverythingByTypeWithoutPurging(theEntityType); + purgeAllCaches(); + return result; } private int doExpungeEverythingQuery(String theQuery) { From ae2c752c55ad7799752e0a2df48a62aa7dcf5e2d Mon Sep 17 00:00:00 2001 From: Tadgh Date: Wed, 1 Sep 2021 15:54:28 -0400 Subject: [PATCH 28/30] Add attribution for 2793 --- .../5_6_0/2793-expunge-everything-purges-caches.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2793-expunge-everything-purges-caches.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2793-expunge-everything-purges-caches.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2793-expunge-everything-purges-caches.yaml new file mode 100644 index 00000000000..fc20b9e6c47 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2793-expunge-everything-purges-caches.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 2793 +title: "Previously, when using the Expunge Everything operation, caches could retain old invalid values. This has been corrected. Thanks to [Ben Li-Sauerwine](https://github.com/theGOTOguy) for the fix!" From f5de2da6ba5355d16c587d7941622830d508da0f Mon Sep 17 00:00:00 2001 From: Sidharth Ramesh Date: Thu, 2 Sep 2021 14:52:38 +0530 Subject: [PATCH 29/30] Corrected typo in docs - Plain Providers It was "plan providers" before --- .../src/main/resources/ca/uhn/hapi/fhir/docs/files.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties index 0a37b3348fe..6e3bb52e90c 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties @@ -38,7 +38,7 @@ section.server_plain.title=Plain Server page.server_plain.server_types=REST Server Types page.server_plain.introduction=Plain Server Introduction page.server_plain.get_started=Get Started ⚡ -page.server_plain.resource_providers=Resource Providers and Plan Providers +page.server_plain.resource_providers=Resource Providers and Plain Providers page.server_plain.rest_operations=REST Operations: Overview page.server_plain.rest_operations_search=REST Operations: Search page.server_plain.rest_operations_operations=REST Operations: Extended Operations From 26ff15f9392f361090b02c72853a12d7c0694030 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 2 Sep 2021 14:33:30 -0400 Subject: [PATCH 30/30] Revert "Fix search of :not modifier" --- ...h-with-not-modifier-on-multiple-codes.yaml | 4 - .../fhir/jpa/search/builder/QueryStack.java | 51 +--- .../ResourceProviderSearchModifierR4Test.java | 248 ------------------ 3 files changed, 14 insertions(+), 289 deletions(-) delete mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2837-search-with-not-modifier-on-multiple-codes.yaml delete mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderSearchModifierR4Test.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2837-search-with-not-modifier-on-multiple-codes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2837-search-with-not-modifier-on-multiple-codes.yaml deleted file mode 100644 index fed478a7120..00000000000 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2837-search-with-not-modifier-on-multiple-codes.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -type: fix -issue: 2837 -title: "The :not modifier does not currently work for observations with multiple codes for the search. This is fixed." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java index 068c06c5fe9..253b07e33b6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java @@ -988,12 +988,9 @@ public class QueryStack { String theSpnamePrefix, RuntimeSearchParam theSearchParam, List theList, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) { - List tokens = new ArrayList<>(); - - boolean paramInverted = false; - TokenParamModifier modifier = null; - + List tokens = new ArrayList<>(); for (IQueryParameterType nextOr : theList) { + if (nextOr instanceof TokenParam) { if (!((TokenParam) nextOr).isEmpty()) { TokenParam id = (TokenParam) nextOr; @@ -1012,20 +1009,17 @@ public class QueryStack { } return createPredicateString(theSourceJoinColumn, theResourceName, theSpnamePrefix, theSearchParam, theList, null, theRequestPartitionId); - } - - modifier = id.getModifier(); - // for :not modifier, create a token and remove the :not modifier - if (modifier != null && modifier == TokenParamModifier.NOT) { - tokens.add(new TokenParam(((TokenParam) nextOr).getSystem(), ((TokenParam) nextOr).getValue())); - paramInverted = true; - } else { - tokens.add(nextOr); } + + tokens.add(nextOr); + } + } else { + tokens.add(nextOr); } + } if (tokens.isEmpty()) { @@ -1033,31 +1027,14 @@ public class QueryStack { } String paramName = getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName()); - Condition predicate; - BaseJoiningPredicateBuilder join; - - if (paramInverted) { - SearchQueryBuilder sqlBuilder = mySqlBuilder.newChildSqlBuilder(); - TokenPredicateBuilder tokenSelector = sqlBuilder.addTokenPredicateBuilder(null); - sqlBuilder.addPredicate(tokenSelector.createPredicateToken(tokens, theResourceName, theSpnamePrefix, theSearchParam, theRequestPartitionId)); - SelectQuery sql = sqlBuilder.getSelect(); - Expression subSelect = new Subquery(sql); - - join = mySqlBuilder.getOrCreateFirstPredicateBuilder(); - predicate = new InCondition(join.getResourceIdColumn(), subSelect).setNegate(true); - - } else { - - TokenPredicateBuilder tokenJoin = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.TOKEN, theSourceJoinColumn, paramName, () -> mySqlBuilder.addTokenPredicateBuilder(theSourceJoinColumn)).getResult(); - if (theList.get(0).getMissing() != null) { - return tokenJoin.createPredicateParamMissingForNonReference(theResourceName, paramName, theList.get(0).getMissing(), theRequestPartitionId); - } + TokenPredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.TOKEN, theSourceJoinColumn, paramName, () -> mySqlBuilder.addTokenPredicateBuilder(theSourceJoinColumn)).getResult(); - predicate = tokenJoin.createPredicateToken(tokens, theResourceName, theSpnamePrefix, theSearchParam, theOperation, theRequestPartitionId); - join = tokenJoin; - } - + if (theList.get(0).getMissing() != null) { + return join.createPredicateParamMissingForNonReference(theResourceName, paramName, theList.get(0).getMissing(), theRequestPartitionId); + } + + Condition predicate = join.createPredicateToken(tokens, theResourceName, theSpnamePrefix, theSearchParam, theOperation, theRequestPartitionId); return join.combineWithRequestPartitionIdPredicate(theRequestPartitionId, predicate); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderSearchModifierR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderSearchModifierR4Test.java deleted file mode 100644 index 22210d36e56..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderSearchModifierR4Test.java +++ /dev/null @@ -1,248 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.DateTimeType; -import org.hl7.fhir.r4.model.Observation; -import org.hl7.fhir.r4.model.Observation.ObservationStatus; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Quantity; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import ca.uhn.fhir.jpa.api.config.DaoConfig; -import ca.uhn.fhir.parser.StrictErrorHandler; -import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; - - -public class ResourceProviderSearchModifierR4Test extends BaseResourceProviderR4Test { - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderSearchModifierR4Test.class); - private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor(); - - @Override - @AfterEach - public void after() throws Exception { - super.after(); - - myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete()); - myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); - myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); - myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo()); - myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds()); - myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches()); - myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields()); - - myClient.unregisterInterceptor(myCapturingInterceptor); - } - - @BeforeEach - @Override - public void before() throws Exception { - super.before(); - myFhirCtx.setParserErrorHandler(new StrictErrorHandler()); - - myDaoConfig.setAllowMultipleDelete(true); - myClient.registerInterceptor(myCapturingInterceptor); - myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds()); - } - - @BeforeEach - public void beforeDisableResultReuse() { - myDaoConfig.setReuseCachedSearchResultsForMillis(null); - } - - @Test - public void testSearch_SingleCode_not_modifier() throws Exception { - - List obsList = createObs(10, false); - - String uri = ourServerBase + "/Observation?code:not=2345-3"; - List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); - - assertEquals(9, ids.size()); - assertEquals(obsList.get(0).toString(), ids.get(0)); - assertEquals(obsList.get(1).toString(), ids.get(1)); - assertEquals(obsList.get(2).toString(), ids.get(2)); - assertEquals(obsList.get(4).toString(), ids.get(3)); - assertEquals(obsList.get(5).toString(), ids.get(4)); - assertEquals(obsList.get(6).toString(), ids.get(5)); - assertEquals(obsList.get(7).toString(), ids.get(6)); - assertEquals(obsList.get(8).toString(), ids.get(7)); - assertEquals(obsList.get(9).toString(), ids.get(8)); - } - - @Test - public void testSearch_SingleCode_multiple_not_modifier() throws Exception { - - List obsList = createObs(10, false); - - String uri = ourServerBase + "/Observation?code:not=2345-3&code:not=2345-7&code:not=2345-9"; - List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); - - assertEquals(7, ids.size()); - assertEquals(obsList.get(0).toString(), ids.get(0)); - assertEquals(obsList.get(1).toString(), ids.get(1)); - assertEquals(obsList.get(2).toString(), ids.get(2)); - assertEquals(obsList.get(4).toString(), ids.get(3)); - assertEquals(obsList.get(5).toString(), ids.get(4)); - assertEquals(obsList.get(6).toString(), ids.get(5)); - assertEquals(obsList.get(8).toString(), ids.get(6)); - } - - @Test - public void testSearch_SingleCode_mix_modifier() throws Exception { - - List obsList = createObs(10, false); - - // Observation?code:not=2345-3&code:not=2345-7&code:not=2345-9 - // slower than Observation?code:not=2345-3&code=2345-7&code:not=2345-9 - String uri = ourServerBase + "/Observation?code:not=2345-3&code=2345-7&code:not=2345-9"; - List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); - - assertEquals(1, ids.size()); - assertEquals(obsList.get(7).toString(), ids.get(0)); - } - - @Test - public void testSearch_SingleCode_or_not_modifier() throws Exception { - - List obsList = createObs(10, false); - - String uri = ourServerBase + "/Observation?code:not=2345-3,2345-7,2345-9"; - List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); - - assertEquals(7, ids.size()); - assertEquals(obsList.get(0).toString(), ids.get(0)); - assertEquals(obsList.get(1).toString(), ids.get(1)); - assertEquals(obsList.get(2).toString(), ids.get(2)); - assertEquals(obsList.get(4).toString(), ids.get(3)); - assertEquals(obsList.get(5).toString(), ids.get(4)); - assertEquals(obsList.get(6).toString(), ids.get(5)); - assertEquals(obsList.get(8).toString(), ids.get(6)); - } - - @Test - public void testSearch_MultiCode_not_modifier() throws Exception { - - List obsList = createObs(10, true); - - String uri = ourServerBase + "/Observation?code:not=2345-3"; - List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); - - assertEquals(8, ids.size()); - assertEquals(obsList.get(0).toString(), ids.get(0)); - assertEquals(obsList.get(1).toString(), ids.get(1)); - assertEquals(obsList.get(4).toString(), ids.get(2)); - assertEquals(obsList.get(5).toString(), ids.get(3)); - assertEquals(obsList.get(6).toString(), ids.get(4)); - assertEquals(obsList.get(7).toString(), ids.get(5)); - assertEquals(obsList.get(8).toString(), ids.get(6)); - assertEquals(obsList.get(9).toString(), ids.get(7)); - } - - @Test - public void testSearch_MultiCode_multiple_not_modifier() throws Exception { - - List obsList = createObs(10, true); - - String uri = ourServerBase + "/Observation?code:not=2345-3&code:not=2345-4"; - List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); - - assertEquals(7, ids.size()); - assertEquals(obsList.get(0).toString(), ids.get(0)); - assertEquals(obsList.get(1).toString(), ids.get(1)); - assertEquals(obsList.get(5).toString(), ids.get(2)); - assertEquals(obsList.get(6).toString(), ids.get(3)); - assertEquals(obsList.get(7).toString(), ids.get(4)); - assertEquals(obsList.get(8).toString(), ids.get(5)); - assertEquals(obsList.get(9).toString(), ids.get(6)); - } - - @Test - public void testSearch_MultiCode_mix_modifier() throws Exception { - - List obsList = createObs(10, true); - - String uri = ourServerBase + "/Observation?code:not=2345-3&code=2345-7&code:not=2345-9"; - List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); - - assertEquals(2, ids.size()); - assertEquals(obsList.get(6).toString(), ids.get(0)); - assertEquals(obsList.get(7).toString(), ids.get(1)); - } - - @Test - public void testSearch_MultiCode_or_not_modifier() throws Exception { - - List obsList = createObs(10, true); - - String uri = ourServerBase + "/Observation?code:not=2345-3,2345-7,2345-9"; - List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); - - assertEquals(4, ids.size()); - assertEquals(obsList.get(0).toString(), ids.get(0)); - assertEquals(obsList.get(1).toString(), ids.get(1)); - assertEquals(obsList.get(4).toString(), ids.get(2)); - assertEquals(obsList.get(5).toString(), ids.get(3)); - } - - - private List searchAndReturnUnqualifiedVersionlessIdValues(String uri) throws IOException { - List ids; - HttpGet get = new HttpGet(uri); - - try (CloseableHttpResponse response = ourHttpClient.execute(get)) { - String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response was: {}", resp); - Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, resp); - ourLog.info("Bundle: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); - ids = toUnqualifiedVersionlessIdValues(bundle); - } - return ids; - } - - private List createObs(int obsNum, boolean isMultiple) { - - Patient patient = new Patient(); - patient.addIdentifier().setSystem("urn:system").setValue("001"); - patient.addName().setFamily("Tester").addGiven("Joe"); - IIdType pid = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); - - List obsIds = new ArrayList<>(); - IIdType obsId = null; - for (int i=0; i