Skip to content

Commit 63b7b37

Browse files
jamesagnewdmuylwyk
andauthored
Improve ValueSet filtering (#2162)
* Improve filter search * Filter improvements * Tests passing * Test fixes * Fix transaction filter * Add changelog * Test fix * Update hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties Co-authored-by: Diederik Muylwyk <[email protected]> * Update hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java Co-authored-by: Diederik Muylwyk <[email protected]> * Resolve FIXME * Test fix * Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java Co-authored-by: Diederik Muylwyk <[email protected]> Co-authored-by: Diederik Muylwyk <[email protected]>
1 parent 4f6be9d commit 63b7b37

File tree

17 files changed

+771
-278
lines changed

17 files changed

+771
-278
lines changed

hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11

22
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionRefersToUnknownCs=Unknown CodeSystem URI "{0}" referenced from ValueSet
3+
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetNotYetExpanded=ValueSet "{0}" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: {1} | {2}
4+
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetNotYetExpanded_OffsetNotAllowed=ValueSet expansion can not combine "offset" with "ValueSet.compose.exclude" unless the ValueSet has been pre-expanded. ValueSet "{0}" must be pre-expanded for this operation to work.
5+
36

47
# Core Library Messages
58
ca.uhn.fhir.context.FhirContext.unknownResourceName=Unknown resource name "{0}" (this name is not known in FHIR version "{1}")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
type: fix
3+
issue: 2162
4+
title: "When expanding a pre-expanded ValueSet using a filter, the filter was ignored and the pre-expansion was not used
5+
resulting in an inefficient and potentially incorrect expansion. This has been corrected."

hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetConceptViewDao.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package ca.uhn.fhir.jpa.dao.data;
22

33
import ca.uhn.fhir.jpa.entity.TermValueSetConceptView;
4+
import org.springframework.data.domain.PageRequest;
5+
import org.springframework.data.domain.Pageable;
46
import org.springframework.data.jpa.repository.JpaRepository;
57
import org.springframework.data.jpa.repository.Query;
68
import org.springframework.data.repository.query.Param;
@@ -32,4 +34,7 @@ public interface ITermValueSetConceptViewDao extends JpaRepository<TermValueSetC
3234
@Query("SELECT v FROM TermValueSetConceptView v WHERE v.myConceptValueSetPid = :pid AND v.myConceptOrder >= :from AND v.myConceptOrder < :to ORDER BY v.myConceptOrder")
3335
List<TermValueSetConceptView> findByTermValueSetId(@Param("from") int theFrom, @Param("to") int theTo, @Param("pid") Long theValueSetId);
3436

37+
@Query("SELECT v FROM TermValueSetConceptView v WHERE v.myConceptValueSetPid = :pid AND v.myConceptDisplay LIKE :display ORDER BY v.myConceptOrder")
38+
List<TermValueSetConceptView> findByTermValueSetId(@Param("pid") Long theValueSetId, @Param("display") String theDisplay);
39+
3540
}

hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
2828
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
2929
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
30+
import ca.uhn.fhir.jpa.model.util.JpaConstants;
3031
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
3132
import ca.uhn.fhir.rest.api.server.RequestDetails;
3233
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@@ -221,7 +222,7 @@ private void applyFilter(IntegerType theTotalElement, List<ValueSetExpansionCont
221222
private void addFilterIfPresent(String theFilter, ConceptSetComponent include) {
222223
if (ElementUtil.isEmpty(include.getConcept())) {
223224
if (isNotBlank(theFilter)) {
224-
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter);
225+
include.addFilter().setProperty(JpaConstants.VALUESET_FILTER_DISPLAY).setOp(FilterOperator.EQUAL).setValue(theFilter);
225226
}
226227
}
227228
}

hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ public <T> T execute(RequestDetails theRequestDetails, TransactionCallback<T> th
9494
}
9595
}
9696

97-
9897
}
9998

