From 794d9145e9197f875b84e72108629668ac5261fc Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 24 Oct 2018 17:54:58 -0300 Subject: [PATCH] Move query count tests to consolidate them, and avoid an accidental rewrite of existing indexes in some conditions --- .../fhir/rest/gclient/StringClientParam.java | 2 +- .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 13 + .../java/ca/uhn/fhir/jpa/dao/DaoConfig.java | 29 + .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 194 +- .../BaseResourceIndexedSearchParam.java | 6 +- .../ResourceIndexedSearchParamCoords.java | 1 + .../ResourceIndexedSearchParamDate.java | 1 + .../ResourceIndexedSearchParamNumber.java | 1 + .../ResourceIndexedSearchParamQuantity.java | 1 + .../ResourceIndexedSearchParamString.java | 1 + .../ResourceIndexedSearchParamToken.java | 1 + .../entity/ResourceIndexedSearchParamUri.java | 1 + .../SubscriptionTriggeringProvider.java | 20 +- .../search/StaleSearchDeletingSvcImpl.java | 4 +- .../ca/uhn/fhir/jpa/config/TestR4Config.java | 9 +- .../dao/r4/FhirResourceDaoR4CreateTest.java | 38 +- .../r4/FhirResourceDaoR4QueryCountTest.java | 128 +- .../FhirResourceDaoR4SearchNoHashesTest.java | 3343 +++++++++++++++++ .../dao/r4/FhirResourceDaoR4UpdateTest.java | 27 - src/changes/changes.xml | 5 + 20 files changed, 3736 insertions(+), 89 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/StringClientParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/StringClientParam.java index 5d0a463bbfe..c7e023cc56e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/StringClientParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/StringClientParam.java @@ -31,7 +31,7 @@ import java.util.List; * @author james * */ -public class StringClientParam extends BaseClientParam implements IParam { +public class StringClientParam extends BaseClientParam implements IParam { private final String myParamName; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index ea1941a8885..9cab2789e04 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -2086,6 +2086,7 @@ public abstract class BaseHapiFhirDao implements IDao, */ if (thePerformIndexing) { + calculateHashes(stringParams); for (ResourceIndexedSearchParamString next : removeCommon(existingStringParams, stringParams)) { next.setDaoConfig(myConfig); myEntityManager.remove(next); @@ -2095,6 +2096,7 @@ public abstract class BaseHapiFhirDao implements IDao, myEntityManager.persist(next); } + calculateHashes(tokenParams); for (ResourceIndexedSearchParamToken next : removeCommon(existingTokenParams, tokenParams)) { myEntityManager.remove(next); theEntity.getParamsToken().remove(next); @@ -2103,6 +2105,7 @@ public abstract class BaseHapiFhirDao implements IDao, myEntityManager.persist(next); } + calculateHashes(numberParams); for (ResourceIndexedSearchParamNumber next : removeCommon(existingNumberParams, numberParams)) { myEntityManager.remove(next); theEntity.getParamsNumber().remove(next); @@ -2111,6 +2114,7 @@ public abstract class BaseHapiFhirDao implements IDao, myEntityManager.persist(next); } + calculateHashes(quantityParams); for (ResourceIndexedSearchParamQuantity next : removeCommon(existingQuantityParams, quantityParams)) { myEntityManager.remove(next); theEntity.getParamsQuantity().remove(next); @@ -2120,6 +2124,7 @@ public abstract class BaseHapiFhirDao implements IDao, } // Store date SP's + calculateHashes(dateParams); for (ResourceIndexedSearchParamDate next : removeCommon(existingDateParams, dateParams)) { myEntityManager.remove(next); theEntity.getParamsDate().remove(next); @@ -2129,6 +2134,7 @@ public abstract class BaseHapiFhirDao implements IDao, } // Store URI SP's + calculateHashes(uriParams); for (ResourceIndexedSearchParamUri next : removeCommon(existingUriParams, uriParams)) { myEntityManager.remove(next); theEntity.getParamsUri().remove(next); @@ -2138,6 +2144,7 @@ public abstract class BaseHapiFhirDao implements IDao, } // Store Coords SP's + calculateHashes(coordsParams); for (ResourceIndexedSearchParamCoords next : removeCommon(existingCoordsParams, coordsParams)) { myEntityManager.remove(next); theEntity.getParamsCoords().remove(next); @@ -2187,6 +2194,12 @@ public abstract class BaseHapiFhirDao implements IDao, return theEntity; } + private void calculateHashes(Collection theStringParams) { + for (BaseResourceIndexedSearchParam next : theStringParams) { + next.calculateHashes(); + } + } + protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable entity, Date theDeletedTimestampOrNull, Date theUpdateTime) { return updateEntity(theRequest, theResource, entity, theDeletedTimestampOrNull, true, true, theUpdateTime, false, true); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java index a455a8dff04..0bd421cc87c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java @@ -155,6 +155,7 @@ public class DaoConfig { private boolean myValidateSearchParameterExpressionsOnSave = true; private List mySearchPreFetchThresholds = Arrays.asList(500, 2000, -1); private List myWarmCacheEntries = new ArrayList<>(); + private boolean myDisableHashBasedSearches; /** * Constructor @@ -1383,6 +1384,34 @@ public class DaoConfig { return mySearchPreFetchThresholds; } + /** + * If set to true (default is false) the server will not use + * hash based searches. These searches were introduced in HAPI FHIR 3.5.0 + * and are the new default way of searching. However they require a very + * large data migration if an existing system has a large amount of data + * so this setting can be used to use the old search mechanism while data + * is migrated. + * + * @since 3.6.0 + */ + public boolean getDisableHashBasedSearches() { + return myDisableHashBasedSearches; + } + + /** + * If set to true (default is false) the server will not use + * hash based searches. These searches were introduced in HAPI FHIR 3.5.0 + * and are the new default way of searching. However they require a very + * large data migration if an existing system has a large amount of data + * so this setting can be used to use the old search mechanism while data + * is migrated. + * + * @since 3.6.0 + */ + public void setDisableHashBasedSearches(boolean theDisableHashBasedSearches) { + myDisableHashBasedSearches = theDisableHashBasedSearches; + } + public enum IndexEnabledEnum { ENABLED, DISABLED diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 42d9af1f44b..210da7c3d41 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -55,6 +55,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -94,6 +95,7 @@ public class SearchBuilder implements ISearchBuilder { private static SearchParameterMap ourLastHandlerParamsForUnitTest; private static String ourLastHandlerThreadForUnitTest; private static boolean ourTrackHandlersForUnitTest; + private final boolean myDontUseHashesForSearch; protected IResourceTagDao myResourceTagDao; private IResourceSearchViewDao myResourceSearchViewDao; private List myAlsoIncludePids; @@ -130,6 +132,7 @@ public class SearchBuilder implements ISearchBuilder { myEntityManager = theEntityManager; myFulltextSearchSvc = theFulltextSearchSvc; myCallingDao = theDao; + myDontUseHashesForSearch = theDao.getConfig().getDisableHashBasedSearches(); myResourceIndexedSearchParamUriDao = theResourceIndexedSearchParamUriDao; myForcedIdDao = theForcedIdDao; myTerminologySvc = theTerminologySvc; @@ -304,6 +307,15 @@ public class SearchBuilder implements ISearchBuilder { } private void addPredicateParamMissing(String theResourceName, String theParamName, boolean theMissing) { +// if (myDontUseHashesForSearch) { +// Join paramPresentJoin = myResourceTableRoot.join("mySearchParamPresents", JoinType.LEFT); +// Join paramJoin = paramPresentJoin.join("mySearchParam", JoinType.LEFT); +// +// myPredicates.add(myBuilder.equal(paramJoin.get("myResourceName"), theResourceName)); +// myPredicates.add(myBuilder.equal(paramJoin.get("myParamName"), theParamName)); +// myPredicates.add(myBuilder.equal(paramPresentJoin.get("myPresent"), !theMissing)); +// } + Join paramPresentJoin = myResourceTableRoot.join("mySearchParamPresents", JoinType.LEFT); Expression hashPresence = paramPresentJoin.get("myHashPresence").as(Long.class); @@ -841,10 +853,18 @@ public class SearchBuilder implements ISearchBuilder { } else { - long hashUri = ResourceIndexedSearchParamUri.calculateHashUri(theResourceName, theParamName, value); - Predicate hashPredicate = myBuilder.equal(join.get("myHashUri"), hashUri); - codePredicates.add(hashPredicate); + if (myDontUseHashesForSearch) { + Predicate predicate = myBuilder.equal(join.get("myUri").as(String.class), value); + codePredicates.add(predicate); + + } else { + + long hashUri = ResourceIndexedSearchParamUri.calculateHashUri(theResourceName, theParamName, value); + Predicate hashPredicate = myBuilder.equal(join.get("myHashUri"), hashUri); + codePredicates.add(hashPredicate); + + } } } else { @@ -868,6 +888,13 @@ public class SearchBuilder implements ISearchBuilder { } private Predicate combineParamIndexPredicateWithParamNamePredicate(String theResourceName, String theParamName, From theFrom, Predicate thePredicate) { + if (myDontUseHashesForSearch) { + Predicate resourceTypePredicate = myBuilder.equal(theFrom.get("myResourceType"), theResourceName); + Predicate paramNamePredicate = myBuilder.equal(theFrom.get("myParamName"), theParamName); + Predicate outerPredicate = myBuilder.and(resourceTypePredicate, paramNamePredicate, thePredicate); + return outerPredicate; + } + long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(theResourceName, theParamName); Predicate hashIdentityPredicate = myBuilder.equal(theFrom.get("myHashIdentity"), hashIdentity); return myBuilder.and(hashIdentityPredicate, thePredicate); @@ -1079,6 +1106,37 @@ public class SearchBuilder implements ISearchBuilder { throw new IllegalArgumentException("Invalid quantity type: " + theParam.getClass()); } + if (myDontUseHashesForSearch) { + Predicate system = null; + if (!isBlank(systemValue)) { + system = theBuilder.equal(theFrom.get("mySystem"), systemValue); + } + + Predicate code = null; + if (!isBlank(unitsValue)) { + code = theBuilder.equal(theFrom.get("myUnits"), unitsValue); + } + + cmpValue = ObjectUtils.defaultIfNull(cmpValue, ParamPrefixEnum.EQUAL); + final Expression path = theFrom.get("myValue"); + String invalidMessageName = "invalidQuantityPrefix"; + + Predicate num = createPredicateNumeric(theResourceName, null, theFrom, theBuilder, theParam, cmpValue, valueValue, path, invalidMessageName); + + Predicate singleCode; + if (system == null && code == null) { + singleCode = num; + } else if (system == null) { + singleCode = theBuilder.and(code, num); + } else if (code == null) { + singleCode = theBuilder.and(system, num); + } else { + singleCode = theBuilder.and(system, code, num); + } + + return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode); + } + Predicate hashPredicate; if (!isBlank(systemValue) && !isBlank(unitsValue)) { long hash = ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(theResourceName, theParamName, systemValue, unitsValue); @@ -1130,6 +1188,31 @@ public class SearchBuilder implements ISearchBuilder { + ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm); } + if (myDontUseHashesForSearch) { + String likeExpression = BaseHapiFhirDao.normalizeString(rawSearchTerm); + if (myCallingDao.getConfig().isAllowContainsSearches()) { + if (theParameter instanceof StringParam) { + if (((StringParam) theParameter).isContains()) { + likeExpression = createLeftAndRightMatchLikeExpression(likeExpression); + } else { + likeExpression = createLeftMatchLikeExpression(likeExpression); + } + } else { + likeExpression = createLeftMatchLikeExpression(likeExpression); + } + } else { + likeExpression = createLeftMatchLikeExpression(likeExpression); + } + + Predicate singleCode = theBuilder.like(theFrom.get("myValueNormalized").as(String.class), likeExpression); + if (theParameter instanceof StringParam && ((StringParam) theParameter).isExact()) { + Predicate exactCode = theBuilder.equal(theFrom.get("myValueExact"), rawSearchTerm); + singleCode = theBuilder.and(singleCode, exactCode); + } + + return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode); + } + boolean exactMatch = theParameter instanceof StringParam && ((StringParam) theParameter).isExact(); if (exactMatch) { @@ -1234,6 +1317,92 @@ public class SearchBuilder implements ISearchBuilder { return new BooleanStaticAssertionPredicate((CriteriaBuilderImpl) theBuilder, false); } + if (myDontUseHashesForSearch) { + ArrayList singleCodePredicates = new ArrayList(); + if (codes != null) { + + List orPredicates = new ArrayList(); + Map> map = new HashMap>(); + for (VersionIndependentConcept nextCode : codes) { + List systemCodes = map.get(nextCode.getSystem()); + if (null == systemCodes) { + systemCodes = new ArrayList<>(); + map.put(nextCode.getSystem(), systemCodes); + } + systemCodes.add(nextCode); + } + // Use "in" in case of large numbers of codes due to param modifiers + final Path systemExpression = theFrom.get("mySystem"); + final Path valueExpression = theFrom.get("myValue"); + for (Map.Entry> entry : map.entrySet()) { + CriteriaBuilder.In codePredicate = theBuilder.in(valueExpression); + boolean haveAtLeastOneCode = false; + for (VersionIndependentConcept nextCode : entry.getValue()) { + if (isNotBlank(nextCode.getCode())) { + codePredicate.value(nextCode.getCode()); + haveAtLeastOneCode = true; + } + } + + if (entry.getKey() != null) { + Predicate systemPredicate = theBuilder.equal(systemExpression, entry.getKey()); + if (haveAtLeastOneCode) { + orPredicates.add(theBuilder.and(systemPredicate, codePredicate)); + } else { + orPredicates.add(systemPredicate); + } + } else { + orPredicates.add(codePredicate); + } + } + + Predicate or = theBuilder.or(orPredicates.toArray(new Predicate[0])); + if (modifier == TokenParamModifier.NOT) { + or = theBuilder.not(or); + } + singleCodePredicates.add(or); + + } else { + + /* + * Ok, this is a normal query + */ + + if (StringUtils.isNotBlank(system)) { + if (modifier != null && modifier == TokenParamModifier.NOT) { + singleCodePredicates.add(theBuilder.notEqual(theFrom.get("mySystem"), system)); + } else { + singleCodePredicates.add(theBuilder.equal(theFrom.get("mySystem"), system)); + } + } else if (system == null) { + // don't check the system + } else { + // If the system is "", we only match on null systems + singleCodePredicates.add(theBuilder.isNull(theFrom.get("mySystem"))); + } + + if (StringUtils.isNotBlank(code)) { + if (modifier != null && modifier == TokenParamModifier.NOT) { + singleCodePredicates.add(theBuilder.notEqual(theFrom.get("myValue"), code)); + } else { + singleCodePredicates.add(theBuilder.equal(theFrom.get("myValue"), code)); + } + } else { + /* + * As of HAPI FHIR 1.5, if the client searched for a token with a system but no specified value this means to + * match all tokens with the given value. + * + * I'm not sure I agree with this, but hey.. FHIR-I voted and this was the result :) + */ + // singleCodePredicates.add(theBuilder.isNull(theFrom.get("myValue"))); + } + } + + Predicate singleCode = theBuilder.and(toArray(singleCodePredicates)); + return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode); + } + + /* * Note: A null system value means "match any system", but * an empty-string system value means "match values that @@ -1607,9 +1776,14 @@ public class SearchBuilder implements ISearchBuilder { if (param.getParamType() == RestSearchParameterTypeEnum.REFERENCE) { thePredicates.add(join.get("mySourcePath").as(String.class).in(param.getPathsSplit())); } else { - Long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(myResourceName, theSort.getParamName()); - Predicate joinParam1 = theBuilder.equal(join.get("myHashIdentity"), hashIdentity); - thePredicates.add(joinParam1); + if (myDontUseHashesForSearch) { + Predicate joinParam1 = theBuilder.equal(join.get("myParamName"), theSort.getParamName()); + thePredicates.add(joinParam1); + } else { + Long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(myResourceName, theSort.getParamName()); + Predicate joinParam1 = theBuilder.equal(join.get("myHashIdentity"), hashIdentity); + thePredicates.add(joinParam1); + } } } else { ourLog.debug("Reusing join for {}", theSort.getParamName()); @@ -1668,7 +1842,7 @@ public class SearchBuilder implements ISearchBuilder { //-- preload all tags with tag definition if any Map> tagMap = getResourceTagMap(resourceSearchViewList); - Long resourceId = null; + Long resourceId; for (ResourceSearchView next : resourceSearchViewList) { Class resourceType = context.getResourceDefinition(next.getResourceType()).getImplementingClass(); @@ -1706,7 +1880,7 @@ public class SearchBuilder implements ISearchBuilder { private Map> getResourceTagMap(Collection theResourceSearchViewList) { - List idList = new ArrayList(theResourceSearchViewList.size()); + List idList = new ArrayList<>(theResourceSearchViewList.size()); //-- find all resource has tags for (ResourceSearchView resource : theResourceSearchViewList) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java index 82366f7ed15..a51d92819f4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -129,6 +129,8 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable { public abstract IQueryParameterType toQueryParameterType(); + public abstract void calculateHashes(); + public static long calculateHashIdentity(String theResourceType, String theParamName) { return hash(theResourceType, theParamName); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamCoords.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamCoords.java index e21f6bcd9ba..b7503142046 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamCoords.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamCoords.java @@ -67,6 +67,7 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP setLongitude(theLongitude); } + @Override @PrePersist public void calculateHashes() { if (myHashIdentity == null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java index b26ec43f84a..7b43ebf9dce 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java @@ -83,6 +83,7 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar myOriginalValue = theOriginalValue; } + @Override @PrePersist public void calculateHashes() { if (myHashIdentity == null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamNumber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamNumber.java index cc28ecbe639..ed5568fbd92 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamNumber.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamNumber.java @@ -69,6 +69,7 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP setValue(theValue); } + @Override @PrePersist public void calculateHashes() { if (myHashIdentity == null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java index 2e78221cde1..13c7e7fc2ad 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java @@ -95,6 +95,7 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc setUnits(theUnits); } + @Override @PrePersist public void calculateHashes() { if (myHashIdentity == null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java index 9429ad953c8..023199b395f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java @@ -161,6 +161,7 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP myHashIdentity = theHashIdentity; } + @Override @PrePersist public void calculateHashes() { if (myHashNormalizedPrefix == null && myDaoConfig != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java index 75938fb79e8..74a86253195 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java @@ -108,6 +108,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa setValue(theValue); } + @Override @PrePersist public void calculateHashes() { if (myHashSystem == null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java index 6f35d8809e9..b94ee78db6f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java @@ -82,6 +82,7 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara setUri(theUri); } + @Override @PrePersist public void calculateHashes() { if (myHashUri == null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/SubscriptionTriggeringProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/SubscriptionTriggeringProvider.java index 5f8a18d5bbf..2386ea4070a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/SubscriptionTriggeringProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/SubscriptionTriggeringProvider.java @@ -39,6 +39,7 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.util.ParametersUtil; @@ -307,7 +308,7 @@ public class SubscriptionTriggeringProvider implements IResourceProvider, Applic ourLog.info("Triggering job[{}] search {} requesting resources {} - {}", theJobDetails.getJobId(), theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex); List resourceIds = mySearchCoordinatorSvc.getResources(theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex); - ourLog.info("Triggering job[{}] delivering {} resources", theJobDetails.getJobId(), theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex); + ourLog.info("Triggering job[{}] delivering {} resources", theJobDetails.getJobId(), resourceIds.size()); int highestIndexSubmitted = theJobDetails.getCurrentSearchLastUploadedIndex(); for (Long next : resourceIds) { @@ -374,9 +375,22 @@ public class SubscriptionTriggeringProvider implements IResourceProvider, Applic msg.setNewPayload(myFhirContext, theResourceToTrigger); return myExecutorService.submit(()->{ - for (BaseSubscriptionInterceptor next : mySubscriptionInterceptorList) { - next.submitResourceModified(msg); + for (int i = 0; ; i++) { + try { + for (BaseSubscriptionInterceptor next : mySubscriptionInterceptorList) { + next.submitResourceModified(msg); + } + break; + } catch (Exception e) { + if (i >= 3) { + throw new InternalErrorException(e); + } + + ourLog.warn("Exception while retriggering subscriptions (going to sleep and retry): {}", e.toString()); + Thread.sleep(1000); + } } + return null; }); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java index 5e90ad0dd27..00a046f9a07 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java @@ -74,7 +74,6 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc { private void deleteSearch(final Long theSearchPid) { mySearchDao.findById(theSearchPid).ifPresent(searchToDelete -> { - ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), new InstantType(searchToDelete.getCreated()), new InstantType(searchToDelete.getSearchLastReturned())); mySearchIncludeDao.deleteForSearch(searchToDelete.getId()); /* @@ -93,7 +92,10 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc { // Only delete if we don't have results left in this search if (resultPids.getNumberOfElements() < max) { + ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), new InstantType(searchToDelete.getCreated()), new InstantType(searchToDelete.getSearchLastReturned())); mySearchDao.deleteByPid(searchToDelete.getId()); + } else { + ourLog.info("Purged {} search results for deleted search {}/{}", resultPids.getSize(), searchToDelete.getId(), searchToDelete.getUuid()); } }); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java index d6a0342cacf..1d86d6e2070 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.config; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; +import net.ttddyy.dsproxy.listener.SingleQueryCountHolder; import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder; import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; @@ -107,12 +108,18 @@ public class TestR4Config extends BaseJavaConfigR4 { .create(retVal) .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL") .logSlowQueryBySlf4j(10, TimeUnit.SECONDS) - .countQuery(new ThreadQueryCountHolder()) +// .countQuery(new ThreadQueryCountHolder()) + .countQuery(singleQueryCountHolder()) .build(); return dataSource; } + @Bean + public SingleQueryCountHolder singleQueryCountHolder() { + return new SingleQueryCountHolder(); + } + @Override @Bean() public LocalContainerEntityManagerFactoryBean entityManagerFactory() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CreateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CreateTest.java index 1a2b7155c1a..4006322d6bc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CreateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CreateTest.java @@ -4,7 +4,6 @@ import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.util.TestUtil; -import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.IdType; @@ -18,10 +17,9 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.matchesPattern; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test { private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4CreateTest.class); @@ -37,22 +35,22 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test { Patient p = myFhirCtx.newXmlParser().parseResource(Patient.class, input); String id = myPatientDao.create(p).getId().toUnqualifiedVersionless().getValue(); - SearchParameterMap map= new SearchParameterMap(); + SearchParameterMap map = new SearchParameterMap(); map.setLoadSynchronous(true); map.add(Patient.SP_FAMILY, new StringParam("김")); assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(id)); - map= new SearchParameterMap(); + map = new SearchParameterMap(); map.setLoadSynchronous(true); map.add(Patient.SP_GIVEN, new StringParam("준")); assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(id)); - map= new SearchParameterMap(); + map = new SearchParameterMap(); map.setLoadSynchronous(true); map.add(Patient.SP_GIVEN, new StringParam("준수")); assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(id)); - map= new SearchParameterMap(); + map = new SearchParameterMap(); map.setLoadSynchronous(true); map.add(Patient.SP_GIVEN, new StringParam("수")); // rightmost character only assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty()); @@ -60,7 +58,7 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test { } @Test - public void testCreateWithUuidResourceStrategy() throws Exception { + public void testCreateWithUuidResourceStrategy() { myDaoConfig.setResourceServerIdStrategy(DaoConfig.IdStrategyEnum.UUID); Patient p = new Patient(); @@ -110,26 +108,6 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test { assertThat(output.getEntry().get(1).getResponse().getLocation(), matchesPattern("Patient/[a-z0-9]{8}-.*")); - } - - @Test - public void testWritesPerformMinimalSqlStatements() { - Patient p = new Patient(); - p.addIdentifier().setSystem("sys1").setValue("val1"); - p.addIdentifier().setSystem("sys2").setValue("val2"); - - ourLog.info("** About to perform write"); - new ThreadQueryCountHolder().getOrCreateQueryCount("").setInsert(0); - new ThreadQueryCountHolder().getOrCreateQueryCount("").setUpdate(0); - - myPatientDao.create(p); - - ourLog.info("** Done performing write"); - - ourLog.info("Inserts: {}", new ThreadQueryCountHolder().getOrCreateQueryCount("").getInsert()); - ourLog.info("Updates: {}", new ThreadQueryCountHolder().getOrCreateQueryCount("").getUpdate()); - - } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java index 749492d8def..dfcbc1c61a8 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java @@ -1,14 +1,19 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.dao.SearchParameterMap; +import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.util.TestUtil; +import net.ttddyy.dsproxy.QueryCount; import net.ttddyy.dsproxy.QueryCountHolder; +import net.ttddyy.dsproxy.listener.SingleQueryCountHolder; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.Patient; import org.junit.After; import org.junit.AfterClass; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; import static org.junit.Assert.assertEquals; @@ -18,6 +23,8 @@ import static org.junit.Assert.assertEquals; }) public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4QueryCountTest.class); + @Autowired + private SingleQueryCountHolder myCountHolder; @After public void afterResetDao() { @@ -25,22 +32,87 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test { myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields()); } + @Test + public void testWritesPerformMinimalSqlStatements() { + Patient p = new Patient(); + p.addIdentifier().setSystem("sys1").setValue("val1"); + p.addIdentifier().setSystem("sys2").setValue("val2"); + + ourLog.info("** About to perform write"); + myCountHolder.clear(); + + IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless(); + + ourLog.info("** Done performing write"); + + assertEquals(6, getQueryCount().getInsert()); + assertEquals(0, getQueryCount().getUpdate()); + + /* + * Not update the value + */ + + p = new Patient(); + p.setId(id); + p.addIdentifier().setSystem("sys1").setValue("val3"); + p.addIdentifier().setSystem("sys2").setValue("val4"); + + ourLog.info("** About to perform write 2"); + myCountHolder.clear(); + + myPatientDao.update(p).getId().toUnqualifiedVersionless(); + + ourLog.info("** Done performing write 2"); + + assertEquals(2, getQueryCount().getInsert()); + assertEquals(1, getQueryCount().getUpdate()); + assertEquals(1, getQueryCount().getDelete()); + } + + @Test + public void testSearch() { + + for (int i = 0; i < 20; i++) { + Patient p = new Patient(); + p.addIdentifier().setSystem("sys1").setValue("val" + i); + myPatientDao.create(p); + } + + myCountHolder.clear(); + + ourLog.info("** About to perform search"); + IBundleProvider search = myPatientDao.search(new SearchParameterMap()); + ourLog.info("** About to retrieve resources"); + search.getResources(0, 20); + ourLog.info("** Done retrieving resources"); + + assertEquals(4, getQueryCount().getSelect()); + assertEquals(2, getQueryCount().getInsert()); + assertEquals(1, getQueryCount().getUpdate()); + assertEquals(0, getQueryCount().getDelete()); + + } + + private QueryCount getQueryCount() { + return myCountHolder.getQueryCountMap().get(""); + } + @Test public void testCreateClientAssignedId() { myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED); - QueryCountHolder.clear(); + myCountHolder.clear(); ourLog.info("** Starting Update Non-Existing resource with client assigned ID"); Patient p = new Patient(); p.setId("A"); p.getPhotoFirstRep().setCreationElement(new DateTimeType("2011")); // non-indexed field myPatientDao.update(p).getId().toUnqualifiedVersionless(); - assertEquals(1, QueryCountHolder.getGrandTotal().getSelect()); - assertEquals(4, QueryCountHolder.getGrandTotal().getInsert()); - assertEquals(0, QueryCountHolder.getGrandTotal().getDelete()); + assertEquals(1, getQueryCount().getSelect()); + assertEquals(4, getQueryCount().getInsert()); + assertEquals(0, getQueryCount().getDelete()); // Because of the forced ID's bidirectional link HFJ_RESOURCE <-> HFJ_FORCED_ID - assertEquals(1, QueryCountHolder.getGrandTotal().getUpdate()); + assertEquals(1, getQueryCount().getUpdate()); runInTransaction(() -> { assertEquals(1, myResourceTableDao.count()); assertEquals(1, myResourceHistoryTableDao.count()); @@ -50,17 +122,17 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test { // Ok how about an update - QueryCountHolder.clear(); + myCountHolder.clear(); ourLog.info("** Starting Update Existing resource with client assigned ID"); p = new Patient(); p.setId("A"); p.getPhotoFirstRep().setCreationElement(new DateTimeType("2012")); // non-indexed field myPatientDao.update(p).getId().toUnqualifiedVersionless(); - assertEquals(5, QueryCountHolder.getGrandTotal().getSelect()); - assertEquals(1, QueryCountHolder.getGrandTotal().getInsert()); - assertEquals(0, QueryCountHolder.getGrandTotal().getDelete()); - assertEquals(1, QueryCountHolder.getGrandTotal().getUpdate()); + assertEquals(5, getQueryCount().getSelect()); + assertEquals(1, getQueryCount().getInsert()); + assertEquals(0, getQueryCount().getDelete()); + assertEquals(1, getQueryCount().getUpdate()); runInTransaction(() -> { assertEquals(1, myResourceTableDao.count()); assertEquals(2, myResourceHistoryTableDao.count()); @@ -75,24 +147,24 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test { public void testOneRowPerUpdate() { myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED); - QueryCountHolder.clear(); + myCountHolder.clear(); Patient p = new Patient(); p.getPhotoFirstRep().setCreationElement(new DateTimeType("2011")); // non-indexed field IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless(); - assertEquals(3, QueryCountHolder.getGrandTotal().getInsert()); + assertEquals(3, getQueryCount().getInsert()); runInTransaction(() -> { assertEquals(1, myResourceTableDao.count()); assertEquals(1, myResourceHistoryTableDao.count()); }); - QueryCountHolder.clear(); + myCountHolder.clear(); p = new Patient(); p.setId(id); p.getPhotoFirstRep().setCreationElement(new DateTimeType("2012")); // non-indexed field myPatientDao.update(p).getId().toUnqualifiedVersionless(); - assertEquals(1, QueryCountHolder.getGrandTotal().getInsert()); + assertEquals(1, getQueryCount().getInsert()); runInTransaction(() -> { assertEquals(1, myResourceTableDao.count()); assertEquals(2, myResourceHistoryTableDao.count()); @@ -101,6 +173,34 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test { } + @Test + public void testUpdateReusesIndexes() { + myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED); + + myCountHolder.clear(); + + Patient pt = new Patient(); + pt.setActive(true); + pt.addName().setFamily("FAMILY1").addGiven("GIVEN1A").addGiven("GIVEN1B"); + IIdType id = myPatientDao.create(pt).getId().toUnqualifiedVersionless(); + + ourLog.info("Now have {} deleted", getQueryCount().getDelete()); + ourLog.info("Now have {} inserts", getQueryCount().getInsert()); + myCountHolder.clear(); + + ourLog.info("** About to update"); + + pt.setId(id); + pt.getNameFirstRep().addGiven("GIVEN1C"); + myPatientDao.update(pt); + + ourLog.info("Now have {} deleted", getQueryCount().getDelete()); + ourLog.info("Now have {} inserts", getQueryCount().getInsert()); + assertEquals(0, getQueryCount().getDelete()); + assertEquals(2, getQueryCount().getInsert()); + } + + @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java new file mode 100644 index 00000000000..9c3395469fc --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java @@ -0,0 +1,3343 @@ +package ca.uhn.fhir.jpa.dao.r4; + +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.dao.SearchParameterMap; +import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum; +import ca.uhn.fhir.jpa.entity.*; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.TemporalPrecisionEnum; +import ca.uhn.fhir.parser.StrictErrorHandler; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; +import ca.uhn.fhir.util.TestUtil; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Bundle.BundleType; +import org.hl7.fhir.r4.model.Bundle.HTTPVerb; +import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem; +import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; +import org.hl7.fhir.r4.model.Observation.ObservationStatus; +import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType; +import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus; +import org.junit.*; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.util.*; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; + +@SuppressWarnings({"unchecked", "Duplicates"}) +public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchNoHashesTest.class); + + @After + public void afterResetSearchSize() { + myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); + myDaoConfig.setFetchSizeDefaultMaximum(new DaoConfig().getFetchSizeDefaultMaximum()); + myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches()); + myDaoConfig.setDisableHashBasedSearches(false); + } + + @Before + public void beforeInitialize() { + myDaoConfig.setReuseCachedSearchResultsForMillis(null); + myDaoConfig.setDisableHashBasedSearches(true); + } + + @Test + public void testChainWithMultipleTypePossibilities() { + + Patient sub1 = new Patient(); + sub1.setActive(true); + sub1.addIdentifier().setSystem("foo").setValue("bar"); + String sub1Id = myPatientDao.create(sub1).getId().toUnqualifiedVersionless().getValue(); + + Group sub2 = new Group(); + sub2.setActive(true); + sub2.addIdentifier().setSystem("foo").setValue("bar"); + String sub2Id = myGroupDao.create(sub2).getId().toUnqualifiedVersionless().getValue(); + + Encounter enc1 = new Encounter(); + enc1.getSubject().setReference(sub1Id); + String enc1Id = myEncounterDao.create(enc1).getId().toUnqualifiedVersionless().getValue(); + + Encounter enc2 = new Encounter(); + enc2.getSubject().setReference(sub2Id); + String enc2Id = myEncounterDao.create(enc2).getId().toUnqualifiedVersionless().getValue(); + + List ids; + SearchParameterMap map; + IBundleProvider results; + + map = new SearchParameterMap(); + map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject", "foo|bar").setChain("identifier")); + results = myEncounterDao.search(map); + ids = toUnqualifiedVersionlessIdValues(results); + assertThat(ids, hasItems(enc1Id, enc2Id)); + + map = new SearchParameterMap(); + map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject:Patient", "foo|bar").setChain("identifier")); + results = myEncounterDao.search(map); + ids = toUnqualifiedVersionlessIdValues(results); + assertThat(ids, hasItems(enc1Id)); + + map = new SearchParameterMap(); + map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject:Group", "foo|bar").setChain("identifier")); + results = myEncounterDao.search(map); + ids = toUnqualifiedVersionlessIdValues(results); + assertThat(ids, hasItems(enc2Id)); + + map = new SearchParameterMap(); + map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject", "04823543").setChain("identifier")); + results = myEncounterDao.search(map); + ids = toUnqualifiedVersionlessIdValues(results); + assertThat(ids, empty()); + } + + /** + * See #441 + */ + @Test + public void testChainedMedication() { + Medication medication = new Medication(); + medication.getCode().addCoding().setSystem("SYSTEM").setCode("04823543"); + IIdType medId = myMedicationDao.create(medication).getId().toUnqualifiedVersionless(); + + MedicationAdministration ma = new MedicationAdministration(); + ma.setMedication(new Reference(medId)); + IIdType moId = myMedicationAdministrationDao.create(ma).getId().toUnqualified(); + + SearchParameterMap map = new SearchParameterMap(); + map.add(MedicationAdministration.SP_MEDICATION, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().add(new ReferenceParam("code", "04823543")))); + IBundleProvider results = myMedicationAdministrationDao.search(map); + List ids = toUnqualifiedIdValues(results); + + assertThat(ids, contains(moId.getValue())); + } + + @Test + public void testEmptyChain() { + + SearchParameterMap map = new SearchParameterMap(); + map.add(Encounter.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().add(new ReferenceParam("subject", "04823543").setChain("identifier")))); + IBundleProvider results = myMedicationAdministrationDao.search(map); + List ids = toUnqualifiedIdValues(results); + + assertThat(ids, empty()); + } + + /** + * See #1053 + */ + @Test + public void testLastUpdateShouldntApplyToIncludes() { + SearchParameterMap map; + List ids; + + Date beforeAll = new Date(); + ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100); + + Organization org = new Organization(); + org.setName("O1"); + org.setId("O1"); + myOrganizationDao.update(org); + ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100); + + Date beforePatient = new Date(); + ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100); + + Patient p = new Patient(); + p.setId("P1"); + p.setActive(true); + p.setManagingOrganization(new Reference("Organization/O1")); + myPatientDao.update(p); + + ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100); + Date afterAll = new Date(); + + // Search with between date (should still return Organization even though + // it was created before that date, since it's an include) + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(beforePatient)); + map.addInclude(Patient.INCLUDE_ORGANIZATION); + ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); + assertThat(ids, contains("Patient/P1", "Organization/O1")); + + // Search before everything + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(beforeAll)); + map.addInclude(Patient.INCLUDE_ORGANIZATION); + ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); + assertThat(ids, contains("Patient/P1", "Organization/O1")); + + // Search after everything + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(afterAll)); + map.addInclude(Patient.INCLUDE_ORGANIZATION); + ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); + assertThat(ids, empty()); + + } + + /** + * See #1053 + *

+ * Note that I don't know that _lastUpdate actually should apply to reverse includes. The + * spec doesn't say one way or ther other, but it seems like sensible behaviour to me. + *

+ * Definitely the $everything operation depends on this behaviour, so if we change it + * we need to account for the everything operation... + */ + @Test + public void testLastUpdateShouldApplyToReverseIncludes() { + SearchParameterMap map; + List ids; + + // This gets updated in a sec.. + Organization org = new Organization(); + org.setActive(false); + org.setId("O1"); + myOrganizationDao.update(org); + + Date beforeAll = new Date(); + ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100); + + Patient p = new Patient(); + p.setId("P1"); + p.setActive(true); + p.setManagingOrganization(new Reference("Organization/O1")); + myPatientDao.update(p); + + ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100); + + Date beforeOrg = new Date(); + ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100); + + org = new Organization(); + org.setActive(true); + org.setId("O1"); + myOrganizationDao.update(org); + + ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100); + Date afterAll = new Date(); + + // Everything should come back + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(beforeAll)); + map.addRevInclude(Patient.INCLUDE_ORGANIZATION); + ids = toUnqualifiedVersionlessIdValues(myOrganizationDao.search(map)); + assertThat(ids, contains("Organization/O1", "Patient/P1")); + + // Search before everything + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(beforeOrg)); + map.addInclude(Patient.INCLUDE_ORGANIZATION); + ids = toUnqualifiedVersionlessIdValues(myOrganizationDao.search(map)); + assertThat(ids, contains("Organization/O1")); + + // Search after everything + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(afterAll)); + map.addInclude(Patient.INCLUDE_ORGANIZATION); + ids = toUnqualifiedVersionlessIdValues(myOrganizationDao.search(map)); + assertThat(ids, empty()); + + } + + @Test + public void testEverythingTimings() { + String methodName = "testEverythingTimings"; + + Organization org = new Organization(); + org.setName(methodName); + IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + + Medication med = new Medication(); + med.getCode().setText(methodName); + IIdType medId = myMedicationDao.create(med, mySrd).getId().toUnqualifiedVersionless(); + + Patient pat = new Patient(); + pat.addAddress().addLine(methodName); + pat.getManagingOrganization().setReferenceElement(orgId); + IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless(); + + Patient pat2 = new Patient(); + pat2.addAddress().addLine(methodName + "2"); + pat2.getManagingOrganization().setReferenceElement(orgId); + IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless(); + + MedicationRequest mo = new MedicationRequest(); + mo.getSubject().setReferenceElement(patId); + mo.setMedication(new Reference(medId)); + IIdType moId = myMedicationRequestDao.create(mo, mySrd).getId().toUnqualifiedVersionless(); + + HttpServletRequest request = mock(HttpServletRequest.class); + IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, mySrd); + assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2)); + + request = mock(HttpServletRequest.class); + resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, mySrd); + assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId)); + } + + /** + * Per message from David Hay on Skype + */ + @Test + @Ignore + public void testEverythingWithLargeSet() throws Exception { + myFhirCtx.setParserErrorHandler(new StrictErrorHandler()); + + String inputString = IOUtils.toString(getClass().getResourceAsStream("/david_big_bundle.json"), StandardCharsets.UTF_8); + Bundle inputBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, inputString); + inputBundle.setType(BundleType.TRANSACTION); + + Set allIds = new TreeSet(); + for (BundleEntryComponent nextEntry : inputBundle.getEntry()) { + nextEntry.getRequest().setMethod(HTTPVerb.PUT); + nextEntry.getRequest().setUrl(nextEntry.getResource().getId()); + allIds.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue()); + } + + mySystemDao.transaction(mySrd, inputBundle); + + SearchParameterMap map = new SearchParameterMap(); + map.setEverythingMode(EverythingModeEnum.PATIENT_INSTANCE); + IPrimitiveType count = new IntegerType(1000); + IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, mySrd); + + TreeSet ids = new TreeSet(toUnqualifiedVersionlessIdValues(everything)); + assertThat(ids, hasItem("List/A161444")); + assertThat(ids, hasItem("List/A161468")); + assertThat(ids, hasItem("List/A161500")); + + ourLog.info("Expected {} - {}", allIds.size(), allIds); + ourLog.info("Actual {} - {}", ids.size(), ids); + assertEquals(allIds, ids); + + ids = new TreeSet(); + for (int i = 0; i < everything.size(); i++) { + for (IBaseResource next : everything.getResources(i, i + 1)) { + ids.add(next.getIdElement().toUnqualifiedVersionless().getValue()); + } + } + assertThat(ids, hasItem("List/A161444")); + assertThat(ids, hasItem("List/A161468")); + assertThat(ids, hasItem("List/A161500")); + + ourLog.info("Expected {} - {}", allIds.size(), allIds); + ourLog.info("Actual {} - {}", ids.size(), ids); + assertEquals(allIds, ids); + + } + + @SuppressWarnings("unused") + @Test + public void testHasAndHas() { + Patient p1 = new Patient(); + p1.setActive(true); + IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless(); + + Patient p2 = new Patient(); + p2.setActive(true); + IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless(); + + Observation p1o1 = new Observation(); + p1o1.setStatus(ObservationStatus.FINAL); + p1o1.getSubject().setReferenceElement(p1id); + IIdType p1o1id = myObservationDao.create(p1o1).getId().toUnqualifiedVersionless(); + + Observation p1o2 = new Observation(); + p1o2.setEffective(new DateTimeType("2001-01-01")); + p1o2.getSubject().setReferenceElement(p1id); + IIdType p1o2id = myObservationDao.create(p1o2).getId().toUnqualifiedVersionless(); + + Observation p2o1 = new Observation(); + p2o1.setStatus(ObservationStatus.FINAL); + p2o1.getSubject().setReferenceElement(p2id); + IIdType p2o1id = myObservationDao.create(p2o1).getId().toUnqualifiedVersionless(); + + SearchParameterMap map = new SearchParameterMap(); + + HasAndListParam hasAnd = new HasAndListParam(); + hasAnd.addValue(new HasOrListParam().add(new HasParam("Observation", "subject", "status", "final"))); + hasAnd.addValue(new HasOrListParam().add(new HasParam("Observation", "subject", "date", "2001-01-01"))); + map.add("_has", hasAnd); + List actual = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); + assertThat(actual, containsInAnyOrder(p1id.getValue())); + + } + + @Test + public void testHasParameter() { + IIdType pid0; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester").addGiven("Joe"); + pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester").addGiven("Joe"); + myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("FOO"); + obs.getSubject().setReferenceElement(pid0); + myObservationDao.create(obs, mySrd); + } + { + Device device = new Device(); + device.addIdentifier().setValue("DEVICEID"); + IIdType devId = myDeviceDao.create(device, mySrd).getId().toUnqualifiedVersionless(); + + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("NOLINK"); + obs.setDevice(new Reference(devId)); + myObservationDao.create(obs, mySrd); + } + + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_has", new HasParam("Observation", "subject", "identifier", "urn:system|FOO")); + assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(pid0.getValue())); + + // No targets exist + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_has", new HasParam("Observation", "subject", "identifier", "urn:system|UNKNOWN")); + assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), empty()); + + // Target exists but doesn't link to us + params = new SearchParameterMap(); + params.add("_has", new HasParam("Observation", "subject", "identifier", "urn:system|NOLINK")); + assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), empty()); + } + + @Test + public void testHasParameterChained() { + IIdType pid0; + { + Device device = new Device(); + device.addIdentifier().setSystem("urn:system").setValue("DEVICEID"); + IIdType devId = myDeviceDao.create(device, mySrd).getId().toUnqualifiedVersionless(); + + Patient patient = new Patient(); + patient.setGender(AdministrativeGender.MALE); + pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("FOO"); + obs.setDevice(new Reference(devId)); + obs.setSubject(new Reference(pid0)); + myObservationDao.create(obs, mySrd).getId(); + } + + SearchParameterMap params; + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_has", new HasParam("Observation", "subject", "device.identifier", "urn:system|DEVICEID")); + assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(pid0.getValue())); + + // No targets exist + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_has", new HasParam("Observation", "subject", "identifier", "urn:system|UNKNOWN")); + assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), empty()); + + // Target exists but doesn't link to us + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_has", new HasParam("Observation", "subject", "identifier", "urn:system|NOLINK")); + assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), empty()); + } + + @Test + public void testHasParameterInvalidResourceType() { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_has", new HasParam("Observation__", "subject", "identifier", "urn:system|FOO")); + try { + myPatientDao.search(params); + fail(); + } catch (InvalidRequestException e) { + assertEquals("Invalid resource type: Observation__", e.getMessage()); + } + } + + @Test + public void testHasParameterInvalidSearchParam() { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_has", new HasParam("Observation", "subject", "IIIIDENFIEYR", "urn:system|FOO")); + try { + myPatientDao.search(params); + fail(); + } catch (InvalidRequestException e) { + assertEquals("Unknown parameter name: Observation:IIIIDENFIEYR", e.getMessage()); + } + } + + @Test + public void testHasParameterInvalidTargetPath() { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_has", new HasParam("Observation", "soooooobject", "identifier", "urn:system|FOO")); + try { + myPatientDao.search(params); + fail(); + } catch (InvalidRequestException e) { + assertEquals("Unknown parameter name: Observation:soooooobject", e.getMessage()); + } + } + + @Test + public void testIncludeLinkedObservations() { + + DiagnosticReport dr = new DiagnosticReport(); + dr.setId("DiagnosticReport/DR"); + dr.setStatus(DiagnosticReport.DiagnosticReportStatus.FINAL); + + Observation parentObs = new Observation(); + parentObs.setStatus(ObservationStatus.FINAL); + parentObs.setId("Observation/parentObs"); + + Observation childObs = new Observation(); + childObs.setId("Observation/childObs"); + childObs.setStatus(ObservationStatus.FINAL); + + dr.addResult().setReference("Observation/parentObs").setResource(parentObs); + parentObs.addHasMember(new Reference(childObs).setReference("Observation/childObs")); + childObs.addDerivedFrom(new Reference(parentObs).setReference("Observation/parentObs")); + + Bundle input = new Bundle(); + input.setType(BundleType.TRANSACTION); + input.addEntry() + .setResource(dr) + .getRequest().setMethod(HTTPVerb.PUT).setUrl(dr.getId()); + input.addEntry() + .setResource(parentObs) + .getRequest().setMethod(HTTPVerb.PUT).setUrl(parentObs.getId()); + input.addEntry() + .setResource(childObs) + .getRequest().setMethod(HTTPVerb.PUT).setUrl(childObs.getId()); + mySystemDao.transaction(mySrd, input); + + SearchParameterMap params = new SearchParameterMap(); + params.add("_id", new TokenParam(null, "DR")); + params.addInclude(new Include("DiagnosticReport:subject").setRecurse(true)); + params.addInclude(new Include("DiagnosticReport:result").setRecurse(true)); + params.addInclude(Observation.INCLUDE_HAS_MEMBER.setRecurse(true)); + + IBundleProvider result = myDiagnosticReportDao.search(params); + List resultIds = toUnqualifiedVersionlessIdValues(result); + assertThat(resultIds, containsInAnyOrder("DiagnosticReport/DR", "Observation/parentObs", "Observation/childObs")); + + } + + @Test + public void testIndexNoDuplicatesDate() { + Encounter order = new Encounter(); + order.addLocation().getPeriod().setStartElement(new DateTimeType("2011-12-12T11:12:12Z")).setEndElement(new DateTimeType("2011-12-12T11:12:12Z")); + order.addLocation().getPeriod().setStartElement(new DateTimeType("2011-12-12T11:12:12Z")).setEndElement(new DateTimeType("2011-12-12T11:12:12Z")); + order.addLocation().getPeriod().setStartElement(new DateTimeType("2011-12-12T11:12:12Z")).setEndElement(new DateTimeType("2011-12-12T11:12:12Z")); + order.addLocation().getPeriod().setStartElement(new DateTimeType("2011-12-11T11:12:12Z")).setEndElement(new DateTimeType("2011-12-11T11:12:12Z")); + order.addLocation().getPeriod().setStartElement(new DateTimeType("2011-12-11T11:12:12Z")).setEndElement(new DateTimeType("2011-12-11T11:12:12Z")); + order.addLocation().getPeriod().setStartElement(new DateTimeType("2011-12-11T11:12:12Z")).setEndElement(new DateTimeType("2011-12-11T11:12:12Z")); + + IIdType id = myEncounterDao.create(order, mySrd).getId().toUnqualifiedVersionless(); + + List actual = toUnqualifiedVersionlessIds( + myEncounterDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Encounter.SP_LOCATION_PERIOD, new DateParam("2011-12-12T11:12:12Z")))); + assertThat(actual, contains(id)); + + runInTransaction(() -> { + Class type = ResourceIndexedSearchParamDate.class; + List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i WHERE i.myMissing = false", type).getResultList(); + ourLog.info(toStringMultiline(results)); + assertEquals(2, results.size()); + }); + } + + @Test + public void testIndexNoDuplicatesNumber() { + final RiskAssessment res = new RiskAssessment(); + res.addPrediction().setProbability(new DecimalType("1.0")); + res.addPrediction().setProbability(new DecimalType("1.0")); + res.addPrediction().setProbability(new DecimalType("1.0")); + res.addPrediction().setProbability(new DecimalType("2.0")); + res.addPrediction().setProbability(new DecimalType("2.0")); + res.addPrediction().setProbability(new DecimalType("2.0")); + res.addPrediction().setProbability(new DecimalType("2.0")); + + IIdType id = myRiskAssessmentDao.create(res, mySrd).getId().toUnqualifiedVersionless(); + + List actual = toUnqualifiedVersionlessIds(myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam("1.0")))); + assertThat(actual, contains(id)); + actual = toUnqualifiedVersionlessIds(myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam("99.0")))); + assertThat(actual, empty()); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus status) { + ResourceTable resource = myResourceTableDao.findAll().iterator().next(); + assertEquals("RiskAssessment", resource.getResourceType()); + + Class type = ResourceIndexedSearchParamNumber.class; + List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList(); + ourLog.info(toStringMultiline(results)); + + ResourceIndexedSearchParamNumber expect0 = new ResourceIndexedSearchParamNumber(RiskAssessment.SP_PROBABILITY, new BigDecimal("1.00")); + expect0.setResource(resource); + expect0.calculateHashes(); + ResourceIndexedSearchParamNumber expect1 = new ResourceIndexedSearchParamNumber(RiskAssessment.SP_PROBABILITY, new BigDecimal("2.00")); + expect1.setResource(resource); + expect1.calculateHashes(); + + assertThat("Got: \"" + results.toString()+"\"", results, containsInAnyOrder(expect0, expect1)); + } + }); + } + + @Test + public void testIndexNoDuplicatesQuantity() { + Substance res = new Substance(); + res.addInstance().getQuantity().setSystem("http://foo").setCode("UNIT").setValue(123); + res.addInstance().getQuantity().setSystem("http://foo").setCode("UNIT").setValue(123); + res.addInstance().getQuantity().setSystem("http://foo2").setCode("UNIT2").setValue(1232); + res.addInstance().getQuantity().setSystem("http://foo2").setCode("UNIT2").setValue(1232); + + IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless(); + + runInTransaction(() -> { + Class type = ResourceIndexedSearchParamQuantity.class; + List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList(); + ourLog.info(toStringMultiline(results)); + assertEquals(2, results.size()); + }); + + List actual = toUnqualifiedVersionlessIds( + mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, "http://foo", "UNIT")))); + assertThat(actual, contains(id)); + } + + @Test + public void testIndexNoDuplicatesReference() { + ServiceRequest pr = new ServiceRequest(); + pr.setId("ServiceRequest/somepract"); + pr.getAuthoredOnElement().setValue(new Date()); + myServiceRequestDao.update(pr, mySrd); + ServiceRequest pr2 = new ServiceRequest(); + pr2.setId("ServiceRequest/somepract2"); + pr2.getAuthoredOnElement().setValue(new Date()); + myServiceRequestDao.update(pr2, mySrd); + + ServiceRequest res = new ServiceRequest(); + res.addReplaces(new Reference("ServiceRequest/somepract")); + res.addReplaces(new Reference("ServiceRequest/somepract")); + res.addReplaces(new Reference("ServiceRequest/somepract2")); + res.addReplaces(new Reference("ServiceRequest/somepract2")); + + final IIdType id = myServiceRequestDao.create(res, mySrd).getId().toUnqualifiedVersionless(); + + TransactionTemplate txTemplate = new TransactionTemplate(myTransactionMgr); + txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); + txTemplate.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theArg0) { + Class type = ResourceLink.class; + List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList(); + ourLog.info(toStringMultiline(results)); + assertEquals(2, results.size()); + List actual = toUnqualifiedVersionlessIds( + myServiceRequestDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ServiceRequest.SP_REPLACES, new ReferenceParam("ServiceRequest/somepract")))); + assertThat(actual, contains(id)); + } + }); + + } + + @Test + public void testIndexNoDuplicatesString() { + Patient p = new Patient(); + p.addAddress().addLine("123 Fake Street"); + p.addAddress().addLine("123 Fake Street"); + p.addAddress().addLine("123 Fake Street"); + p.addAddress().addLine("456 Fake Street"); + p.addAddress().addLine("456 Fake Street"); + p.addAddress().addLine("456 Fake Street"); + + IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); + + runInTransaction(() -> { + Class type = ResourceIndexedSearchParamString.class; + List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i WHERE i.myMissing = false", type).getResultList(); + ourLog.info(toStringMultiline(results)); + assertEquals(2, results.size()); + }); + + List actual = toUnqualifiedVersionlessIds(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_ADDRESS, new StringParam("123 Fake Street")))); + assertThat(actual, contains(id)); + } + + @Test + public void testIndexNoDuplicatesToken() { + Patient res = new Patient(); + res.addIdentifier().setSystem("http://foo1").setValue("123"); + res.addIdentifier().setSystem("http://foo1").setValue("123"); + res.addIdentifier().setSystem("http://foo2").setValue("1234"); + res.addIdentifier().setSystem("http://foo2").setValue("1234"); + + IIdType id = myPatientDao.create(res, mySrd).getId().toUnqualifiedVersionless(); + + runInTransaction(() -> { + Class type = ResourceIndexedSearchParamToken.class; + List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i WHERE i.myMissing = false", type).getResultList(); + ourLog.info(toStringMultiline(results)); + // This is 3 for now because the FluentPath for Patient:deceased adds a value.. this should + // be corrected at some point, and we'll then drop back down to 2 + assertEquals(3, results.size()); + }); + + + List actual = toUnqualifiedVersionlessIds(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_IDENTIFIER, new TokenParam("http://foo1", "123")))); + assertThat(actual, contains(id)); + } + + @Test + public void testIndexNoDuplicatesUri() { + ValueSet res = new ValueSet(); + res.getCompose().addInclude().setSystem("http://foo"); + res.getCompose().addInclude().setSystem("http://bar"); + res.getCompose().addInclude().setSystem("http://foo"); + res.getCompose().addInclude().setSystem("http://bar"); + res.getCompose().addInclude().setSystem("http://foo"); + res.getCompose().addInclude().setSystem("http://bar"); + + IIdType id = myValueSetDao.create(res, mySrd).getId().toUnqualifiedVersionless(); + + runInTransaction(() -> { + Class type = ResourceIndexedSearchParamUri.class; + List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i WHERE i.myMissing = false", type).getResultList(); + ourLog.info(toStringMultiline(results)); + assertEquals(2, results.size()); + }); + + List actual = toUnqualifiedVersionlessIds(myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_REFERENCE, new UriParam("http://foo")))); + assertThat(actual, contains(id)); + } + + /** + * #454 + */ + @Test + public void testIndexWithUtf8Chars() throws IOException { + String input = IOUtils.toString(getClass().getResourceAsStream("/bug454_utf8.json"), StandardCharsets.UTF_8); + + CodeSystem cs = (CodeSystem) myFhirCtx.newJsonParser().parseResource(input); + myCodeSystemDao.create(cs); + } + + @Test + public void testReturnOnlyCorrectResourceType() { + ValueSet vsRes = new ValueSet(); + vsRes.setUrl("http://foo"); + String vsId = myValueSetDao.create(vsRes).getId().toUnqualifiedVersionless().getValue(); + + CodeSystem csRes = new CodeSystem(); + csRes.setUrl("http://bar"); + String csId = myCodeSystemDao.create(csRes).getId().toUnqualifiedVersionless().getValue(); + + SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true); + map.add(ValueSet.SP_URL, new UriParam("http://foo")); + List actual = toUnqualifiedVersionlessIdValues(myValueSetDao.search(map)); + assertThat(actual, contains(vsId)); + + map = new SearchParameterMap().setLoadSynchronous(true); + map.add(ValueSet.SP_URL, new UriParam("http://bar")); + actual = toUnqualifiedVersionlessIdValues(myCodeSystemDao.search(map)); + assertThat(actual, contains(csId)); + } + + @Test + public void testSearchAll() { + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester").addGiven("Joe"); + myPatientDao.create(patient, mySrd); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("Tester").addGiven("John"); + myPatientDao.create(patient, mySrd); + } + + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + List patients = toList(myPatientDao.search(params)); + assertTrue(patients.size() >= 2); + } + + @Test + public void testSearchByIdParam() { + String id1; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue(); + } + String id2; + { + Organization patient = new Organization(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id2 = myOrganizationDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue(); + } + + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(id1)); + + params = new SearchParameterMap(); + params.add("_id", new StringParam(id1)); + assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(id1)); + + params = new SearchParameterMap(); + params.add("_id", new StringParam("9999999999999999")); + assertEquals(0, toList(myPatientDao.search(params)).size()); + + params = new SearchParameterMap(); + params.add("_id", new StringParam(id2)); + assertEquals(0, toList(myPatientDao.search(params)).size()); + + } + + @Test + public void testSearchByIdParamAnd() { + IIdType id1; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType id2; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + SearchParameterMap params; + StringAndListParam param; + + params = new SearchParameterMap(); + param = new StringAndListParam(); + param.addAnd(new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id2.getIdPart()))); + param.addAnd(new StringOrListParam().addOr(new StringParam(id1.getIdPart()))); + params.add("_id", param); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); + + params = new SearchParameterMap(); + param = new StringAndListParam(); + param.addAnd(new StringOrListParam().addOr(new StringParam(id2.getIdPart()))); + param.addAnd(new StringOrListParam().addOr(new StringParam(id1.getIdPart()))); + params.add("_id", param); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty()); + + params = new SearchParameterMap(); + param = new StringAndListParam(); + param.addAnd(new StringOrListParam().addOr(new StringParam(id2.getIdPart()))); + param.addAnd(new StringOrListParam().addOr(new StringParam("9999999999999"))); + params.add("_id", param); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty()); + + params = new SearchParameterMap(); + param = new StringAndListParam(); + param.addAnd(new StringOrListParam().addOr(new StringParam("9999999999999"))); + param.addAnd(new StringOrListParam().addOr(new StringParam(id2.getIdPart()))); + params.add("_id", param); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty()); + + } + + @Test + public void testSearchByIdParamOr() { + IIdType id1; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + long betweenTime = System.currentTimeMillis(); + IIdType id2; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + SearchParameterMap params = new SearchParameterMap(); + params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id2.getIdPart()))); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1, id2)); + + params = new SearchParameterMap(); + params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id1.getIdPart()))); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); + + params = new SearchParameterMap(); + params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam("999999999999"))); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); + + // With lastupdated + + params = new SearchParameterMap(); + params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id2.getIdPart()))); + params.setLastUpdated(new DateRangeParam(new Date(betweenTime), null)); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id2)); + + } + + @Test + public void testSearchByIdParamWrongType() { + IIdType id1; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType id2; + { + Organization patient = new Organization(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id2 = myOrganizationDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + SearchParameterMap params = new SearchParameterMap(); + params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id2.getIdPart()))); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); + + } + + @Test + public void testSearchCode() { + Subscription subs = new Subscription(); + subs.setStatus(SubscriptionStatus.ACTIVE); + subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET); + subs.setCriteria("Observation?"); + IIdType id = mySubscriptionDao.create(subs, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap params = new SearchParameterMap(); + assertThat(toUnqualifiedVersionlessIds(mySubscriptionDao.search(params)), contains(id)); + + params = new SearchParameterMap(); + params.add(Subscription.SP_TYPE, new TokenParam(null, SubscriptionChannelType.WEBSOCKET.toCode())); + params.add(Subscription.SP_STATUS, new TokenParam(null, SubscriptionStatus.ACTIVE.toCode())); + assertThat(toUnqualifiedVersionlessIds(mySubscriptionDao.search(params)), contains(id)); + + params = new SearchParameterMap(); + params.add(Subscription.SP_TYPE, new TokenParam(null, SubscriptionChannelType.WEBSOCKET.toCode())); + params.add(Subscription.SP_STATUS, new TokenParam(null, SubscriptionStatus.ACTIVE.toCode() + "2")); + assertThat(toUnqualifiedVersionlessIds(mySubscriptionDao.search(params)), empty()); + + // Wrong param + params = new SearchParameterMap(); + params.add(Subscription.SP_STATUS, new TokenParam(null, SubscriptionChannelType.WEBSOCKET.toCode())); + assertThat(toUnqualifiedVersionlessIds(mySubscriptionDao.search(params)), empty()); + } + + @Test + public void testSearchCompositeParam() { + Observation o1 = new Observation(); + o1.getCode().addCoding().setSystem("foo").setCode("testSearchCompositeParamN01"); + o1.setValue(new StringType("testSearchCompositeParamS01")); + IIdType id1 = myObservationDao.create(o1, mySrd).getId(); + + Observation o2 = new Observation(); + o2.getCode().addCoding().setSystem("foo").setCode("testSearchCompositeParamN01"); + o2.setValue(new StringType("testSearchCompositeParamS02")); + IIdType id2 = myObservationDao.create(o2, mySrd).getId(); + + { + TokenParam v0 = new TokenParam("foo", "testSearchCompositeParamN01"); + StringParam v1 = new StringParam("testSearchCompositeParamS01"); + CompositeParam val = new CompositeParam(v0, v1); + IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE_VALUE_STRING, val)); + assertEquals(1, result.size().intValue()); + assertEquals(id1.toUnqualifiedVersionless(), result.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless()); + } + { + TokenParam v0 = new TokenParam("foo", "testSearchCompositeParamN01"); + StringParam v1 = new StringParam("testSearchCompositeParamS02"); + CompositeParam val = new CompositeParam(v0, v1); + IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE_VALUE_STRING, val)); + assertEquals(1, result.size().intValue()); + assertEquals(id2.toUnqualifiedVersionless(), result.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless()); + } + } + + @Test + public void testSearchCompositeParamDate() { + Observation o1 = new Observation(); + o1.getCode().addCoding().setSystem("foo").setCode("testSearchCompositeParamDateN01"); + o1.setValue(new Period().setStartElement(new DateTimeType("2001-01-01T11:11:11Z")).setEndElement(new DateTimeType("2001-01-01T12:11:11Z"))); + IIdType id1 = myObservationDao.create(o1, mySrd).getId().toUnqualifiedVersionless(); + + Observation o2 = new Observation(); + o2.getCode().addCoding().setSystem("foo").setCode("testSearchCompositeParamDateN01"); + o2.setValue(new Period().setStartElement(new DateTimeType("2001-01-02T11:11:11Z")).setEndElement(new DateTimeType("2001-01-02T12:11:11Z"))); + IIdType id2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless(); + + { + TokenParam v0 = new TokenParam("foo", "testSearchCompositeParamDateN01"); + DateParam v1 = new DateParam("2001-01-01"); + CompositeParam val = new CompositeParam(v0, v1); + IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE_VALUE_DATE, val)); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id1)); + } + { + TokenParam v0 = new TokenParam("foo", "testSearchCompositeParamDateN01"); + DateParam v1 = new DateParam(">2001-01-01T10:12:12Z"); + CompositeParam val = new CompositeParam(v0, v1); + IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE_VALUE_DATE, val)); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id1, id2)); + } + { + TokenParam v0 = new TokenParam("foo", "testSearchCompositeParamDateN01"); + DateParam v1 = new DateParam("gt2001-01-01T11:12:12Z"); + CompositeParam val = new CompositeParam(v0, v1); + IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE_VALUE_DATE, val)); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id1, id2)); + } + { + TokenParam v0 = new TokenParam("foo", "testSearchCompositeParamDateN01"); + DateParam v1 = new DateParam("gt2001-01-01T15:12:12Z"); + CompositeParam val = new CompositeParam(v0, v1); + IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_CODE_VALUE_DATE, val)); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id2)); + } + + } + + @Test + public void testComponentQuantity() { + Observation o1 = new Observation(); + o1.addComponent() + .setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1"))) + .setValue(new Quantity().setSystem("http://bar").setCode("code1").setValue(200)); + o1.addComponent() + .setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code2"))) + .setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(200)); + IIdType id1 = myObservationDao.create(o1, mySrd).getId().toUnqualifiedVersionless(); + + String param = Observation.SP_COMPONENT_VALUE_QUANTITY; + + { + QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 150, "http://bar", "code1"); + SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true).add(param, v1); + IBundleProvider result = myObservationDao.search(map); + assertThat("Got: "+ toUnqualifiedVersionlessIdValues(result), toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id1.getValue())); + } + } + + @Test + public void testSearchCompositeParamQuantity() { + Observation o1 = new Observation(); + o1.addComponent() + .setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1"))) + .setValue(new Quantity().setSystem("http://bar").setCode("code1").setValue(100)); + o1.addComponent() + .setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code2"))) + .setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(100)); + IIdType id1 = myObservationDao.create(o1, mySrd).getId().toUnqualifiedVersionless(); + + Observation o2 = new Observation(); + o2.addComponent() + .setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1"))) + .setValue(new Quantity().setSystem("http://bar").setCode("code1").setValue(200)); + o2.addComponent() + .setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code3"))) + .setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(200)); + IIdType id2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless(); + + String param = Observation.SP_COMPONENT_CODE_VALUE_QUANTITY; + + { + TokenParam v0 = new TokenParam("http://foo", "code1"); + QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 150, "http://bar", "code1"); + CompositeParam val = new CompositeParam<>(v0, v1); + SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true).add(param, val); + IBundleProvider result = myObservationDao.search(map); + assertThat("Got: "+ toUnqualifiedVersionlessIdValues(result), toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id2.getValue())); + } + { + TokenParam v0 = new TokenParam("http://foo", "code1"); + QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code1"); + CompositeParam val = new CompositeParam<>(v0, v1); + IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(param, val)); + assertThat(toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id1.getValue(), id2.getValue())); + } + { + TokenParam v0 = new TokenParam("http://foo", "code4"); + QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code1"); + CompositeParam val = new CompositeParam<>(v0, v1); + IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(param, val)); + assertThat(toUnqualifiedVersionlessIdValues(result), empty()); + } + { + TokenParam v0 = new TokenParam("http://foo", "code1"); + QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code4"); + CompositeParam val = new CompositeParam<>(v0, v1); + IBundleProvider result = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(param, val)); + assertThat(toUnqualifiedVersionlessIdValues(result), empty()); + } + } + + @Test + public void testSearchDateWrongParam() { + Patient p1 = new Patient(); + p1.getBirthDateElement().setValueAsString("1980-01-01"); + String id1 = myPatientDao.create(p1).getId().toUnqualifiedVersionless().getValue(); + + Patient p2 = new Patient(); + p2.setDeceased(new DateTimeType("1980-01-01")); + String id2 = myPatientDao.create(p2).getId().toUnqualifiedVersionless().getValue(); + + { + IBundleProvider found = myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_BIRTHDATE, new DateParam("1980-01-01"))); + assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id1)); + assertEquals(1, found.size().intValue()); + } + { + IBundleProvider found = myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_DEATH_DATE, new DateParam("1980-01-01"))); + assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id2)); + assertEquals(1, found.size().intValue()); + } + + } + + /** + * #222 + */ + @Test + public void testSearchForDeleted() { + + { + Patient patient = new Patient(); + patient.setId("TEST"); + patient.setLanguageElement(new CodeType("TEST")); + patient.addName().setFamily("TEST"); + patient.addIdentifier().setSystem("TEST").setValue("TEST"); + myPatientDao.update(patient, mySrd); + } + + SearchParameterMap params; + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_id", new StringParam("TEST")); + assertEquals(1, toList(myPatientDao.search(params)).size()); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_language", new StringParam("TEST")); + assertEquals(1, toList(myPatientDao.search(params)).size()); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_IDENTIFIER, new TokenParam("TEST", "TEST")); + assertEquals(1, toList(myPatientDao.search(params)).size()); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_NAME, new StringParam("TEST")); + assertEquals(1, toList(myPatientDao.search(params)).size()); + + myPatientDao.delete(new IdType("Patient/TEST"), mySrd); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_id", new StringParam("TEST")); + assertEquals(0, toList(myPatientDao.search(params)).size()); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("_language", new StringParam("TEST")); + assertEquals(0, toList(myPatientDao.search(params)).size()); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_IDENTIFIER, new TokenParam("TEST", "TEST")); + assertEquals(0, toList(myPatientDao.search(params)).size()); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_NAME, new StringParam("TEST")); + assertEquals(0, toList(myPatientDao.search(params)).size()); + + } + + @Test + public void testSearchForUnknownAlphanumericId() { + { + SearchParameterMap map = new SearchParameterMap(); + map.add("_id", new StringParam("testSearchForUnknownAlphanumericId")); + IBundleProvider retrieved = myPatientDao.search(map); + assertEquals(0, retrieved.size().intValue()); + } + } + + @Test + public void testSearchLanguageParam() { + IIdType id1; + { + Patient patient = new Patient(); + patient.getLanguageElement().setValue("en_CA"); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("testSearchLanguageParam").addGiven("Joe"); + id1 = myPatientDao.create(patient, mySrd).getId(); + } + IIdType id2; + { + Patient patient = new Patient(); + patient.getLanguageElement().setValue("en_US"); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("testSearchLanguageParam").addGiven("John"); + id2 = myPatientDao.create(patient, mySrd).getId(); + } + SearchParameterMap params; + { + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + + params.add(IAnyResource.SP_RES_LANGUAGE, new StringParam("en_CA")); + List patients = toList(myPatientDao.search(params)); + assertEquals(1, patients.size()); + assertEquals(id1.toUnqualifiedVersionless(), patients.get(0).getIdElement().toUnqualifiedVersionless()); + } + { + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + + params.add(IAnyResource.SP_RES_LANGUAGE, new StringParam("en_US")); + List patients = toList(myPatientDao.search(params)); + assertEquals(1, patients.size()); + assertEquals(id2.toUnqualifiedVersionless(), patients.get(0).getIdElement().toUnqualifiedVersionless()); + } + { + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + + params.add(IAnyResource.SP_RES_LANGUAGE, new StringParam("en_GB")); + List patients = toList(myPatientDao.search(params)); + assertEquals(0, patients.size()); + } + } + + @Test + public void testSearchLanguageParamAndOr() { + IIdType id1; + { + Patient patient = new Patient(); + patient.getLanguageElement().setValue("en_CA"); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("testSearchLanguageParam").addGiven("Joe"); + id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + Date betweenTime = new Date(); + + IIdType id2; + { + Patient patient = new Patient(); + patient.getLanguageElement().setValue("en_US"); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("testSearchLanguageParam").addGiven("John"); + id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.add(IAnyResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US"))); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1, id2)); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.add(IAnyResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US"))); + params.setLastUpdated(new DateRangeParam(betweenTime, null)); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id2)); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.add(IAnyResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ"))); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); + } + { + SearchParameterMap params = new SearchParameterMap(); + StringAndListParam and = new StringAndListParam(); + and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ"))); + and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA"))); + params.add(IAnyResource.SP_RES_LANGUAGE, and); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); + } + { + SearchParameterMap params = new SearchParameterMap(); + StringAndListParam and = new StringAndListParam(); + and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ"))); + and.addAnd(new StringOrListParam().addOr(new StringParam("ZZZZZ"))); + params.add(IAnyResource.SP_RES_LANGUAGE, and); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty()); + } + { + SearchParameterMap params = new SearchParameterMap(); + StringAndListParam and = new StringAndListParam(); + and.addAnd(new StringOrListParam().addOr(new StringParam("ZZZZZ"))); + and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ"))); + params.add(IAnyResource.SP_RES_LANGUAGE, and); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty()); + } + { + SearchParameterMap params = new SearchParameterMap(); + StringAndListParam and = new StringAndListParam(); + and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ"))); + and.addAnd(new StringOrListParam().addOr(new StringParam("")).addOr(new StringParam(null))); + params.add(IAnyResource.SP_RES_LANGUAGE, and); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.add("_id", new StringParam(id1.getIdPart())); + StringAndListParam and = new StringAndListParam(); + and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ"))); + and.addAnd(new StringOrListParam().addOr(new StringParam("")).addOr(new StringParam(null))); + params.add(IAnyResource.SP_RES_LANGUAGE, and); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); + } + { + SearchParameterMap params = new SearchParameterMap(); + StringAndListParam and = new StringAndListParam(); + and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ"))); + and.addAnd(new StringOrListParam().addOr(new StringParam("")).addOr(new StringParam(null))); + params.add(IAnyResource.SP_RES_LANGUAGE, and); + params.add("_id", new StringParam(id1.getIdPart())); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); + } + + } + + @Test + public void testSearchLastUpdatedParam() throws InterruptedException { + String methodName = "testSearchLastUpdatedParam"; + + int sleep = 100; + Thread.sleep(sleep); + + DateTimeType beforeAny = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI); + IIdType id1a; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily(methodName).addGiven("Joe"); + id1a = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType id1b; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily(methodName + "XXXX").addGiven("Joe"); + id1b = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + Thread.sleep(1100); + DateTimeType beforeR2 = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI); + Thread.sleep(1100); + + IIdType id2; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily(methodName).addGiven("John"); + id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + { + SearchParameterMap params = new SearchParameterMap(); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, hasItems(id1a, id1b, id2)); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLastUpdated(new DateRangeParam(beforeAny, null)); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, hasItems(id1a, id1b, id2)); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLastUpdated(new DateRangeParam(beforeR2, null)); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, hasItems(id2)); + assertThat(patients, not(hasItems(id1a, id1b))); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLastUpdated(new DateRangeParam(beforeAny, beforeR2)); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients.toString(), patients, not(hasItems(id2))); + assertThat(patients.toString(), patients, (hasItems(id1a, id1b))); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLastUpdated(new DateRangeParam(null, beforeR2)); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, (hasItems(id1a, id1b))); + assertThat(patients, not(hasItems(id2))); + } + + + { + SearchParameterMap params = new SearchParameterMap(); + params.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, beforeR2))); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, not(hasItems(id1a, id1b))); + assertThat(patients, (hasItems(id2))); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, beforeR2))); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, (hasItems(id1a, id1b))); + assertThat(patients, not(hasItems(id2))); + } + + } + + @SuppressWarnings("deprecation") + @Test + public void testSearchLastUpdatedParamWithComparator() throws InterruptedException { + IIdType id0; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + int sleep = 100; + + long start = System.currentTimeMillis(); + Thread.sleep(sleep); + + IIdType id1a; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id1a = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType id1b; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id1b = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + ourLog.info("Res 1: {}", myPatientDao.read(id0, mySrd).getMeta().getLastUpdatedElement().getValueAsString()); + ourLog.info("Res 2: {}", myPatientDao.read(id1a, mySrd).getMeta().getLastUpdatedElement().getValueAsString()); + ourLog.info("Res 3: {}", myPatientDao.read(id1b, mySrd).getMeta().getLastUpdatedElement().getValueAsString()); + + Thread.sleep(sleep); + long end = System.currentTimeMillis(); + + SearchParameterMap map; + Date startDate = new Date(start); + Date endDate = new Date(end); + DateTimeType startDateTime = new DateTimeType(startDate, TemporalPrecisionEnum.MILLI); + DateTimeType endDateTime = new DateTimeType(endDate, TemporalPrecisionEnum.MILLI); + + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam(startDateTime, endDateTime)); + ourLog.info("Searching: {}", map.getLastUpdated()); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a, id1b)); + + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, startDateTime), new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, endDateTime))); + ourLog.info("Searching: {}", map.getLastUpdated()); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a, id1b)); + + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN, startDateTime), new DateParam(ParamPrefixEnum.LESSTHAN, endDateTime))); + ourLog.info("Searching: {}", map.getLastUpdated()); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a, id1b)); + + map = new SearchParameterMap(); + map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN, startDateTime.getValue()), + new DateParam(ParamPrefixEnum.LESSTHAN, myPatientDao.read(id1b, mySrd).getMeta().getLastUpdatedElement().getValue()))); + ourLog.info("Searching: {}", map.getLastUpdated()); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a)); + } + + @Test + public void testSearchNameParam() { + IIdType id1; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("testSearchNameParam01Fam").addGiven("testSearchNameParam01Giv"); + id1 = myPatientDao.create(patient, mySrd).getId(); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("testSearchNameParam02Fam").addGiven("testSearchNameParam02Giv"); + myPatientDao.create(patient, mySrd); + } + + SearchParameterMap params; + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_FAMILY, new StringParam("testSearchNameParam01Fam")); + List patients = toList(myPatientDao.search(params)); + assertEquals(1, patients.size()); + assertEquals(id1.getIdPart(), patients.get(0).getIdElement().getIdPart()); + + // Given name shouldn't return for family param + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_FAMILY, new StringParam("testSearchNameParam01Giv")); + patients = toList(myPatientDao.search(params)); + assertEquals(0, patients.size()); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_NAME, new StringParam("testSearchNameParam01Fam")); + patients = toList(myPatientDao.search(params)); + assertEquals(1, patients.size()); + assertEquals(id1.getIdPart(), patients.get(0).getIdElement().getIdPart()); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_NAME, new StringParam("testSearchNameParam01Giv")); + patients = toList(myPatientDao.search(params)); + assertEquals(1, patients.size()); + assertEquals(id1.getIdPart(), patients.get(0).getIdElement().getIdPart()); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_FAMILY, new StringParam("testSearchNameParam01Foo")); + patients = toList(myPatientDao.search(params)); + assertEquals(0, patients.size()); + + } + + /** + * TODO: currently this doesn't index, we should get it working + */ + @Test + public void testSearchNearParam() { + { + Location loc = new Location(); + loc.getPosition().setLatitude(43.7); + loc.getPosition().setLatitude(79.4); + myLocationDao.create(loc, mySrd); + } + } + + @Test + public void testSearchNumberParam() { + RiskAssessment e1 = new RiskAssessment(); + e1.addIdentifier().setSystem("foo").setValue("testSearchNumberParam01"); + e1.addPrediction().setProbability(new DecimalType(4 * 24 * 60)); + IIdType id1 = myRiskAssessmentDao.create(e1, mySrd).getId(); + + RiskAssessment e2 = new RiskAssessment(); + e2.addIdentifier().setSystem("foo").setValue("testSearchNumberParam02"); + e2.addPrediction().setProbability(new DecimalType(4)); + IIdType id2 = myRiskAssessmentDao.create(e2, mySrd).getId(); + { + IBundleProvider found = myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam(">2"))); + assertEquals(2, found.size().intValue()); + assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1.toUnqualifiedVersionless(), id2.toUnqualifiedVersionless())); + } + { + IBundleProvider found = myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam("<1"))); + assertEquals(0, found.size().intValue()); + } + { + IBundleProvider found = myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam("4"))); + assertEquals(1, found.size().intValue()); + assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id2.toUnqualifiedVersionless())); + } + } + + @Test + public void testSearchNumberWrongParam() { + ImmunizationRecommendation ir1 = new ImmunizationRecommendation(); + ir1.addRecommendation().setDoseNumber(new PositiveIntType(1)); + String id1 = myImmunizationRecommendationDao.create(ir1).getId().toUnqualifiedVersionless().getValue(); + + ImmunizationRecommendation ir2 = new ImmunizationRecommendation(); + ir2.addRecommendation().setDoseNumber(new PositiveIntType(2)); + String id2 = myImmunizationRecommendationDao.create(ir2).getId().toUnqualifiedVersionless().getValue(); + + { + IBundleProvider found = myImmunizationRecommendationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ImmunizationRecommendation.SP_DOSE_NUMBER, new NumberParam("1"))); + assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id1)); + assertEquals(1, found.size().intValue()); + } + { + IBundleProvider found = myImmunizationRecommendationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ImmunizationRecommendation.SP_DOSE_SEQUENCE, new NumberParam("1"))); + assertThat(toUnqualifiedVersionlessIdValues(found), empty()); + assertEquals(0, found.size().intValue()); + } + + } + + /** + * When a valueset expansion returns no codes + */ + @Test + public void testSearchOnCodesWithNone() { + ValueSet vs = new ValueSet(); + vs.setUrl("urn:testSearchOnCodesWithNone"); + myValueSetDao.create(vs); + + Patient p1 = new Patient(); + p1.setGender(AdministrativeGender.MALE); + String id1 = myPatientDao.create(p1).getId().toUnqualifiedVersionless().getValue(); + + Patient p2 = new Patient(); + p2.setGender(AdministrativeGender.FEMALE); + String id2 = myPatientDao.create(p2).getId().toUnqualifiedVersionless().getValue(); + + { + IBundleProvider found = myPatientDao + .search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_GENDER, new TokenParam().setModifier(TokenParamModifier.IN).setValue("urn:testSearchOnCodesWithNone"))); + assertThat(toUnqualifiedVersionlessIdValues(found), empty()); + assertEquals(0, found.size().intValue()); + } + + } + + @Test + public void testSearchParamChangesType() { + String name = "testSearchParamChangesType"; + IIdType id; + { + Patient patient = new Patient(); + patient.addName().setFamily(name); + id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + SearchParameterMap params; + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_FAMILY, new StringParam(name)); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, contains(id)); + + Patient patient = new Patient(); + patient.addIdentifier().setSystem(name).setValue(name); + patient.setId(id); + myPatientDao.update(patient, mySrd); + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_FAMILY, new StringParam(name)); + patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, not(contains(id))); + + } + + @Test + public void testSearchPractitionerPhoneAndEmailParam() { + String methodName = "testSearchPractitionerPhoneAndEmailParam"; + IIdType id1; + { + Practitioner patient = new Practitioner(); + patient.addName().setFamily(methodName); + patient.addTelecom().setSystem(ContactPointSystem.PHONE).setValue("123"); + id1 = myPractitionerDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType id2; + { + Practitioner patient = new Practitioner(); + patient.addName().setFamily(methodName); + patient.addTelecom().setSystem(ContactPointSystem.EMAIL).setValue("abc"); + id2 = myPractitionerDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + SearchParameterMap params; + List patients; + + params = new SearchParameterMap(); + params.add(Practitioner.SP_FAMILY, new StringParam(methodName)); + params.add(Practitioner.SP_EMAIL, new TokenParam(null, "123")); + patients = toUnqualifiedVersionlessIds(myPractitionerDao.search(params)); + assertEquals(0, patients.size()); + + params = new SearchParameterMap(); + params.add(Practitioner.SP_FAMILY, new StringParam(methodName)); + patients = toUnqualifiedVersionlessIds(myPractitionerDao.search(params)); + assertEquals(2, patients.size()); + assertThat(patients, containsInAnyOrder(id1, id2)); + + params = new SearchParameterMap(); + params.add(Practitioner.SP_FAMILY, new StringParam(methodName)); + params.add(Practitioner.SP_EMAIL, new TokenParam(null, "abc")); + patients = toUnqualifiedVersionlessIds(myPractitionerDao.search(params)); + assertEquals(1, patients.size()); + assertThat(patients, containsInAnyOrder(id2)); + + params = new SearchParameterMap(); + params.add(Practitioner.SP_FAMILY, new StringParam(methodName)); + params.add(Practitioner.SP_PHONE, new TokenParam(null, "123")); + patients = toUnqualifiedVersionlessIds(myPractitionerDao.search(params)); + assertEquals(1, patients.size()); + assertThat(patients, containsInAnyOrder(id1)); + + } + + @Test + public void testSearchQuantityWrongParam() { + Condition c1 = new Condition(); + c1.setAbatement(new Range().setLow((SimpleQuantity) new SimpleQuantity().setValue(1L)).setHigh((SimpleQuantity) new SimpleQuantity().setValue(1L))); + String id1 = myConditionDao.create(c1).getId().toUnqualifiedVersionless().getValue(); + + Condition c2 = new Condition(); + c2.setOnset(new Range().setLow((SimpleQuantity) new SimpleQuantity().setValue(1L)).setHigh((SimpleQuantity) new SimpleQuantity().setValue(1L))); + String id2 = myConditionDao.create(c2).getId().toUnqualifiedVersionless().getValue(); + + { + IBundleProvider found = myConditionDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Condition.SP_ABATEMENT_AGE, new QuantityParam("1"))); + assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id1)); + assertEquals(1, found.size().intValue()); + } + { + IBundleProvider found = myConditionDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Condition.SP_ONSET_AGE, new QuantityParam("1"))); + assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id2)); + assertEquals(1, found.size().intValue()); + } + + } + + @Test + public void testSearchResourceLinkWithChain() { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("testSearchResourceLinkWithChainXX"); + patient.addIdentifier().setSystem("urn:system").setValue("testSearchResourceLinkWithChain01"); + IIdType patientId01 = myPatientDao.create(patient, mySrd).getId(); + + Patient patient02 = new Patient(); + patient02.addIdentifier().setSystem("urn:system").setValue("testSearchResourceLinkWithChainXX"); + patient02.addIdentifier().setSystem("urn:system").setValue("testSearchResourceLinkWithChain02"); + IIdType patientId02 = myPatientDao.create(patient02, mySrd).getId(); + + Observation obs01 = new Observation(); + obs01.setEffective(new DateTimeType(new Date())); + obs01.setSubject(new Reference(patientId01)); + IIdType obsId01 = myObservationDao.create(obs01, mySrd).getId(); + + Observation obs02 = new Observation(); + obs02.setEffective(new DateTimeType(new Date())); + obs02.setSubject(new Reference(patientId02)); + IIdType obsId02 = myObservationDao.create(obs02, mySrd).getId(); + + // Create another type, that shouldn't be returned + DiagnosticReport dr01 = new DiagnosticReport(); + dr01.setSubject(new Reference(patientId01)); + IIdType drId01 = myDiagnosticReportDao.create(dr01, mySrd).getId(); + + ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]", patientId01, patientId02, obsId01, obsId02, drId01); + + List result = toList(myObservationDao + .search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_IDENTIFIER, "urn:system|testSearchResourceLinkWithChain01")))); + assertEquals(1, result.size()); + assertEquals(obsId01.getIdPart(), result.get(0).getIdElement().getIdPart()); + + result = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_PATIENT, new ReferenceParam(patientId01.getIdPart())))); + assertEquals(1, result.size()); + + result = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_PATIENT, new ReferenceParam(patientId01.getIdPart())))); + assertEquals(1, result.size()); + + result = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_IDENTIFIER, "999999999999")))); + assertEquals(0, result.size()); + + result = toList(myObservationDao + .search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_IDENTIFIER, "urn:system|testSearchResourceLinkWithChainXX")))); + assertEquals(2, result.size()); + + result = toList( + myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_IDENTIFIER, "testSearchResourceLinkWithChainXX")))); + assertEquals(2, result.size()); + + result = toList( + myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_IDENTIFIER, "|testSearchResourceLinkWithChainXX")))); + assertEquals(0, result.size()); + + } + + @Test + public void testSearchResourceLinkWithChainDouble() { + String methodName = "testSearchResourceLinkWithChainDouble"; + + Organization org = new Organization(); + org.setName(methodName); + IIdType orgId01 = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + + Location locParent = new Location(); + locParent.setManagingOrganization(new Reference(orgId01)); + IIdType locParentId = myLocationDao.create(locParent, mySrd).getId().toUnqualifiedVersionless(); + + Location locChild = new Location(); + locChild.setPartOf(new Reference(locParentId)); + IIdType locChildId = myLocationDao.create(locChild, mySrd).getId().toUnqualifiedVersionless(); + + Location locGrandchild = new Location(); + locGrandchild.setPartOf(new Reference(locChildId)); + IIdType locGrandchildId = myLocationDao.create(locGrandchild, mySrd).getId().toUnqualifiedVersionless(); + + IBundleProvider found; + ReferenceParam param; + + found = myLocationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("organization", new ReferenceParam(orgId01.getIdPart()))); + assertEquals(1, found.size().intValue()); + assertEquals(locParentId, found.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless()); + + param = new ReferenceParam(orgId01.getIdPart()); + param.setChain("organization"); + found = myLocationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("partof", param)); + assertEquals(1, found.size().intValue()); + assertEquals(locChildId, found.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless()); + + param = new ReferenceParam(orgId01.getIdPart()); + param.setChain("partof.organization"); + found = myLocationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("partof", param)); + assertEquals(1, found.size().intValue()); + assertEquals(locGrandchildId, found.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless()); + + param = new ReferenceParam(methodName); + param.setChain("partof.organization.name"); + found = myLocationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("partof", param)); + assertEquals(1, found.size().intValue()); + assertEquals(locGrandchildId, found.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless()); + } + + @Test + public void testSearchResourceLinkWithChainWithMultipleTypes() throws Exception { + Patient patient = new Patient(); + patient.addName().setFamily("testSearchResourceLinkWithChainWithMultipleTypes01"); + patient.addName().setFamily("testSearchResourceLinkWithChainWithMultipleTypesXX"); + IIdType patientId01 = myPatientDao.create(patient, mySrd).getId(); + + Location loc01 = new Location(); + loc01.getNameElement().setValue("testSearchResourceLinkWithChainWithMultipleTypes01"); + IIdType locId01 = myLocationDao.create(loc01, mySrd).getId(); + + Observation obs01 = new Observation(); + obs01.setEffective(new DateTimeType(new Date())); + obs01.setSubject(new Reference(patientId01)); + IIdType obsId01 = myObservationDao.create(obs01, mySrd).getId().toUnqualifiedVersionless(); + + Date between = new Date(); + Thread.sleep(10); + + Observation obs02 = new Observation(); + obs02.setEffective(new DateTimeType(new Date())); + obs02.setSubject(new Reference(locId01)); + IIdType obsId02 = myObservationDao.create(obs02, mySrd).getId().toUnqualifiedVersionless(); + + Thread.sleep(10); + Date after = new Date(); + + ourLog.info("P1[{}] L1[{}] Obs1[{}] Obs2[{}]", patientId01, locId01, obsId01, obsId02); + + List result; + SearchParameterMap params; + + result = toUnqualifiedVersionlessIds(myObservationDao + .search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypesXX")))); + assertThat(result, containsInAnyOrder(obsId01)); + assertEquals(1, result.size()); + + result = toUnqualifiedVersionlessIds(myObservationDao.search( + new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam("Patient", Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypes01")))); + assertThat(result, containsInAnyOrder(obsId01)); + assertEquals(1, result.size()); + + params = new SearchParameterMap(); + params.add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypes01")); + result = toUnqualifiedVersionlessIds(myObservationDao.search(params)); + assertEquals(2, result.size()); + assertThat(result, containsInAnyOrder(obsId01, obsId02)); + + params = new SearchParameterMap(); + params.add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypes01")); + params.setLastUpdated(new DateRangeParam(between, after)); + result = toUnqualifiedVersionlessIds(myObservationDao.search(params)); + assertEquals(1, result.size()); + assertThat(result, containsInAnyOrder(obsId02)); + + result = toUnqualifiedVersionlessIds(myObservationDao + .search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypesYY")))); + assertEquals(0, result.size()); + + } + + @Test + public void testSearchResourceLinkWithTextLogicalId() { + Patient patient = new Patient(); + patient.setId("testSearchResourceLinkWithTextLogicalId01"); + patient.addIdentifier().setSystem("urn:system").setValue("testSearchResourceLinkWithTextLogicalIdXX"); + patient.addIdentifier().setSystem("urn:system").setValue("testSearchResourceLinkWithTextLogicalId01"); + IIdType patientId01 = myPatientDao.update(patient, mySrd).getId(); + + Patient patient02 = new Patient(); + patient02.setId("testSearchResourceLinkWithTextLogicalId02"); + patient02.addIdentifier().setSystem("urn:system").setValue("testSearchResourceLinkWithTextLogicalIdXX"); + patient02.addIdentifier().setSystem("urn:system").setValue("testSearchResourceLinkWithTextLogicalId02"); + IIdType patientId02 = myPatientDao.update(patient02, mySrd).getId(); + + Observation obs01 = new Observation(); + obs01.setEffective(new DateTimeType(new Date())); + obs01.setSubject(new Reference(patientId01)); + IIdType obsId01 = myObservationDao.create(obs01, mySrd).getId(); + + Observation obs02 = new Observation(); + obs02.setEffective(new DateTimeType(new Date())); + obs02.setSubject(new Reference(patientId02)); + IIdType obsId02 = myObservationDao.create(obs02, mySrd).getId(); + + // Create another type, that shouldn't be returned + DiagnosticReport dr01 = new DiagnosticReport(); + dr01.setSubject(new Reference(patientId01)); + IIdType drId01 = myDiagnosticReportDao.create(dr01, mySrd).getId(); + + ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]", patientId01, patientId02, obsId01, obsId02, drId01); + + List result = toList( + myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam("testSearchResourceLinkWithTextLogicalId01")))); + assertEquals(1, result.size()); + assertEquals(obsId01.getIdPart(), result.get(0).getIdElement().getIdPart()); + + result = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam("testSearchResourceLinkWithTextLogicalId99")))); + assertEquals(0, result.size()); + + result = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam("999999999999999")))); + assertEquals(0, result.size()); + + } + + @SuppressWarnings("unused") + @Test + public void testSearchResourceReferenceOnlyCorrectPath() { + IIdType oid1; + { + Organization org = new Organization(); + org.setActive(true); + oid1 = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType tid1; + { + Task task = new Task(); + task.setRequester(new Reference(oid1)); + tid1 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType tid2; + { + Task task = new Task(); + task.setOwner(new Reference(oid1)); + tid2 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); + } + + SearchParameterMap map; + List ids; + + map = new SearchParameterMap(); + map.add(Task.SP_REQUESTER, new ReferenceParam(oid1.getValue())); + ids = toUnqualifiedVersionlessIds(myTaskDao.search(map)); + assertThat(ids, contains(tid1)); // NOT tid2 + + } + + @Test + public void testSearchStringParam() throws Exception { + IIdType pid1; + IIdType pid2; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester_testSearchStringParam").addGiven("Joe"); + pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + Date between = new Date(); + Thread.sleep(10); + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("Tester_testSearchStringParam").addGiven("John"); + pid2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + Thread.sleep(10); + Date after = new Date(); + + SearchParameterMap params; + List patients; + + params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_testSearchStringParam")); + params.setLastUpdated(new DateRangeParam(between, after)); + patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, containsInAnyOrder(pid2)); + + params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_testSearchStringParam")); + patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, containsInAnyOrder(pid1, pid2)); + assertEquals(2, patients.size()); + + params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("FOO_testSearchStringParam")); + patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertEquals(0, patients.size()); + + // Try with different casing + + params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("tester_testsearchstringparam")); + patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, containsInAnyOrder(pid1, pid2)); + assertEquals(2, patients.size()); + + params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("TESTER_TESTSEARCHSTRINGPARAM")); + patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, containsInAnyOrder(pid1, pid2)); + assertEquals(2, patients.size()); + } + + @Test + public void testSearchStringParamDoesntMatchWrongType() { + IIdType pid1; + IIdType pid2; + { + Patient patient = new Patient(); + patient.addName().setFamily("HELLO"); + pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + Practitioner patient = new Practitioner(); + patient.addName().setFamily("HELLO"); + pid2 = myPractitionerDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + SearchParameterMap params; + List patients; + + params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("HELLO")); + patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, containsInAnyOrder(pid1)); + assertThat(patients, not(containsInAnyOrder(pid2))); + } + + @Test + public void testSearchStringParamReallyLong() { + String methodName = "testSearchStringParamReallyLong"; + String value = StringUtils.rightPad(methodName, 200, 'a'); + + IIdType longId; + IIdType shortId; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily(value); + longId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + shortId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + SearchParameterMap params; + + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + + String substring = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH); + params.add(Patient.SP_FAMILY, new StringParam(substring)); + IBundleProvider found = myPatientDao.search(params); + assertEquals(1, toList(found).size()); + assertThat(toUnqualifiedVersionlessIds(found), contains(longId)); + assertThat(toUnqualifiedVersionlessIds(found), not(contains(shortId))); + + } + + @Test + public void testSearchStringParamWithNonNormalized() { + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().addGiven("testSearchStringParamWithNonNormalized_h\u00F6ra"); + myPatientDao.create(patient, mySrd); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().addGiven("testSearchStringParamWithNonNormalized_HORA"); + myPatientDao.create(patient, mySrd); + } + + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_GIVEN, new StringParam("testSearchStringParamWithNonNormalized_hora")); + List patients = toList(myPatientDao.search(params)); + assertEquals(2, patients.size()); + + StringParam parameter = new StringParam("testSearchStringParamWithNonNormalized_hora"); + parameter.setExact(true); + params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_GIVEN, parameter); + patients = toList(myPatientDao.search(params)); + assertEquals(0, patients.size()); + + } + + @Test + public void testSearchStringWrongParam() { + Patient p1 = new Patient(); + p1.getNameFirstRep().setFamily("AAA"); + String id1 = myPatientDao.create(p1).getId().toUnqualifiedVersionless().getValue(); + + Patient p2 = new Patient(); + p2.getNameFirstRep().addGiven("AAA"); + String id2 = myPatientDao.create(p2).getId().toUnqualifiedVersionless().getValue(); + + { + IBundleProvider found = myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_FAMILY, new StringParam("AAA"))); + assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id1)); + assertEquals(1, found.size().intValue()); + } + { + IBundleProvider found = myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_GIVEN, new StringParam("AAA"))); + assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id2)); + assertEquals(1, found.size().intValue()); + } + + } + + @Test + public void testSearchTokenParam() { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam001"); + patient.addName().setFamily("Tester").addGiven("testSearchTokenParam1"); + patient.addCommunication().getLanguage().setText("testSearchTokenParamComText").addCoding().setCode("testSearchTokenParamCode").setSystem("testSearchTokenParamSystem") + .setDisplay("testSearchTokenParamDisplay"); + myPatientDao.create(patient, mySrd); + + patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam002"); + patient.addName().setFamily("Tester").addGiven("testSearchTokenParam2"); + myPatientDao.create(patient, mySrd); + + { + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testSearchTokenParam001")); + IBundleProvider retrieved = myPatientDao.search(map); + assertEquals(1, retrieved.size().intValue()); + } + { + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_IDENTIFIER, new TokenParam(null, "testSearchTokenParam001")); + IBundleProvider retrieved = myPatientDao.search(map); + assertEquals(1, retrieved.size().intValue()); + } + { + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_LANGUAGE, new TokenParam("testSearchTokenParamSystem", "testSearchTokenParamCode")); + assertEquals(1, myPatientDao.search(map).size().intValue()); + } + { + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamCode", true)); + assertEquals(0, myPatientDao.search(map).size().intValue()); + } + { + // Complete match + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamComText", true)); + assertEquals(1, myPatientDao.search(map).size().intValue()); + } + { + // Left match + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamcomtex", true)); + assertEquals(1, myPatientDao.search(map).size().intValue()); + } + { + // Right match + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamComTex", true)); + assertEquals(1, myPatientDao.search(map).size().intValue()); + } + { + SearchParameterMap map = new SearchParameterMap(); + TokenOrListParam listParam = new TokenOrListParam(); + listParam.add("urn:system", "testSearchTokenParam001"); + listParam.add("urn:system", "testSearchTokenParam002"); + map.add(Patient.SP_IDENTIFIER, listParam); + IBundleProvider retrieved = myPatientDao.search(map); + assertEquals(2, retrieved.size().intValue()); + } + { + SearchParameterMap map = new SearchParameterMap(); + TokenOrListParam listParam = new TokenOrListParam(); + listParam.add(null, "testSearchTokenParam001"); + listParam.add("urn:system", "testSearchTokenParam002"); + map.add(Patient.SP_IDENTIFIER, listParam); + IBundleProvider retrieved = myPatientDao.search(map); + assertEquals(2, retrieved.size().intValue()); + } + } + + @Test + public void testSearchTokenParamNoValue() { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam001"); + patient.addName().setFamily("Tester").addGiven("testSearchTokenParam1"); + patient.addCommunication().getLanguage().setText("testSearchTokenParamComText").addCoding().setCode("testSearchTokenParamCode").setSystem("testSearchTokenParamSystem") + .setDisplay("testSearchTokenParamDisplay"); + myPatientDao.create(patient, mySrd); + + patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam002"); + patient.addName().setFamily("Tester").addGiven("testSearchTokenParam2"); + myPatientDao.create(patient, mySrd); + + patient = new Patient(); + patient.addIdentifier().setSystem("urn:system2").setValue("testSearchTokenParam002"); + patient.addName().setFamily("Tester").addGiven("testSearchTokenParam2"); + myPatientDao.create(patient, mySrd); + + { + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", null)); + IBundleProvider retrieved = myPatientDao.search(map); + assertEquals(2, retrieved.size().intValue()); + } + { + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "")); + IBundleProvider retrieved = myPatientDao.search(map); + assertEquals(2, retrieved.size().intValue()); + } + } + + /** + * See #819 + */ + @Test + public void testSearchTokenWithNotModifier() { + String male, female; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester").addGiven("Joe"); + patient.setGender(AdministrativeGender.MALE); + male = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue(); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("Tester").addGiven("Jane"); + patient.setGender(AdministrativeGender.FEMALE); + female = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue(); + } + + List patients; + SearchParameterMap params; + + params = new SearchParameterMap(); + params.add(Patient.SP_GENDER, new TokenParam(null, "male")); + params.setLoadSynchronous(true); + patients = toUnqualifiedVersionlessIdValues(myPatientDao.search(params)); + assertThat(patients, contains(male)); + + params = new SearchParameterMap(); + params.add(Patient.SP_GENDER, new TokenParam(null, "male").setModifier(TokenParamModifier.NOT)); + params.setLoadSynchronous(true); + patients = toUnqualifiedVersionlessIdValues(myPatientDao.search(params)); + assertThat(patients, contains(female)); + } + + @Test + public void testSearchTokenWrongParam() { + Patient p1 = new Patient(); + p1.setGender(AdministrativeGender.MALE); + String id1 = myPatientDao.create(p1).getId().toUnqualifiedVersionless().getValue(); + + Patient p2 = new Patient(); + p2.addIdentifier().setValue(AdministrativeGender.MALE.toCode()); + String id2 = myPatientDao.create(p2).getId().toUnqualifiedVersionless().getValue(); + + { + IBundleProvider found = myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_GENDER, new TokenParam(null, "male"))); + assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id1)); + assertEquals(1, found.size().intValue()); + } + { + IBundleProvider found = myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_IDENTIFIER, new TokenParam(null, "male"))); + assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id2)); + assertEquals(1, found.size().intValue()); + } + + } + + @Test + @Ignore + public void testSearchUnknownContentParam() { + SearchParameterMap params = new SearchParameterMap(); + params.add(Constants.PARAM_CONTENT, new StringParam("fulltext")); + try { + myPatientDao.search(params); + fail(); + } catch (InvalidRequestException e) { + assertEquals("Fulltext search is not enabled on this service, can not process parameter: _content", e.getMessage()); + } + } + + @Test + @Ignore + public void testSearchUnknownTextParam() { + SearchParameterMap params = new SearchParameterMap(); + params.add(Constants.PARAM_TEXT, new StringParam("fulltext")); + try { + myPatientDao.search(params); + fail(); + } catch (InvalidRequestException e) { + assertEquals("Fulltext search is not enabled on this service, can not process parameter: _text", e.getMessage()); + } + } + + @Test + public void testSearchValueQuantity() { + String methodName = "testSearchValueQuantity"; + + String id1; + { + Observation o = new Observation(); + o.getCode().addCoding().setSystem("urn:foo").setCode(methodName + "code"); + Quantity q = new Quantity().setSystem("urn:bar:" + methodName).setCode(methodName + "units").setValue(100); + o.setValue(q); + id1 = myObservationDao.create(o, mySrd).getId().toUnqualifiedVersionless().getValue(); + } + + String id2; + { + Observation o = new Observation(); + o.getCode().addCoding().setSystem("urn:foo").setCode(methodName + "code"); + Quantity q = new Quantity().setSystem("urn:bar:" + methodName).setCode(methodName + "units").setValue(5); + o.setValue(q); + id2 = myObservationDao.create(o, mySrd).getId().toUnqualifiedVersionless().getValue(); + } + + SearchParameterMap map; + IBundleProvider found; + QuantityParam param; + + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), null, null); + map.add(Observation.SP_VALUE_QUANTITY, param); + found = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(found), contains(id1)); + + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), null, methodName + "units"); + map.add(Observation.SP_VALUE_QUANTITY, param); + found = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(found), contains(id1)); + + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), "urn:bar:" + methodName, null); + map.add(Observation.SP_VALUE_QUANTITY, param); + found = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(found), contains(id1)); + + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), "urn:bar:" + methodName, methodName + "units"); + map.add(Observation.SP_VALUE_QUANTITY, param); + found = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(found), contains(id1)); + + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("1000"), "urn:bar:" + methodName, methodName + "units"); + map.add(Observation.SP_VALUE_QUANTITY, param); + found = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(found), empty()); + + } + + @Test + public void testSearchWithContains() { + myDaoConfig.setAllowContainsSearches(true); + + Patient pt1 = new Patient(); + pt1.addName().setFamily("ABCDEFGHIJK"); + String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue(); + + Patient pt2 = new Patient(); + pt2.addName().setFamily("FGHIJK"); + String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue(); + + Patient pt3 = new Patient(); + pt3.addName().setFamily("ZZZZZ"); + myPatientDao.create(pt3).getId().toUnqualifiedVersionless().getValue(); + + + List ids; + SearchParameterMap map; + IBundleProvider results; + + // Contains = true + map = new SearchParameterMap(); + map.add(Patient.SP_NAME, new StringParam("FGHIJK").setContains(true)); + map.setLoadSynchronous(true); + results = myPatientDao.search(map); + ids = toUnqualifiedVersionlessIdValues(results); + assertThat(ids, containsInAnyOrder(pt1id, pt2id)); + + // Contains = false + map = new SearchParameterMap(); + map.add(Patient.SP_NAME, new StringParam("FGHIJK").setContains(false)); + map.setLoadSynchronous(true); + results = myPatientDao.search(map); + ids = toUnqualifiedVersionlessIdValues(results); + assertThat(ids, containsInAnyOrder(pt2id)); + + // No contains + map = new SearchParameterMap(); + map.add(Patient.SP_NAME, new StringParam("FGHIJK")); + map.setLoadSynchronous(true); + results = myPatientDao.search(map); + ids = toUnqualifiedVersionlessIdValues(results); + assertThat(ids, containsInAnyOrder(pt2id)); + } + + @Test + public void testSearchWithContainsDisabled() { + myDaoConfig.setAllowContainsSearches(false); + + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_NAME, new StringParam("FGHIJK").setContains(true)); + + try { + myPatientDao.search(map); + fail(); + } catch (MethodNotAllowedException e) { + assertEquals(":contains modifier is disabled on this server", e.getMessage()); + } + } + + @Test + public void testSearchWithDate() { + IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId(); + IIdType id2; + IIdType id1; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("Tester_testSearchStringParam").addGiven("John"); + patient.setBirthDateElement(new DateType("2011-01-01")); + patient.getManagingOrganization().setReferenceElement(orgId); + id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_BIRTHDATE, new DateParam("2011-01-01")); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, contains(id2)); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_BIRTHDATE, new DateParam("2011-01-03")); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, empty()); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_BIRTHDATE, new DateParam("2011-01-03").setPrefix(ParamPrefixEnum.LESSTHAN)); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, contains(id2)); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(Patient.SP_BIRTHDATE, new DateParam("2010-01-01").setPrefix(ParamPrefixEnum.LESSTHAN)); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, empty()); + } + } + + @Test + public void testSearchWithFetchSizeDefaultMaximum() { + myDaoConfig.setFetchSizeDefaultMaximum(5); + + for (int i = 0; i < 10; i++) { + Patient p = new Patient(); + p.addName().setFamily("PT" + i); + myPatientDao.create(p); + } + + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + IBundleProvider values = myPatientDao.search(map); + assertEquals(5, values.size().intValue()); + assertEquals(5, values.getResources(0, 1000).size()); + } + + @Test + public void testSearchWithIncludes() { + String methodName = "testSearchWithIncludes"; + IIdType parentOrgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1Parent"); + parentOrgId = myOrganizationDao.create(org, mySrd).getId(); + } + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1"); + org.setPartOf(new Reference(parentOrgId)); + IIdType orgId = myOrganizationDao.create(org, mySrd).getId(); + + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester_" + methodName + "_P1").addGiven("Joe"); + patient.getManagingOrganization().setReferenceElement(orgId); + myPatientDao.create(patient, mySrd); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("Tester_" + methodName + "_P2").addGiven("John"); + myPatientDao.create(patient, mySrd); + } + + { + // No includes + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); + List patients = toList(myPatientDao.search(params)); + assertEquals(1, patients.size()); + } + { + // Named include + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); + params.addInclude(Patient.INCLUDE_ORGANIZATION.asNonRecursive()); + IBundleProvider search = myPatientDao.search(params); + List patients = toList(search); + assertEquals(2, patients.size()); + assertEquals(Patient.class, patients.get(0).getClass()); + assertEquals(Organization.class, patients.get(1).getClass()); + } + { + // Named include with parent non-recursive + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); + params.addInclude(Patient.INCLUDE_ORGANIZATION); + params.addInclude(Organization.INCLUDE_PARTOF.asNonRecursive()); + IBundleProvider search = myPatientDao.search(params); + List patients = toList(search); + assertEquals(2, patients.size()); + assertEquals(Patient.class, patients.get(0).getClass()); + assertEquals(Organization.class, patients.get(1).getClass()); + } + { + // Named include with parent recursive + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); + params.addInclude(Patient.INCLUDE_ORGANIZATION); + params.addInclude(Organization.INCLUDE_PARTOF.asRecursive()); + IBundleProvider search = myPatientDao.search(params); + List patients = toList(search); + assertEquals(3, patients.size()); + assertEquals(Patient.class, patients.get(0).getClass()); + assertEquals(Organization.class, patients.get(1).getClass()); + assertEquals(Organization.class, patients.get(2).getClass()); + } + { + // * include non recursive + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); + params.addInclude(IBaseResource.INCLUDE_ALL.asNonRecursive()); + IBundleProvider search = myPatientDao.search(params); + List patients = toList(search); + assertEquals(2, patients.size()); + assertEquals(Patient.class, patients.get(0).getClass()); + assertEquals(Organization.class, patients.get(1).getClass()); + } + { + // * include recursive + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); + params.addInclude(IBaseResource.INCLUDE_ALL.asRecursive()); + IBundleProvider search = myPatientDao.search(params); + List patients = toList(search); + assertEquals(3, patients.size()); + assertEquals(Patient.class, patients.get(0).getClass()); + assertEquals(Organization.class, patients.get(1).getClass()); + assertEquals(Organization.class, patients.get(2).getClass()); + } + { + // Irrelevant include + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); + params.addInclude(Encounter.INCLUDE_EPISODE_OF_CARE); + IBundleProvider search = myPatientDao.search(params); + List patients = toList(search); + assertEquals(1, patients.size()); + assertEquals(Patient.class, patients.get(0).getClass()); + } + } + + @SuppressWarnings("unused") + @Test + public void testSearchWithIncludesParameterNoRecurse() { + String methodName = "testSearchWithIncludes"; + IIdType parentParentOrgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1Parent"); + parentParentOrgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType parentOrgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1Parent"); + org.setPartOf(new Reference(parentParentOrgId)); + parentOrgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType orgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1"); + org.setPartOf(new Reference(parentOrgId)); + orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType patientId; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester_" + methodName + "_P1").addGiven("Joe"); + patient.getManagingOrganization().setReferenceElement(orgId); + patientId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + { + SearchParameterMap params = new SearchParameterMap(); + params.add(IAnyResource.SP_RES_ID, new StringParam(orgId.getIdPart())); + params.addInclude(Organization.INCLUDE_PARTOF.asNonRecursive()); + List resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(resources, contains(orgId, parentOrgId)); + } + } + + @SuppressWarnings("unused") + @Test + public void testSearchWithIncludesParameterRecurse() { + String methodName = "testSearchWithIncludes"; + IIdType parentParentOrgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1Parent"); + parentParentOrgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType parentOrgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1Parent"); + org.setPartOf(new Reference(parentParentOrgId)); + parentOrgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType orgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1"); + org.setPartOf(new Reference(parentOrgId)); + orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType patientId; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester_" + methodName + "_P1").addGiven("Joe"); + patient.getManagingOrganization().setReferenceElement(orgId); + patientId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + { + SearchParameterMap params = new SearchParameterMap(); + params.add(IAnyResource.SP_RES_ID, new StringParam(orgId.getIdPart())); + params.addInclude(Organization.INCLUDE_PARTOF.asRecursive()); + List resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + ourLog.info(resources.toString()); + assertThat(resources, contains(orgId, parentOrgId, parentParentOrgId)); + } + } + + @Test + public void testSearchWithIncludesStarNoRecurse() { + String methodName = "testSearchWithIncludes"; + IIdType parentParentOrgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1Parent"); + parentParentOrgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType parentOrgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1Parent"); + org.setPartOf(new Reference(parentParentOrgId)); + parentOrgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType orgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1"); + org.setPartOf(new Reference(parentOrgId)); + orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType patientId; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester_" + methodName + "_P1").addGiven("Joe"); + patient.getManagingOrganization().setReferenceElement(orgId); + patientId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + { + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); + params.addInclude(new Include("*").asNonRecursive()); + List resources = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(resources, contains(patientId, orgId)); + } + } + + @Test + public void testSearchWithIncludesStarRecurse() { + String methodName = "testSearchWithIncludes"; + IIdType parentParentOrgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1Parent"); + parentParentOrgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType parentOrgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1Parent"); + org.setPartOf(new Reference(parentParentOrgId)); + parentOrgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType orgId; + { + Organization org = new Organization(); + org.getNameElement().setValue(methodName + "_O1"); + org.setPartOf(new Reference(parentOrgId)); + orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType patientId; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester_" + methodName + "_P1").addGiven("Joe"); + patient.getManagingOrganization().setReferenceElement(orgId); + patientId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + + { + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); + params.addInclude(new Include("*").asRecursive()); + List resources = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(resources, contains(patientId, orgId, parentOrgId, parentParentOrgId)); + } + } + + /** + * Test for #62 + */ + @Test + public void testSearchWithIncludesThatHaveTextId() { + { + Organization org = new Organization(); + org.setId("testSearchWithIncludesThatHaveTextIdid1"); + org.getNameElement().setValue("testSearchWithIncludesThatHaveTextId_O1"); + IIdType orgId = myOrganizationDao.update(org, mySrd).getId(); + assertThat(orgId.getValue(), endsWith("Organization/testSearchWithIncludesThatHaveTextIdid1/_history/1")); + + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester_testSearchWithIncludesThatHaveTextId_P1").addGiven("Joe"); + patient.getManagingOrganization().setReferenceElement(orgId); + myPatientDao.create(patient, mySrd); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("Tester_testSearchWithIncludesThatHaveTextId_P2").addGiven("John"); + myPatientDao.create(patient, mySrd); + } + + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_testSearchWithIncludesThatHaveTextId_P1")); + params.addInclude(Patient.INCLUDE_ORGANIZATION); + IBundleProvider search = myPatientDao.search(params); + List patients = toList(search); + assertEquals(2, patients.size()); + assertEquals(Patient.class, patients.get(0).getClass()); + assertEquals(Organization.class, patients.get(1).getClass()); + + params = new SearchParameterMap(); + params.add(Patient.SP_FAMILY, new StringParam("Tester_testSearchWithIncludesThatHaveTextId_P1")); + patients = toList(myPatientDao.search(params)); + assertEquals(1, patients.size()); + + } + + @Test + public void testSearchWithNoResults() { + Device dev = new Device(); + dev.addIdentifier().setSystem("Foo"); + myDeviceDao.create(dev, mySrd); + + IBundleProvider value = myDeviceDao.search(new SearchParameterMap()); + ourLog.info("Initial size: " + value.size()); + for (IBaseResource next : value.getResources(0, value.size())) { + ourLog.info("Deleting: {}", next.getIdElement()); + myDeviceDao.delete(next.getIdElement(), mySrd); + } + + value = myDeviceDao.search(new SearchParameterMap()); + if (value.size() > 0) { + ourLog.info("Found: " + (value.getResources(0, 1).get(0).getIdElement())); + fail(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(value.getResources(0, 1).get(0))); + } + assertEquals(0, value.size().intValue()); + + List res = value.getResources(0, 0); + assertTrue(res.isEmpty()); + + } + + @Test + public void testSearchWithRevIncludes() { + final String methodName = "testSearchWithRevIncludes"; + TransactionTemplate txTemplate = new TransactionTemplate(myTransactionMgr); + txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); + IIdType pid = txTemplate.execute(new TransactionCallback() { + + @Override + public IIdType doInTransaction(TransactionStatus theStatus) { + Patient p = new Patient(); + p.addName().setFamily(methodName); + IIdType pid = myPatientDao.create(p).getId().toUnqualifiedVersionless(); + + Condition c = new Condition(); + c.getSubject().setReferenceElement(pid); + myConditionDao.create(c); + + return pid; + } + }); + + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_RES_ID, new StringParam(pid.getIdPart())); + map.addRevInclude(Condition.INCLUDE_PATIENT); + IBundleProvider results = myPatientDao.search(map); + List foundResources = results.getResources(0, results.size()); + assertEquals(Patient.class, foundResources.get(0).getClass()); + assertEquals(Condition.class, foundResources.get(1).getClass()); + } + + @Test + public void testSearchWithSecurityAndProfileParams() { + String methodName = "testSearchWithSecurityAndProfileParams"; + + IIdType tag1id; + { + Organization org = new Organization(); + org.getNameElement().setValue("FOO"); + org.getMeta().addSecurity("urn:taglist", methodName + "1a", null); + tag1id = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType tag2id; + { + Organization org = new Organization(); + org.getNameElement().setValue("FOO"); + org.getMeta().addProfile("http://" + methodName); + tag2id = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.add("_security", new TokenParam("urn:taglist", methodName + "1a")); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag1id)); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.add("_profile", new UriParam("http://" + methodName)); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag2id)); + } + } + + @Test + public void testSearchWithTagParameter() { + String methodName = "testSearchWithTagParameter"; + + IIdType tag1id; + { + Organization org = new Organization(); + org.getNameElement().setValue("FOO"); + org.getMeta().addTag("urn:taglist", methodName + "1a", null); + org.getMeta().addTag("urn:taglist", methodName + "1b", null); + tag1id = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + + Date betweenDate = new Date(); + + IIdType tag2id; + { + Organization org = new Organization(); + org.getNameElement().setValue("FOO"); + org.getMeta().addTag("urn:taglist", methodName + "2a", null); + org.getMeta().addTag("urn:taglist", methodName + "2b", null); + tag2id = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + + { + // One tag + SearchParameterMap params = new SearchParameterMap(); + params.add("_tag", new TokenParam("urn:taglist", methodName + "1a")); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag1id)); + } + { + // Code only + SearchParameterMap params = new SearchParameterMap(); + params.add("_tag", new TokenParam(null, methodName + "1a")); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag1id)); + } + { + // Or tags + SearchParameterMap params = new SearchParameterMap(); + TokenOrListParam orListParam = new TokenOrListParam(); + orListParam.add(new TokenParam("urn:taglist", methodName + "1a")); + orListParam.add(new TokenParam("urn:taglist", methodName + "2a")); + params.add("_tag", orListParam); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag1id, tag2id)); + } + { + // Or tags with lastupdated + SearchParameterMap params = new SearchParameterMap(); + TokenOrListParam orListParam = new TokenOrListParam(); + orListParam.add(new TokenParam("urn:taglist", methodName + "1a")); + orListParam.add(new TokenParam("urn:taglist", methodName + "2a")); + params.add("_tag", orListParam); + params.setLastUpdated(new DateRangeParam(betweenDate, null)); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag2id)); + } + // TODO: get multiple/AND working + { + // And tags + SearchParameterMap params = new SearchParameterMap(); + TokenAndListParam andListParam = new TokenAndListParam(); + andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "1a")); + andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "2a")); + params.add("_tag", andListParam); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertEquals(0, patients.size()); + } + + { + // And tags + SearchParameterMap params = new SearchParameterMap(); + TokenAndListParam andListParam = new TokenAndListParam(); + andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "1a")); + andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "1b")); + params.add("_tag", andListParam); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag1id)); + } + + } + + @Test + public void testSearchWithTagParameterMissing() { + String methodName = "testSearchWithTagParameterMissing"; + + IIdType tag1id; + { + Organization org = new Organization(); + org.getNameElement().setValue("FOO"); + org.getMeta().addTag("urn:taglist", methodName + "1a", null); + org.getMeta().addTag("urn:taglist", methodName + "1b", null); + tag1id = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + + IIdType tag2id; + { + Organization org = new Organization(); + org.getNameElement().setValue("FOO"); + org.getMeta().addTag("urn:taglist", methodName + "1b", null); + tag2id = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + + { + // One tag + SearchParameterMap params = new SearchParameterMap(); + params.add("_tag", new TokenParam("urn:taglist", methodName + "1a").setModifier(TokenParamModifier.NOT)); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag2id)); + assertThat(patients, not(containsInAnyOrder(tag1id))); + } + { + // Non existant tag + SearchParameterMap params = new SearchParameterMap(); + params.add("_tag", new TokenParam("urn:taglist", methodName + "FOO").setModifier(TokenParamModifier.NOT)); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag1id, tag2id)); + } + { + // Common tag + SearchParameterMap params = new SearchParameterMap(); + params.add("_tag", new TokenParam("urn:taglist", methodName + "1b").setModifier(TokenParamModifier.NOT)); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, empty()); + } + } + + /** + * https://chat.fhir.org/#narrow/stream/implementers/topic/Understanding.20_include + */ + @Test + public void testSearchWithTypedInclude() { + IIdType patId; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType practId; + { + Practitioner pract = new Practitioner(); + pract.addIdentifier().setSystem("urn:system").setValue("001"); + practId = myPractitionerDao.create(pract, mySrd).getId().toUnqualifiedVersionless(); + } + + Appointment appt = new Appointment(); + appt.addParticipant().getActor().setReference(patId.getValue()); + appt.addParticipant().getActor().setReference(practId.getValue()); + IIdType apptId = myAppointmentDao.create(appt, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap params = new SearchParameterMap(); + params.addInclude(Appointment.INCLUDE_PATIENT); + assertThat(toUnqualifiedVersionlessIds(myAppointmentDao.search(params)), containsInAnyOrder(patId, apptId)); + + } + + @Test + public void testSearchWithUriParam() throws Exception { + Class type = ValueSet.class; + String resourceName = "/valueset-dstu2.json"; + ValueSet vs = loadResourceFromClasspath(type, resourceName); + IIdType id1 = myValueSetDao.update(vs, mySrd).getId().toUnqualifiedVersionless(); + + ValueSet vs2 = new ValueSet(); + vs2.setUrl("http://hl7.org/foo/bar"); + myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless(); + + IBundleProvider result; + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/basic-resource-type"))); + assertThat(toUnqualifiedVersionlessIds(result), contains(id1)); + + result = myValueSetDao + .search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/basic-resource-type").setQualifier(UriParamQualifierEnum.BELOW))); + assertThat(toUnqualifiedVersionlessIds(result), contains(id1)); + + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/").setQualifier(UriParamQualifierEnum.BELOW))); + assertThat(toUnqualifiedVersionlessIds(result), contains(id1)); + + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/FOOOOOO"))); + assertThat(toUnqualifiedVersionlessIds(result), empty()); + + } + + @Test + public void testSearchWithUriParamAbove() { + ValueSet vs1 = new ValueSet(); + vs1.setUrl("http://hl7.org/foo/baz"); + myValueSetDao.create(vs1, mySrd).getId().toUnqualifiedVersionless(); + + ValueSet vs2 = new ValueSet(); + vs2.setUrl("http://hl7.org/foo/bar"); + IIdType id2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless(); + + ValueSet vs3 = new ValueSet(); + vs3.setUrl("http://hl7.org/foo/bar/baz"); + IIdType id3 = myValueSetDao.create(vs3, mySrd).getId().toUnqualifiedVersionless(); + + IBundleProvider result; + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/foo/bar/baz/boz").setQualifier(UriParamQualifierEnum.ABOVE))); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id2, id3)); + + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/foo/bar/baz").setQualifier(UriParamQualifierEnum.ABOVE))); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id2, id3)); + + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/foo/bar").setQualifier(UriParamQualifierEnum.ABOVE))); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id2)); + + result = myValueSetDao + .search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/basic-resource-type").setQualifier(UriParamQualifierEnum.ABOVE))); + assertThat(toUnqualifiedVersionlessIds(result), empty()); + + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org").setQualifier(UriParamQualifierEnum.ABOVE))); + assertThat(toUnqualifiedVersionlessIds(result), empty()); + } + + @Test + public void testSearchWithUriParamBelow() throws Exception { + myFhirCtx.setParserErrorHandler(new StrictErrorHandler()); + + Class type = ValueSet.class; + String resourceName = "/valueset-dstu2.json"; + ValueSet vs = loadResourceFromClasspath(type, resourceName); + IIdType id1 = myValueSetDao.update(vs, mySrd).getId().toUnqualifiedVersionless(); + + ValueSet vs2 = new ValueSet(); + vs2.setUrl("http://hl7.org/foo/bar"); + IIdType id2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless(); + + IBundleProvider result; + + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://").setQualifier(UriParamQualifierEnum.BELOW))); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id1, id2)); + + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org").setQualifier(UriParamQualifierEnum.BELOW))); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id1, id2)); + + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/foo").setQualifier(UriParamQualifierEnum.BELOW))); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(id2)); + + result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_URL, new UriParam("http://hl7.org/foo/baz").setQualifier(UriParamQualifierEnum.BELOW))); + assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder()); + } + + /** + * See #744 + */ + @Test + public void testSearchWithVeryLongUrlLonger() { + myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); + + Patient p = new Patient(); + p.addName().setFamily("A1"); + myPatientDao.create(p); + + assertEquals(0, mySearchEntityDao.count()); + + SearchParameterMap map = new SearchParameterMap(); + StringOrListParam or = new StringOrListParam(); + or.addOr(new StringParam("A1")); + for (int i = 0; i < 50; i++) { + or.addOr(new StringParam(StringUtils.leftPad("", 200, (char) ('A' + i)))); + } + map.add(Patient.SP_NAME, or); + IBundleProvider results = myPatientDao.search(map); + assertEquals(1, results.getResources(0, 10).size()); + assertEquals(1, mySearchEntityDao.count()); + + map = new SearchParameterMap(); + or = new StringOrListParam(); + or.addOr(new StringParam("A1")); + or.addOr(new StringParam("A1")); + for (int i = 0; i < 50; i++) { + or.addOr(new StringParam(StringUtils.leftPad("", 200, (char) ('A' + i)))); + } + map.add(Patient.SP_NAME, or); + results = myPatientDao.search(map); + assertEquals(1, results.getResources(0, 10).size()); + // We expect a new one because we don't cache the search URL for very long search URLs + assertEquals(2, mySearchEntityDao.count()); + + } + + @Test + public void testDateSearchParametersShouldBeTimezoneIndependent() { + + createObservationWithEffective("NO1", "2011-01-02T23:00:00-11:30"); + createObservationWithEffective("NO2", "2011-01-03T00:00:00+01:00"); + + createObservationWithEffective("YES01", "2011-01-02T00:00:00-11:30"); + createObservationWithEffective("YES02", "2011-01-02T00:00:00-10:00"); + createObservationWithEffective("YES03", "2011-01-02T00:00:00-09:00"); + createObservationWithEffective("YES04", "2011-01-02T00:00:00-08:00"); + createObservationWithEffective("YES05", "2011-01-02T00:00:00-07:00"); + createObservationWithEffective("YES06", "2011-01-02T00:00:00-06:00"); + createObservationWithEffective("YES07", "2011-01-02T00:00:00-05:00"); + createObservationWithEffective("YES08", "2011-01-02T00:00:00-04:00"); + createObservationWithEffective("YES09", "2011-01-02T00:00:00-03:00"); + createObservationWithEffective("YES10", "2011-01-02T00:00:00-02:00"); + createObservationWithEffective("YES11", "2011-01-02T00:00:00-01:00"); + createObservationWithEffective("YES12", "2011-01-02T00:00:00Z"); + createObservationWithEffective("YES13", "2011-01-02T00:00:00+01:00"); + createObservationWithEffective("YES14", "2011-01-02T00:00:00+02:00"); + createObservationWithEffective("YES15", "2011-01-02T00:00:00+03:00"); + createObservationWithEffective("YES16", "2011-01-02T00:00:00+04:00"); + createObservationWithEffective("YES17", "2011-01-02T00:00:00+05:00"); + createObservationWithEffective("YES18", "2011-01-02T00:00:00+06:00"); + createObservationWithEffective("YES19", "2011-01-02T00:00:00+07:00"); + createObservationWithEffective("YES20", "2011-01-02T00:00:00+08:00"); + createObservationWithEffective("YES21", "2011-01-02T00:00:00+09:00"); + createObservationWithEffective("YES22", "2011-01-02T00:00:00+10:00"); + createObservationWithEffective("YES23", "2011-01-02T00:00:00+11:00"); + + + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_DATE, new DateParam("2011-01-02")); + IBundleProvider results = myObservationDao.search(map); + List values = toUnqualifiedVersionlessIdValues(results); + Collections.sort(values); + assertThat(values.toString(), values, contains( + "Observation/YES01", + "Observation/YES02", + "Observation/YES03", + "Observation/YES04", + "Observation/YES05", + "Observation/YES06", + "Observation/YES07", + "Observation/YES08", + "Observation/YES09", + "Observation/YES10", + "Observation/YES11", + "Observation/YES12", + "Observation/YES13", + "Observation/YES14", + "Observation/YES15", + "Observation/YES16", + "Observation/YES17", + "Observation/YES18", + "Observation/YES19", + "Observation/YES20", + "Observation/YES21", + "Observation/YES22", + "Observation/YES23" + )); + } + + private void createObservationWithEffective(String theId, String theEffective) { + Observation obs = new Observation(); + obs.setId(theId); + obs.setEffective(new DateTimeType(theEffective)); + myObservationDao.update(obs); + + ourLog.info("Obs {} has time {}", theId, obs.getEffectiveDateTimeType().getValue().toString()); + } + + /** + * See #744 + */ + @Test + public void testSearchWithVeryLongUrlShorter() { + myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); + + Patient p = new Patient(); + p.addName().setFamily("A1"); + myPatientDao.create(p); + + assertEquals(0, mySearchEntityDao.count()); + + SearchParameterMap map = new SearchParameterMap(); + StringOrListParam or = new StringOrListParam(); + or.addOr(new StringParam("A1")); + or.addOr(new StringParam(StringUtils.leftPad("", 200, 'A'))); + or.addOr(new StringParam(StringUtils.leftPad("", 200, 'B'))); + or.addOr(new StringParam(StringUtils.leftPad("", 200, 'C'))); + map.add(Patient.SP_NAME, or); + IBundleProvider results = myPatientDao.search(map); + assertEquals(1, results.getResources(0, 10).size()); + assertEquals(1, mySearchEntityDao.count()); + + map = new SearchParameterMap(); + or = new StringOrListParam(); + or.addOr(new StringParam("A1")); + or.addOr(new StringParam(StringUtils.leftPad("", 200, 'A'))); + or.addOr(new StringParam(StringUtils.leftPad("", 200, 'B'))); + or.addOr(new StringParam(StringUtils.leftPad("", 200, 'C'))); + map.add(Patient.SP_NAME, or); + results = myPatientDao.search(map); + assertEquals(1, results.getResources(0, 10).size()); + assertEquals(1, mySearchEntityDao.count()); + + } + + private String toStringMultiline(List theResults) { + StringBuilder b = new StringBuilder(); + for (Object next : theResults) { + b.append('\n'); + b.append(" * ").append(next.toString()); + } + return b.toString(); + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java index 64664d9e64d..728a75f6950 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java @@ -668,33 +668,6 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { } - @Test - public void testUpdateReusesIndexes() { - myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED); - - QueryCountHolder.clear(); - - Patient pt = new Patient(); - pt.setActive(true); - pt.addName().setFamily("FAMILY1").addGiven("GIVEN1A").addGiven("GIVEN1B"); - IIdType id = myPatientDao.create(pt).getId().toUnqualifiedVersionless(); - - ourLog.info("Now have {} deleted", QueryCountHolder.getGrandTotal().getDelete()); - ourLog.info("Now have {} inserts", QueryCountHolder.getGrandTotal().getInsert()); - QueryCountHolder.clear(); - - ourLog.info("** About to update"); - - pt.setId(id); - pt.getNameFirstRep().addGiven("GIVEN1C"); - myPatientDao.update(pt); - - ourLog.info("Now have {} deleted", QueryCountHolder.getGrandTotal().getDelete()); - ourLog.info("Now have {} inserts", QueryCountHolder.getGrandTotal().getInsert()); - assertEquals(0, QueryCountHolder.getGrandTotal().getDelete()); - assertEquals(4, QueryCountHolder.getGrandTotal().getInsert()); - } - @Test public void testUpdateUnknownNumericIdFails() { Patient p = new Patient(); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 9a8f49cd6e1..2f7f9661acb 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -116,6 +116,11 @@ the example project]]> if they are not already. + + When updating resources in the JPA server, a bug caused index table entries to be refreshed + sometimes even though the index value hadn't changed. This issue did not cause incorrect search + results but had an effect on write performance. This has been corrected. +