10099
/**

hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@
3737
import ca.uhn.fhir.jpa.entity.SearchInclude;
3838
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
3939
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
40-
import ca.uhn.fhir.model.api.IQueryParameterType;
41-
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
4240
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
4341
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
4442
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
@@ -47,6 +45,7 @@
4745
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
4846
import ca.uhn.fhir.jpa.util.InterceptorUtil;
4947
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
48+
import ca.uhn.fhir.model.api.IQueryParameterType;
5049
import ca.uhn.fhir.model.api.Include;
5150
import ca.uhn.fhir.rest.api.CacheControlDirective;
5251
import ca.uhn.fhir.rest.api.Constants;
@@ -468,7 +467,7 @@ private IBundleProvider executeQuery(String theResourceType, SearchParameterMap
468467
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
469468
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
470469
return txTemplate.execute(t -> {
471-
470+
472471
// Load the results synchronously
473472
final List<ResourcePersistentId> pids = new ArrayList<>();
474473

@@ -668,6 +667,12 @@ public void setRequestPartitionHelperService(IRequestPartitionHelperSvc theReque
668667
myRequestPartitionHelperService = theRequestPartitionHelperService;
669668
}
670669

670+
private boolean isWantCount(SearchParameterMap myParams, boolean wantOnlyCount) {
671+
return wantOnlyCount ||
672+
SearchTotalModeEnum.ACCURATE.equals(myParams.getSearchTotalMode()) ||
673+
(myParams.getSearchTotalMode() == null && SearchTotalModeEnum.ACCURATE.equals(myDaoConfig.getDefaultTotalMode()));
674+
}
675+
671676
/**
672677
* A search task is a Callable task that runs in
673678
* a thread pool to handle an individual search. One instance
@@ -691,6 +696,8 @@ public class SearchTask implements Callable<Void> {
691696
private final ArrayList<ResourcePersistentId> myUnsyncedPids = new ArrayList<>();
692697
private final RequestDetails myRequest;
693698
private final RequestPartitionId myRequestPartitionId;
699+
private final SearchRuntimeDetails mySearchRuntimeDetails;
700+
private final Transaction myParentTransaction;
694701
private Search mySearch;
695702
private boolean myAbortRequested;
696703
private int myCountSavedTotal = 0;
@@ -699,8 +706,6 @@ public class SearchTask implements Callable<Void> {
699706
private boolean myAdditionalPrefetchThresholdsRemaining;
700707
private List<ResourcePersistentId> myPreviouslyAddedResourcePids;
701708
private Integer myMaxResultsToFetch;
702-
private final SearchRuntimeDetails mySearchRuntimeDetails;
703-
private final Transaction myParentTransaction;
704709

705710
/**
706711
* Constructor
@@ -1193,17 +1198,6 @@ protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theArg0)
11931198
}
11941199
}
11951200

1196-
private boolean isWantCount(SearchParameterMap myParams, boolean wantOnlyCount) {
1197-
return wantOnlyCount ||
1198-
SearchTotalModeEnum.ACCURATE.equals(myParams.getSearchTotalMode()) ||
1199-
(myParams.getSearchTotalMode() == null && SearchTotalModeEnum.ACCURATE.equals(myDaoConfig.getDefaultTotalMode()));
1200-
}
1201-
1202-
private static boolean isWantOnlyCount(SearchParameterMap myParams) {
1203-
return SummaryEnum.COUNT.equals(myParams.getSummaryMode())
1204-
| INTEGER_0.equals(myParams.getCount());
1205-
}
1206-
12071201
public class SearchContinuationTask extends SearchTask {
12081202

12091203
public SearchContinuationTask(Search theSearch, IDao theCallingDao, SearchParameterMap theParams, String theResourceType, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
@@ -1242,6 +1236,10 @@ public Void call() {
12421236

12431237
}
12441238

1239+
private static boolean isWantOnlyCount(SearchParameterMap myParams) {
1240+
return SummaryEnum.COUNT.equals(myParams.getSummaryMode())
1241+
| INTEGER_0.equals(myParams.getCount());
1242+
}
12451243

12461244
public static void populateSearchEntity(SearchParameterMap theParams, String theResourceType, String theSearchUuid, String theQueryString, Search theSearch) {
12471245
theSearch.setDeleted(false);
@@ -1270,8 +1268,8 @@ public static void populateSearchEntity(SearchParameterMap theParams, String the
12701268
* Creates a {@link Pageable} using a start and end index
12711269
*/
12721270
@SuppressWarnings("WeakerAccess")
1273-
public static @Nullable
1274-
Pageable toPage(final int theFromIndex, int theToIndex) {
1271+
@Nullable
1272+
public static Pageable toPage(final int theFromIndex, int theToIndex) {
12751273
int pageSize = theToIndex - theFromIndex;
12761274
if (pageSize < 1) {
12771275
return null;

hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/UriPredicateBuilder.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public UriPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
7777
public Condition addPredicate(List<? extends IQueryParameterType> theUriOrParameterList, String theParamName, SearchFilterParser.CompareOperation theOperation, RequestDetails theRequestDetails) {
7878

7979
List<Condition> codePredicates = new ArrayList<>();
80+
boolean predicateIsHash = false;
8081
for (IQueryParameterType nextOr : theUriOrParameterList) {
8182

8283
if (nextOr instanceof UriParam) {
@@ -141,8 +142,8 @@ public Condition addPredicate(List<? extends IQueryParameterType> theUriOrParame
141142
Condition uriPredicate = null;
142143
if (theOperation == null || theOperation == SearchFilterParser.CompareOperation.eq) {
143144
long hashUri = ResourceIndexedSearchParamUri.calculateHashUri(getPartitionSettings(), getRequestPartitionId(), getResourceType(), theParamName, value);
144-
Condition hashPredicate = BinaryCondition.equalTo(myColumnHashUri, generatePlaceholder(hashUri));
145-
codePredicates.add(hashPredicate);
145+
uriPredicate = BinaryCondition.equalTo(myColumnHashUri, generatePlaceholder(hashUri));
146+
predicateIsHash = true;
146147
} else if (theOperation == SearchFilterParser.CompareOperation.ne) {
147148
uriPredicate = BinaryCondition.notEqualTo(myColumnUri, generatePlaceholder(value));
148149
} else if (theOperation == SearchFilterParser.CompareOperation.co) {
@@ -164,11 +165,7 @@ public Condition addPredicate(List<? extends IQueryParameterType> theUriOrParame
164165
theOperation.toString()));
165166
}
166167

167-
if (uriPredicate != null) {
168-
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), getRequestPartitionId(), getResourceType(), theParamName);
169-
BinaryCondition hashIdentityPredicate = BinaryCondition.equalTo(getColumnHashIdentity(), generatePlaceholder(hashIdentity));
170-
codePredicates.add(ComboCondition.and(hashIdentityPredicate, uriPredicate));
171-
}
168+
codePredicates.add(uriPredicate);
172169
}
173170

174171
} else {
@@ -186,8 +183,11 @@ public Condition addPredicate(List<? extends IQueryParameterType> theUriOrParame
186183
}
187184

188185
ComboCondition orPredicate = ComboCondition.or(codePredicates.toArray(new Condition[0]));
189-
Condition outerPredicate = combineWithHashIdentityPredicate(getResourceType(), theParamName, orPredicate);
190-
return outerPredicate;
186+
if (predicateIsHash) {
187+
return orPredicate;
188+
} else {
189+
return combineWithHashIdentityPredicate(getResourceType(), theParamName, orPredicate);
190+
}
191191

192192
}
193193

0 commit comments

Comments
 (0)