Merge remote-tracking branch 'origin/master' into gg_20201105-remove-person-references

This commit is contained in:
Nick Goupinets 2020-11-27 16:54:04 -05:00
commit 2a3b72db17
49 changed files with 959 additions and 862 deletions

View File

@ -93,4 +93,10 @@ public class ValueSetExpansionOptions {
myFailOnMissingCodeSystem = theFailOnMissingCodeSystem; myFailOnMissingCodeSystem = theFailOnMissingCodeSystem;
return this; return this;
} }
public static ValueSetExpansionOptions forOffsetAndCount(int theOffset, int theCount) {
return new ValueSetExpansionOptions()
.setOffset(theOffset)
.setCount(theCount);
}
} }

View File

@ -80,7 +80,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlMultipleMatches=Invalid match
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.transactionOperationWithMultipleMatchFailure=Failed to {0} resource with match URL "{1}" because this search matched {2} resources ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.transactionOperationWithMultipleMatchFailure=Failed to {0} resource with match URL "{1}" because this search matched {2} resources
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.transactionOperationFailedNoId=Failed to {0} resource in transaction because no ID was provided ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.transactionOperationFailedNoId=Failed to {0} resource in transaction because no ID was provided
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.transactionOperationFailedUnknownId=Failed to {0} resource in transaction because no resource could be found with ID {1} ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.transactionOperationFailedUnknownId=Failed to {0} resource in transaction because no resource could be found with ID {1}
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.uniqueIndexConflictFailure=Can not create resource of type {0} as it would create a duplicate index matching query: {1} (existing index belongs to {2}) ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.uniqueIndexConflictFailure=Can not create resource of type {0} as it would create a duplicate unique index matching query: {1} (existing index belongs to {2}, new unique index created by {3})
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionContainsMultipleWithDuplicateId=Transaction bundle contains multiple resources with ID: {0} ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionContainsMultipleWithDuplicateId=Transaction bundle contains multiple resources with ID: {0}
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionEntryHasInvalidVerb=Transaction bundle entry has missing or invalid HTTP Verb specified in Bundle.entry({1}).request.method. Found value: "{0}" ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionEntryHasInvalidVerb=Transaction bundle entry has missing or invalid HTTP Verb specified in Bundle.entry({1}).request.method. Found value: "{0}"

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 2175
title: "Expanding a ValueSet using a filter now evaluates the display with left-matching
by string token, case-insensitive."

View File

@ -0,0 +1,5 @@
---
type: add
issue: 2182
title: "When a unique index SearchParametr violation is blocked, the error message will now include the ID of the relevant
SearchParameter, in order to make troubleshooting easier."

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 2183
title: "The `CodeSystem/$subsumes` operation inadvertantly reversed the meanings of the CodeA and CodeB parameters, resulting
in `subsumes` and `subsumed-by` responses being reversed. This has been corrected. Thanks to Rob Hausam for reporting!"

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 2195
title: "When performing a ValueSet expansion with a filter a large pre-expanded
ValueSet (more than 1000 codes), the filter failed to find concepts appearing
after the first thousand. This has been corrected."

View File

@ -0,0 +1,4 @@
---
type: change
issue: 2197
title: The resource IDs for several LOINC resources were corrected. Thanks to Steven Wagers for the pull request!

View File

@ -0,0 +1,8 @@
---
- item:
type: "add"
title: "The version of a few dependencies have been bumped to the latest versions
(dependent HAPI modules listed in brackets):
<ul>
<li>Jetty (JPA Starter): 9.4.30.v20200611 -&gt; 9.4.34.v20201102</li>
</ul>"

View File

@ -87,6 +87,20 @@ The following snippet shows a server with this configuration.
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/PartitionExamples.java|multitenantServer}} {{snippet:classpath:/ca/uhn/hapi/fhir/docs/PartitionExamples.java|multitenantServer}}
``` ```
Once enabled, HTTP Requests to the FHIR server must include the name of the partition in the request, for identification purposes. With no multitenancy, a request to create a Patient could look like this:
```HTTP
POST www.example.com/fhir/Patient
```
With partitioning enabled, if we were to now create a patient in the `DEFAULT` paritition, the request would now look like this:
```
POST www.example.com/fhir/DEFAULT/Patient
```
Failure to add a partition name to the request path will result in an error when multitenancy is enabled.
# Limitations # Limitations

View File

@ -46,11 +46,13 @@ The `$partition-management-create-partition` operation can be used to create a n
</table> </table>
### Example ### Example
Note that once multitenancy is enabled, all requests to the FHIR server must contain a tenant. These operations are no exception.
If you fail to include a tenant identifier in the request, an error will be returned.
An HTTP POST to the following URL would be used to invoke this operation: An HTTP POST to the following URL would be used to invoke this operation. Notice that we use the DEFAULT partition, as it always exists by default.
```url ```url
http://example.com/$partition-management-create-partition http://example.com/DEFAULT/$partition-management-create-partition
``` ```
The following request body could be used: The following request body could be used:
@ -117,7 +119,7 @@ The `$partition-management-update-partition` operation can be used to update an
An HTTP POST to the following URL would be used to invoke this operation: An HTTP POST to the following URL would be used to invoke this operation:
```url ```url
http://example.com/$partition-management-update-partition http://example.com/DEFAULT/$partition-management-update-partition
``` ```
The following request body could be used: The following request body could be used:
@ -168,7 +170,7 @@ The `$partition-management-delete-partition` operation can be used to delete an
An HTTP POST to the following URL would be used to invoke this operation: An HTTP POST to the following URL would be used to invoke this operation:
```url ```url
http://example.com/$partition-management-delete-partition http://example.com/DEFAULT/$partition-management-delete-partition
``` ```
The following request body could be used: The following request body could be used:

View File

@ -49,7 +49,7 @@ The CLI tool can be used to start a local, fully functional FHIR server which yo
<img src="/hapi-fhir/docs/images/hapi-fhir-cli-run-server.png" alt="Run Server" style="margin-left: 40px;"/> <img src="/hapi-fhir/docs/images/hapi-fhir-cli-run-server.png" alt="Run Server" style="margin-left: 40px;"/>
Once the server has started, you can access the testing webpage by pointing your browser at <a href="http://localhost:8080/">http://localhost:8080/</a>. The FHIR server base URL will be <a href="http://localhost:8080/baseDstu2/">http://localhost:8080/baseDstu2/</a>. Once the server has started, you can access the testing webpage by pointing your browser at <a href="http://localhost:8080/">http://localhost:8080/</a>. The FHIR server base URL will be <a href="http://localhost:8080/baseDstu3/">http://localhost:8080/baseDstu3/</a>.
Note that by default this server will not be populated with any resources at all. You can easily populate it with the FHIR example resources by <b>leaving it running</b> and opening a second terminal window, then using the <code>hapi-fhir-cli upload-examples</code> command (see the section below). Note that by default this server will not be populated with any resources at all. You can easily populate it with the FHIR example resources by <b>leaving it running</b> and opening a second terminal window, then using the <code>hapi-fhir-cli upload-examples</code> command (see the section below).
@ -59,7 +59,7 @@ The server uses a local Derby database instance for storage. You may want to exe
The <b>upload-examples</b> command downloads the complete set of FHIR example resources from the HL7 website, and uploads them to a server of your choice. This can be useful to populate a server with test data. The <b>upload-examples</b> command downloads the complete set of FHIR example resources from the HL7 website, and uploads them to a server of your choice. This can be useful to populate a server with test data.
To execute this command, uploading test resources to a local CLI server, issue the following: `hapi-fhir-cli upload-examples -t http://localhost:8080/baseDstu2` To execute this command, uploading test resources to a local CLI server, issue the following: `hapi-fhir-cli upload-examples -v dstu3 -t http://localhost:8080/baseDstu3`
Note that this command may take a surprisingly long time to complete because of the large number of examples. Note that this command may take a surprisingly long time to complete because of the large number of examples.

View File

@ -0,0 +1,81 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.ConfigurationException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.base.Charsets;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
public class ChangelogFilesTest {
private static final Logger ourLog = LoggerFactory.getLogger(ChangelogFilesTest.class);
@Test
public void testChangelogFiles() {
Collection<File> files = FileUtils.listFiles(
new File("src/main/resources/ca/uhn/hapi/fhir/changelog"),
new String[]{"yaml"},
true);
for (File next : files) {
ourLog.info("Checking file: {}", next);
String nextFilename = next.getName();
if (nextFilename.equals("changes.yaml")) {
continue;
}
if (nextFilename.equals("version.yaml")) {
continue;
}
if (!nextFilename.matches("[a-zA-Z0-9]+-[a-zA-Z0-9_-]+\\.yaml")) {
fail("Invalid changelog filename: " + next);
}
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
ObjectNode tree;
try (FileInputStream fis = new FileInputStream(next)) {
tree = (ObjectNode) mapper.readTree(new InputStreamReader(fis, Charsets.UTF_8));
} catch (Exception e) {
throw new ConfigurationException("Failed to read " + next, e);
}
List<String> fieldNames = IteratorUtils.toList(tree.fieldNames());
boolean title = fieldNames.remove("title");
assertTrue(title, "No 'title' element in " + next);
boolean type = fieldNames.remove("type");
assertTrue(type, "No 'type' element in " + next);
// this one is optional
boolean haveIssue = fieldNames.remove("issue");
assertThat("Invalid element in " + next + ": " + fieldNames, fieldNames, empty());
if (haveIssue) {
String issue = tree.get("issue").asText();
try {
Integer.parseInt(issue);
} catch (NumberFormatException e) {
fail("Invalid issue value in " + next);
}
}
}
}
}

View File

@ -34,7 +34,7 @@ public interface ITermValueSetConceptViewDao extends JpaRepository<TermValueSetC
@Query("SELECT v FROM TermValueSetConceptView v WHERE v.myConceptValueSetPid = :pid AND v.myConceptOrder >= :from AND v.myConceptOrder < :to ORDER BY v.myConceptOrder") @Query("SELECT v FROM TermValueSetConceptView v WHERE v.myConceptValueSetPid = :pid AND v.myConceptOrder >= :from AND v.myConceptOrder < :to ORDER BY v.myConceptOrder")
List<TermValueSetConceptView> findByTermValueSetId(@Param("from") int theFrom, @Param("to") int theTo, @Param("pid") Long theValueSetId); List<TermValueSetConceptView> findByTermValueSetId(@Param("from") int theFrom, @Param("to") int theTo, @Param("pid") Long theValueSetId);
@Query("SELECT v FROM TermValueSetConceptView v WHERE v.myConceptValueSetPid = :pid AND v.myConceptDisplay LIKE :display ORDER BY v.myConceptOrder") @Query("SELECT v FROM TermValueSetConceptView v WHERE v.myConceptValueSetPid = :pid AND LOWER(v.myConceptDisplay) LIKE :display ORDER BY v.myConceptOrder")
List<TermValueSetConceptView> findByTermValueSetId(@Param("pid") Long theValueSetId, @Param("display") String theDisplay); List<TermValueSetConceptView> findByTermValueSetId(@Param("pid") Long theValueSetId, @Param("display") String theDisplay);
} }

View File

@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.dao.dstu3;
import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
@ -31,216 +30,60 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import org.hl7.fhir.convertors.conv30_40.ValueSet30_40;
import ca.uhn.fhir.util.ElementUtil;
import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IntegerType;
import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.Date; import java.util.Date;
import java.util.List;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoValueSetR4.validateHaveExpansionOrThrowInternalErrorException;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.hl7.fhir.convertors.conv30_40.ValueSet30_40.convertValueSet; import static org.hl7.fhir.convertors.conv30_40.ValueSet30_40.convertValueSet;
public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> { public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> {
private IValidationSupport myValidationSupport;
@Override @Override
public void start() { public org.hl7.fhir.dstu3.model.ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) {
super.start(); org.hl7.fhir.dstu3.model.ValueSet source = read(theId, theRequestDetails);
myValidationSupport = getApplicationContext().getBean(IValidationSupport.class, "myJpaValidationSupportChain");
}
@Override
public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) {
ValueSet source = read(theId, theRequestDetails);
return expand(source, theFilter); return expand(source, theFilter);
} }
@Override @Override
public ValueSet expand(IIdType theId, String theFilter, int theOffset, int theCount, RequestDetails theRequestDetails) { public org.hl7.fhir.dstu3.model.ValueSet expand(IIdType theId, String theFilter, int theOffset, int theCount, RequestDetails theRequestDetails) {
ValueSet source = read(theId, theRequestDetails); org.hl7.fhir.dstu3.model.ValueSet source = read(theId, theRequestDetails);
return expand(source, theFilter, theOffset, theCount); return expand(source, theFilter, theOffset, theCount);
} }
private ValueSet doExpand(ValueSet theSource) { @Override
validateIncludes("include", theSource.getCompose().getInclude()); public org.hl7.fhir.dstu3.model.ValueSet expandByIdentifier(String theUri, String theFilter) {
validateIncludes("exclude", theSource.getCompose().getExclude()); org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(null, theUri, theFilter);
return ValueSet30_40.convertValueSet(canonicalOutput);
IValidationSupport.ValueSetExpansionOutcome retVal = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, theSource);
validateHaveExpansionOrThrowInternalErrorException(retVal);
return (ValueSet) retVal.getValueSet();
}
private ValueSet doExpand(ValueSet theSource, int theOffset, int theCount) {
validateIncludes("include", theSource.getCompose().getInclude());
validateIncludes("exclude", theSource.getCompose().getExclude());
ValueSetExpansionOptions options = new ValueSetExpansionOptions()
.setOffset(theOffset)
.setCount(theCount);
IValidationSupport.ValueSetExpansionOutcome retVal = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, theSource);
validateHaveExpansionOrThrowInternalErrorException(retVal);
return (ValueSet) retVal.getValueSet();
}
private void validateIncludes(String name, List<ConceptSetComponent> listToValidate) {
for (ConceptSetComponent nextExclude : listToValidate) {
if (isBlank(nextExclude.getSystem()) && nextExclude.getValueSet().isEmpty() && !ElementUtil.isEmpty(nextExclude.getConcept(), nextExclude.getFilter())) {
throw new InvalidRequestException("ValueSet contains " + name + " criteria with no system defined");
}
}
} }
@Override @Override
public ValueSet expandByIdentifier(String theUri, String theFilter) { public org.hl7.fhir.dstu3.model.ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) {
if (isBlank(theUri)) { ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(theOffset, theCount);
throw new InvalidRequestException("URI must not be blank or missing"); org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(options, theUri, theFilter);
} return ValueSet30_40.convertValueSet(canonicalOutput);
ValueSet source = new ValueSet();
source.setUrl(theUri);
if (isNotBlank(theFilter)) {
ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter();
filter.setProperty("display");
filter.setOp(FilterOperator.EQUAL);
filter.setValue(theFilter);
} else {
source.getCompose().addInclude().addValueSet(theUri);
}
return doExpand(source);
// if (defaultValueSet != null) {
// source = getContext().newJsonParser().parseResource(ValueSet.class, getContext().newJsonParser().encodeResourceToString(defaultValueSet));
// } else {
// IBundleProvider ids = search(ValueSet.SP_URL, new UriParam(theUri));
// if (ids.size() == 0) {
// throw new InvalidRequestException("Unknown ValueSet URI: " + theUri);
// }
// source = (ValueSet) ids.getResources(0, 1).get(0);
// }
//
// return expand(defaultValueSet, theFilter);
} }
@Override @Override
public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) { public org.hl7.fhir.dstu3.model.ValueSet expand(org.hl7.fhir.dstu3.model.ValueSet theSource, String theFilter) {
if (isBlank(theUri)) { org.hl7.fhir.r4.model.ValueSet canonicalInput = ValueSet30_40.convertValueSet(theSource);
throw new InvalidRequestException("URI must not be blank or missing"); org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(null, canonicalInput, theFilter);
} return ValueSet30_40.convertValueSet(canonicalOutput);
ValueSet source = new ValueSet();
source.setUrl(theUri);
if (isNotBlank(theFilter)) {
ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter();
filter.setProperty("display");
filter.setOp(FilterOperator.EQUAL);
filter.setValue(theFilter);
} else {
source.getCompose().addInclude().addValueSet(theUri);
}
return doExpand(source, theOffset, theCount);
} }
@Override @Override
public ValueSet expand(ValueSet theSource, String theFilter) { public org.hl7.fhir.dstu3.model.ValueSet expand(org.hl7.fhir.dstu3.model.ValueSet theSource, String theFilter, int theOffset, int theCount) {
ValueSet toExpand = new ValueSet(); ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(theOffset, theCount);
org.hl7.fhir.r4.model.ValueSet canonicalInput = ValueSet30_40.convertValueSet(theSource);
// for (UriType next : theSource.getCompose().getInclude()) { org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(options, canonicalInput, theFilter);
// ConceptSetComponent include = toExpand.getCompose().addInclude(); return ValueSet30_40.convertValueSet(canonicalOutput);
// include.setSystem(next.getValue());
// addFilterIfPresent(theFilter, include);
// }
for (ConceptSetComponent next : theSource.getCompose().getInclude()) {
toExpand.getCompose().addInclude(next);
addFilterIfPresent(theFilter, next);
}
if (toExpand.getCompose().isEmpty()) {
throw new InvalidRequestException("ValueSet does not have any compose.include or compose.import values, can not expand");
}
toExpand.getCompose().getExclude().addAll(theSource.getCompose().getExclude());
ValueSet retVal = doExpand(toExpand);
if (isNotBlank(theFilter)) {
applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter);
}
return retVal;
}
@Override
public ValueSet expand(ValueSet theSource, String theFilter, int theOffset, int theCount) {
ValueSet toExpand = new ValueSet();
toExpand.setId(theSource.getId());
toExpand.setUrl(theSource.getUrl());
if (theSource.getVersion() != null) {
toExpand.setVersion(theSource.getVersion());
}
for (ConceptSetComponent next : theSource.getCompose().getInclude()) {
toExpand.getCompose().addInclude(next);
addFilterIfPresent(theFilter, next);
}
if (toExpand.getCompose().isEmpty()) {
throw new InvalidRequestException("ValueSet does not have any compose.include or compose.import values, can not expand");
}
toExpand.getCompose().getExclude().addAll(theSource.getCompose().getExclude());
ValueSet retVal = doExpand(toExpand, theOffset, theCount);
if (isNotBlank(theFilter)) {
applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter);
}
return retVal;
}
private void applyFilter(IntegerType theTotalElement, List<ValueSetExpansionContainsComponent> theContains, String theFilter) {
for (int idx = 0; idx < theContains.size(); idx++) {
ValueSetExpansionContainsComponent next = theContains.get(idx);
if (isBlank(next.getDisplay()) || !org.apache.commons.lang3.StringUtils.containsIgnoreCase(next.getDisplay(), theFilter)) {
theContains.remove(idx);
idx--;
if (theTotalElement.getValue() != null) {
theTotalElement.setValue(theTotalElement.getValue() - 1);
}
}
applyFilter(theTotalElement, next.getContains(), theFilter);
}
}
private void addFilterIfPresent(String theFilter, ConceptSetComponent include) {
if (ElementUtil.isEmpty(include.getConcept())) {
if (isNotBlank(theFilter)) {
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter);
}
}
} }
@Override @Override

View File

@ -206,7 +206,7 @@ public class SearchParamWithInlineReferencesExtractor {
for (String nextQueryString : queryStringsToPopulate) { for (String nextQueryString : queryStringsToPopulate) {
if (isNotBlank(nextQueryString)) { if (isNotBlank(nextQueryString)) {
ourLog.trace("Adding composite unique SP: {}", nextQueryString); ourLog.trace("Adding composite unique SP: {}", nextQueryString);
theParams.myCompositeStringUniques.add(new ResourceIndexedCompositeStringUnique(theEntity, nextQueryString)); theParams.myCompositeStringUniques.add(new ResourceIndexedCompositeStringUnique(theEntity, nextQueryString, next.getId()));
} }
} }
} }
@ -289,7 +289,13 @@ public class SearchParamWithInlineReferencesExtractor {
if (myDaoConfig.isUniqueIndexesCheckedBeforeSave()) { if (myDaoConfig.isUniqueIndexesCheckedBeforeSave()) {
ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString()); ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
if (existing != null) { if (existing != null) {
String msg = myContext.getLocalizer().getMessage(BaseHapiFhirDao.class, "uniqueIndexConflictFailure", theEntity.getResourceType(), next.getIndexString(), existing.getResource().getIdDt().toUnqualifiedVersionless().getValue());
String searchParameterId = "(unknown)";
if (next.getSearchParameterId() != null) {
searchParameterId = next.getSearchParameterId().toUnqualifiedVersionless().getValue();
}
String msg = myContext.getLocalizer().getMessage(BaseHapiFhirDao.class, "uniqueIndexConflictFailure", theEntity.getResourceType(), next.getIndexString(), existing.getResource().getIdDt().toUnqualifiedVersionless().getValue(), searchParameterId);
throw new PreconditionFailedException(msg); throw new PreconditionFailedException(msg);
} }
} }

View File

@ -205,7 +205,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
*/ */
if (modifier == TokenParamModifier.IN) { if (modifier == TokenParamModifier.IN) {
codes.addAll(myTerminologySvc.expandValueSet(null, code)); codes.addAll(myTerminologySvc.expandValueSetIntoConceptList(null, code));
} else if (modifier == TokenParamModifier.ABOVE) { } else if (modifier == TokenParamModifier.ABOVE) {
system = determineSystemIfMissing(theSearchParam, code, system); system = determineSystemIfMissing(theSearchParam, code, system);
validateHaveSystemAndCodeForToken(paramName, code, system); validateHaveSystemAndCodeForToken(paramName, code, system);
@ -273,7 +273,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
String valueSet = valueSetUris.iterator().next(); String valueSet = valueSetUris.iterator().next();
ValueSetExpansionOptions options = new ValueSetExpansionOptions() ValueSetExpansionOptions options = new ValueSetExpansionOptions()
.setFailOnMissingCodeSystem(false); .setFailOnMissingCodeSystem(false);
List<FhirVersionIndependentConcept> candidateCodes = myTerminologySvc.expandValueSet(options, valueSet); List<FhirVersionIndependentConcept> candidateCodes = myTerminologySvc.expandValueSetIntoConceptList(options, valueSet);
for (FhirVersionIndependentConcept nextCandidate : candidateCodes) { for (FhirVersionIndependentConcept nextCandidate : candidateCodes) {
if (nextCandidate.getCode().equals(code)) { if (nextCandidate.getCode().equals(code)) {
retVal = nextCandidate.getSystem(); retVal = nextCandidate.getSystem();

View File

@ -21,48 +21,28 @@ package ca.uhn.fhir.jpa.dao.r4;
*/ */
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.ElementUtil;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r4.model.ValueSet.FilterOperator;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent;
import java.util.Date; import java.util.Date;
import java.util.List;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> { public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> {
private IValidationSupport myValidationSupport;
@Override
public void start() {
super.start();
myValidationSupport = getApplicationContext().getBean(IValidationSupport.class, "myJpaValidationSupportChain");
}
@Override @Override
public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) { public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) {
ValueSet source = read(theId, theRequestDetails); ValueSet source = read(theId, theRequestDetails);
@ -75,156 +55,26 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao<ValueSet>
return expand(source, theFilter, theOffset, theCount); return expand(source, theFilter, theOffset, theCount);
} }
private ValueSet doExpand(ValueSet theSource) {
IValidationSupport.ValueSetExpansionOutcome retVal = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, theSource);
validateHaveExpansionOrThrowInternalErrorException(retVal);
return (ValueSet) retVal.getValueSet();
}
private ValueSet doExpand(ValueSet theSource, int theOffset, int theCount) {
ValueSetExpansionOptions options = new ValueSetExpansionOptions()
.setOffset(theOffset)
.setCount(theCount);
IValidationSupport.ValueSetExpansionOutcome retVal = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, theSource);
validateHaveExpansionOrThrowInternalErrorException(retVal);
return (ValueSet) retVal.getValueSet();
}
@Override @Override
public ValueSet expandByIdentifier(String theUri, String theFilter) { public ValueSet expandByIdentifier(String theUri, String theFilter) {
if (isBlank(theUri)) { return myTerminologySvc.expandValueSet(null, theUri, theFilter);
throw new InvalidRequestException("URI must not be blank or missing");
}
ValueSet source = new ValueSet();
source.setUrl(theUri);
if (isNotBlank(theFilter)) {
ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter();
filter.setProperty("display");
filter.setOp(FilterOperator.EQUAL);
filter.setValue(theFilter);
} else {
source.getCompose().addInclude().addValueSet(theUri);
}
return doExpand(source);
// if (defaultValueSet != null) {
// source = getContext().newJsonParser().parseResource(ValueSet.class, getContext().newJsonParser().encodeResourceToString(defaultValueSet));
// } else {
// IBundleProvider ids = search(ValueSet.SP_URL, new UriParam(theUri));
// if (ids.size() == 0) {
// throw new InvalidRequestException("Unknown ValueSet URI: " + theUri);
// }
// source = (ValueSet) ids.getResources(0, 1).get(0);
// }
//
// return expand(defaultValueSet, theFilter);
} }
@Override @Override
public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) { public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) {
if (isBlank(theUri)) { ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(theOffset, theCount);
throw new InvalidRequestException("URI must not be blank or missing"); return myTerminologySvc.expandValueSet(options, theUri, theFilter);
}
ValueSet source = new ValueSet();
source.setUrl(theUri);
if (isNotBlank(theFilter)) {
ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter();
filter.setProperty("display");
filter.setOp(FilterOperator.EQUAL);
filter.setValue(theFilter);
} else {
source.getCompose().addInclude().addValueSet(theUri);
}
return doExpand(source, theOffset, theCount);
} }
@Override @Override
public ValueSet expand(ValueSet theSource, String theFilter) { public ValueSet expand(ValueSet theSource, String theFilter) {
ValueSet toExpand = new ValueSet(); return myTerminologySvc.expandValueSet(null, theSource, theFilter);
// for (UriType next : theSource.getCompose().getInclude()) {
// ConceptSetComponent include = toExpand.getCompose().addInclude();
// include.setSystem(next.getValue());
// addFilterIfPresent(theFilter, include);
// }
for (ConceptSetComponent next : theSource.getCompose().getInclude()) {
toExpand.getCompose().addInclude(next);
addFilterIfPresent(theFilter, next);
}
if (toExpand.getCompose().isEmpty()) {
throw new InvalidRequestException("ValueSet does not have any compose.include or compose.import values, can not expand");
}
toExpand.getCompose().getExclude().addAll(theSource.getCompose().getExclude());
ValueSet retVal = doExpand(toExpand);
if (isNotBlank(theFilter)) {
applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter);
}
return retVal;
} }
@Override @Override
public ValueSet expand(ValueSet theSource, String theFilter, int theOffset, int theCount) { public ValueSet expand(ValueSet theSource, String theFilter, int theOffset, int theCount) {
ValueSet toExpand = new ValueSet(); ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(theOffset, theCount);
toExpand.setId(theSource.getId()); return myTerminologySvc.expandValueSet(options, theSource, theFilter);
toExpand.setUrl(theSource.getUrl());
if (theSource.getVersion() != null) {
toExpand.setVersion(theSource.getVersion());
}
for (ConceptSetComponent next : theSource.getCompose().getInclude()) {
toExpand.getCompose().addInclude(next);
addFilterIfPresent(theFilter, next);
}
if (toExpand.getCompose().isEmpty()) {
throw new InvalidRequestException("ValueSet does not have any compose.include or compose.import values, can not expand");
}
toExpand.getCompose().getExclude().addAll(theSource.getCompose().getExclude());
ValueSet retVal = doExpand(toExpand, theOffset, theCount);
if (isNotBlank(theFilter)) {
applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter);
}
return retVal;
}
private void applyFilter(IntegerType theTotalElement, List<ValueSetExpansionContainsComponent> theContains, String theFilter) {
for (int idx = 0; idx < theContains.size(); idx++) {
ValueSetExpansionContainsComponent next = theContains.get(idx);
if (isBlank(next.getDisplay()) || !org.apache.commons.lang3.StringUtils.containsIgnoreCase(next.getDisplay(), theFilter)) {
theContains.remove(idx);
idx--;
if (theTotalElement.getValue() != null) {
theTotalElement.setValue(theTotalElement.getValue() - 1);
}
}
applyFilter(theTotalElement, next.getContains(), theFilter);
}
}
private void addFilterIfPresent(String theFilter, ConceptSetComponent include) {
if (ElementUtil.isEmpty(include.getConcept())) {
if (isNotBlank(theFilter)) {
include.addFilter().setProperty(JpaConstants.VALUESET_FILTER_DISPLAY).setOp(FilterOperator.EQUAL).setValue(theFilter);
}
}
} }
@Override @Override

View File

@ -21,47 +21,28 @@ package ca.uhn.fhir.jpa.dao.r5;
*/ */
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.util.ElementUtil; import org.hl7.fhir.convertors.conv40_50.ValueSet40_50;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Enumerations;
import org.hl7.fhir.r5.model.IntegerType;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import java.util.Date; import java.util.Date;
import java.util.List;
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoValueSetR4.validateHaveExpansionOrThrowInternalErrorException;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> { public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> {
private IValidationSupport myValidationSupport;
@Override
public void start() {
super.start();
myValidationSupport = getApplicationContext().getBean(IValidationSupport.class,"myJpaValidationSupportChain" );
}
@Override @Override
public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) { public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) {
ValueSet source = read(theId, theRequestDetails); ValueSet source = read(theId, theRequestDetails);
@ -74,157 +55,32 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao<ValueSet>
return expand(source, theFilter, theOffset, theCount); return expand(source, theFilter, theOffset, theCount);
} }
private ValueSet doExpand(ValueSet theSource) {
IValidationSupport.ValueSetExpansionOutcome retVal = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, theSource);
validateHaveExpansionOrThrowInternalErrorException(retVal);
return (ValueSet) retVal.getValueSet();
}
private ValueSet doExpand(ValueSet theSource, int theOffset, int theCount) {
ValueSetExpansionOptions options = new ValueSetExpansionOptions()
.setOffset(theOffset)
.setCount(theCount);
IValidationSupport.ValueSetExpansionOutcome retVal = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, theSource);
validateHaveExpansionOrThrowInternalErrorException(retVal);
return (ValueSet) retVal.getValueSet();
}
@Override @Override
public ValueSet expandByIdentifier(String theUri, String theFilter) { public ValueSet expandByIdentifier(String theUri, String theFilter) {
if (isBlank(theUri)) { org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(null, theUri, theFilter);
throw new InvalidRequestException("URI must not be blank or missing"); return ValueSet40_50.convertValueSet(canonicalOutput);
}
ValueSet source = new ValueSet();
source.setUrl(theUri);
if (isNotBlank(theFilter)) {
ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter();
filter.setProperty("display");
filter.setOp(Enumerations.FilterOperator.EQUAL);
filter.setValue(theFilter);
} else {
source.getCompose().addInclude().addValueSet(theUri);
}
ValueSet retVal = doExpand(source);
return retVal;
// if (defaultValueSet != null) {
// source = getContext().newJsonParser().parseResource(ValueSet.class, getContext().newJsonParser().encodeResourceToString(defaultValueSet));
// } else {
// IBundleProvider ids = search(ValueSet.SP_URL, new UriParam(theUri));
// if (ids.size() == 0) {
// throw new InvalidRequestException("Unknown ValueSet URI: " + theUri);
// }
// source = (ValueSet) ids.getResources(0, 1).get(0);
// }
//
// return expand(defaultValueSet, theFilter);
} }
@Override @Override
public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) { public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) {
if (isBlank(theUri)) { ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(theOffset, theCount);
throw new InvalidRequestException("URI must not be blank or missing"); org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(options, theUri, theFilter);
} return ValueSet40_50.convertValueSet(canonicalOutput);
ValueSet source = new ValueSet();
source.setUrl(theUri);
if (isNotBlank(theFilter)) {
ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter();
filter.setProperty("display");
filter.setOp(Enumerations.FilterOperator.EQUAL);
filter.setValue(theFilter);
} else {
source.getCompose().addInclude().addValueSet(theUri);
}
return doExpand(source, theOffset, theCount);
} }
@Override @Override
public ValueSet expand(ValueSet theSource, String theFilter) { public ValueSet expand(ValueSet theSource, String theFilter) {
ValueSet toExpand = new ValueSet(); org.hl7.fhir.r4.model.ValueSet canonicalInput = ValueSet40_50.convertValueSet(theSource);
org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(null, canonicalInput, theFilter);
// for (UriType next : theSource.getCompose().getInclude()) { return ValueSet40_50.convertValueSet(canonicalOutput);
// ConceptSetComponent include = toExpand.getCompose().addInclude();
// include.setSystem(next.getValue());
// addFilterIfPresent(theFilter, include);
// }
for (ConceptSetComponent next : theSource.getCompose().getInclude()) {
toExpand.getCompose().addInclude(next);
addFilterIfPresent(theFilter, next);
}
if (toExpand.getCompose().isEmpty()) {
throw new InvalidRequestException("ValueSet does not have any compose.include or compose.import values, can not expand");
}
toExpand.getCompose().getExclude().addAll(theSource.getCompose().getExclude());
ValueSet retVal = doExpand(toExpand);
if (isNotBlank(theFilter)) {
applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter);
}
return retVal;
} }
@Override @Override
public ValueSet expand(ValueSet theSource, String theFilter, int theOffset, int theCount) { public ValueSet expand(ValueSet theSource, String theFilter, int theOffset, int theCount) {
ValueSet toExpand = new ValueSet(); ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(theOffset, theCount);
toExpand.setId(theSource.getId()); org.hl7.fhir.r4.model.ValueSet canonicalInput = ValueSet40_50.convertValueSet(theSource);
toExpand.setUrl(theSource.getUrl()); org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(options, canonicalInput, theFilter);
if (theSource.getVersion() != null) { return ValueSet40_50.convertValueSet(canonicalOutput);
toExpand.setVersion(theSource.getVersion());
}
for (ConceptSetComponent next : theSource.getCompose().getInclude()) {
toExpand.getCompose().addInclude(next);
addFilterIfPresent(theFilter, next);
}
if (toExpand.getCompose().isEmpty()) {
throw new InvalidRequestException("ValueSet does not have any compose.include or compose.import values, can not expand");
}
toExpand.getCompose().getExclude().addAll(theSource.getCompose().getExclude());
ValueSet retVal = doExpand(toExpand, theOffset, theCount);
if (isNotBlank(theFilter)) {
applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter);
}
return retVal;
}
private void applyFilter(IntegerType theTotalElement, List<ValueSetExpansionContainsComponent> theContains, String theFilter) {
for (int idx = 0; idx < theContains.size(); idx++) {
ValueSetExpansionContainsComponent next = theContains.get(idx);
if (isBlank(next.getDisplay()) || !org.apache.commons.lang3.StringUtils.containsIgnoreCase(next.getDisplay(), theFilter)) {
theContains.remove(idx);
idx--;
if (theTotalElement.getValue() != null) {
theTotalElement.setValue(theTotalElement.getValue() - 1);
}
}
applyFilter(theTotalElement, next.getContains(), theFilter);
}
}
private void addFilterIfPresent(String theFilter, ConceptSetComponent include) {
if (ElementUtil.isEmpty(include.getConcept())) {
if (isNotBlank(theFilter)) {
include.addFilter().setProperty("display").setOp(Enumerations.FilterOperator.EQUAL).setValue(theFilter);
}
}
} }
@Override @Override

View File

@ -74,6 +74,7 @@ public class TermConcept implements Serializable {
@Fields({ @Fields({
@Field(name = "myDisplay", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")), @Field(name = "myDisplay", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")),
@Field(name = "myDisplayEdgeNGram", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompleteEdgeAnalyzer")), @Field(name = "myDisplayEdgeNGram", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompleteEdgeAnalyzer")),
@Field(name = "myDisplayWordEdgeNGram", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompleteWordEdgeAnalyzer")),
@Field(name = "myDisplayNGram", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompleteNGramAnalyzer")), @Field(name = "myDisplayNGram", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompleteNGramAnalyzer")),
@Field(name = "myDisplayPhonetic", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompletePhoneticAnalyzer")) @Field(name = "myDisplayPhonetic", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompletePhoneticAnalyzer"))
}) })

View File

@ -48,6 +48,12 @@ public class LuceneSearchMappingFactory {
.filter(EdgeNGramFilterFactory.class) .filter(EdgeNGramFilterFactory.class)
.param("minGramSize", "3") .param("minGramSize", "3")
.param("maxGramSize", "50") .param("maxGramSize", "50")
.analyzerDef("autocompleteWordEdgeAnalyzer", StandardTokenizerFactory.class)
.filter(LowerCaseFilterFactory.class)
.filter(StopFilterFactory.class)
.filter(EdgeNGramFilterFactory.class)
.param("minGramSize", "2")
.param("maxGramSize", "20")
.analyzerDef("autocompletePhoneticAnalyzer", StandardTokenizerFactory.class) .analyzerDef("autocompletePhoneticAnalyzer", StandardTokenizerFactory.class)
.filter(StandardFilterFactory.class) .filter(StandardFilterFactory.class)
.filter(StopFilterFactory.class) .filter(StopFilterFactory.class)

View File

@ -154,7 +154,7 @@ public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder {
*/ */
if (modifier == TokenParamModifier.IN) { if (modifier == TokenParamModifier.IN) {
codes.addAll(myTerminologySvc.expandValueSet(null, code)); codes.addAll(myTerminologySvc.expandValueSetIntoConceptList(null, code));
} else if (modifier == TokenParamModifier.ABOVE) { } else if (modifier == TokenParamModifier.ABOVE) {
system = determineSystemIfMissing(theSearchParam, code, system); system = determineSystemIfMissing(theSearchParam, code, system);
validateHaveSystemAndCodeForToken(paramName, code, system); validateHaveSystemAndCodeForToken(paramName, code, system);
@ -228,7 +228,7 @@ public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder {
String valueSet = valueSetUris.iterator().next(); String valueSet = valueSetUris.iterator().next();
ValueSetExpansionOptions options = new ValueSetExpansionOptions() ValueSetExpansionOptions options = new ValueSetExpansionOptions()
.setFailOnMissingCodeSystem(false); .setFailOnMissingCodeSystem(false);
List<FhirVersionIndependentConcept> candidateCodes = myTerminologySvc.expandValueSet(options, valueSet); List<FhirVersionIndependentConcept> candidateCodes = myTerminologySvc.expandValueSetIntoConceptList(options, valueSet);
for (FhirVersionIndependentConcept nextCandidate : candidateCodes) { for (FhirVersionIndependentConcept nextCandidate : candidateCodes) {
if (nextCandidate.getCode().equals(code)) { if (nextCandidate.getCode().equals(code)) {
retVal = nextCandidate.getSystem(); retVal = nextCandidate.getSystem();

View File

@ -36,6 +36,14 @@ public class ElasticsearchMappingProvider implements ElasticsearchAnalysisDefini
.param("min_gram", "3") .param("min_gram", "3")
.param("max_gram", "50"); .param("max_gram", "50");
builder.analyzer("autocompleteWordEdgeAnalyzer")
.withTokenizer("standard")
.withTokenFilters("lowercase", "stop", "wordedgengram_3_50");
builder.tokenFilter("wordedgengram_3_50")
.type("edgeNGram")
.param("min_gram", "2")
.param("max_gram", "20");
builder.analyzer("autocompletePhoneticAnalyzer") builder.analyzer("autocompletePhoneticAnalyzer")
.withTokenizer("standard") .withTokenizer("standard")
.withTokenFilters("standard", "stop", "snowball_english"); .withTokenFilters("standard", "stop", "snowball_english");

View File

@ -67,6 +67,7 @@ import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
@ -183,6 +184,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.isNoneBlank; import static org.apache.commons.lang3.StringUtils.isNoneBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.lowerCase;
public abstract class BaseTermReadSvcImpl implements ITermReadSvc { public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
public static final int DEFAULT_FETCH_SIZE = 250; public static final int DEFAULT_FETCH_SIZE = 250;
@ -394,25 +396,44 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
@Override @Override
public List<FhirVersionIndependentConcept> expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet) { @Transactional
ExpansionFilter expansionFilter = ExpansionFilter.NO_FILTER; public List<FhirVersionIndependentConcept> expandValueSetIntoConceptList(@Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull String theValueSetCanonicalUrl) {
return expandValueSet(theExpansionOptions, theValueSet, expansionFilter); String expansionFilter = null;
}
private List<FhirVersionIndependentConcept> expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet, ExpansionFilter theExpansionFilter) {
// TODO: DM 2019-09-10 - This is problematic because an incorrect URL that matches ValueSet.id will not be found in the terminology tables but will yield a ValueSet here. Depending on the ValueSet, the expansion may time-out. // TODO: DM 2019-09-10 - This is problematic because an incorrect URL that matches ValueSet.id will not be found in the terminology tables but will yield a ValueSet here. Depending on the ValueSet, the expansion may time-out.
ValueSet valueSet = fetchCanonicalValueSetFromCompleteContext(theValueSet); ValueSet expanded = expandValueSet(theExpansionOptions, theValueSetCanonicalUrl, expansionFilter);
ArrayList<FhirVersionIndependentConcept> retVal = new ArrayList<>();
for (ValueSet.ValueSetExpansionContainsComponent nextContains : expanded.getExpansion().getContains()) {
retVal.add(new FhirVersionIndependentConcept(nextContains.getSystem(), nextContains.getCode(), nextContains.getDisplay(), nextContains.getVersion()));
}
return retVal;
}
@Override
@Transactional
public ValueSet expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull String theValueSetCanonicalUrl, @Nullable String theExpansionFilter) {
ValueSet valueSet = fetchCanonicalValueSetFromCompleteContext(theValueSetCanonicalUrl);
if (valueSet == null) { if (valueSet == null) {
throwInvalidValueSet(theValueSet); throw new ResourceNotFoundException("Unknown ValueSet: " + UrlUtil.escapeUrlParam(theValueSetCanonicalUrl));
} }
return expandValueSetAndReturnVersionIndependentConcepts(theExpansionOptions, valueSet, theExpansionFilter); return expandValueSet(theExpansionOptions, valueSet, theExpansionFilter);
} }
@Override @Override
@Transactional(propagation = Propagation.REQUIRED) @Transactional(propagation = Propagation.REQUIRED)
public ValueSet expandValueSet(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand) { public ValueSet expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull ValueSet theValueSetToExpand) {
return expandValueSet(theExpansionOptions, theValueSetToExpand, (String) null);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public ValueSet expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull ValueSet theValueSetToExpand, @Nullable String theFilter) {
return expandValueSet(theExpansionOptions, theValueSetToExpand, ExpansionFilter.fromFilterString(theFilter));
}
private ValueSet expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, ExpansionFilter theFilter) {
ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSetToExpand, "ValueSet to expand can not be null"); ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSetToExpand, "ValueSet to expand can not be null");
ValueSetExpansionOptions expansionOptions = provideExpansionOptions(theExpansionOptions); ValueSetExpansionOptions expansionOptions = provideExpansionOptions(theExpansionOptions);
@ -431,9 +452,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
accumulator.addParameter().setName("count").setValue(new IntegerType(count)); accumulator.addParameter().setName("count").setValue(new IntegerType(count));
} }
ExpansionFilter filter = ExpansionFilter.NO_FILTER;
expandValueSetIntoAccumulator(theValueSetToExpand, theExpansionOptions, accumulator, filter, true); expandValueSetIntoAccumulator(theValueSetToExpand, theExpansionOptions, accumulator, theFilter, true);
if (accumulator.getTotalConcepts() != null) { if (accumulator.getTotalConcepts() != null) {
accumulator.setTotal(accumulator.getTotalConcepts()); accumulator.setTotal(accumulator.getTotalConcepts());
@ -449,7 +469,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
.setUrl(HapiExtensions.EXT_VALUESET_EXPANSION_MESSAGE) .setUrl(HapiExtensions.EXT_VALUESET_EXPANSION_MESSAGE)
.setValue(new StringType(next)); .setValue(new StringType(next));
} }
return valueSet; return valueSet;
} }
@ -513,6 +532,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
boolean wasFilteredResult = false; boolean wasFilteredResult = false;
if (!theFilter.getFilters().isEmpty() && JpaConstants.VALUESET_FILTER_DISPLAY.equals(theFilter.getFilters().get(0).getProperty()) && theFilter.getFilters().get(0).getOp() == ValueSet.FilterOperator.EQUAL) { if (!theFilter.getFilters().isEmpty() && JpaConstants.VALUESET_FILTER_DISPLAY.equals(theFilter.getFilters().get(0).getProperty()) && theFilter.getFilters().get(0).getOp() == ValueSet.FilterOperator.EQUAL) {
String displayValue = theFilter.getFilters().get(0).getValue().replace("%", "[%]") + "%"; String displayValue = theFilter.getFilters().get(0).getValue().replace("%", "[%]") + "%";
displayValue = lowerCase(displayValue);
conceptViews = myTermValueSetConceptViewDao.findByTermValueSetId(theTermValueSet.getId(), displayValue); conceptViews = myTermValueSetConceptViewDao.findByTermValueSetId(theTermValueSet.getId(), displayValue);
wasFilteredResult = true; wasFilteredResult = true;
} else { } else {
@ -675,7 +695,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
if (TransactionSynchronizationManager.isSynchronizationActive()) { if (TransactionSynchronizationManager.isSynchronizationActive()) {
return theAction.get(); return theAction.get();
} }
return myTxTemplate.execute(t->theAction.get()); return myTxTemplate.execute(t -> theAction.get());
} }
private String getValueSetInfo(ValueSet theValueSet) { private String getValueSetInfo(ValueSet theValueSet) {
@ -702,18 +722,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
return sb.toString(); return sb.toString();
} }
protected List<FhirVersionIndependentConcept> expandValueSetAndReturnVersionIndependentConcepts(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpandR4, @Nonnull ExpansionFilter theExpansionFilter) {
int maxCapacity = myDaoConfig.getMaximumExpansionSize();
ValueSetExpansionComponentWithConceptAccumulator accumulator = new ValueSetExpansionComponentWithConceptAccumulator(myContext, maxCapacity);
expandValueSet(theExpansionOptions, theValueSetToExpandR4, accumulator, theExpansionFilter);
ArrayList<FhirVersionIndependentConcept> retVal = new ArrayList<>();
for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent nextContains : accumulator.getContains()) {
retVal.add(new FhirVersionIndependentConcept(nextContains.getSystem(), nextContains.getCode(), nextContains.getDisplay(), nextContains.getVersion()));
}
return retVal;
}
/** /**
* @return Returns true if there are potentially more results to process. * @return Returns true if there are potentially more results to process.
*/ */
@ -938,12 +946,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
* DM 2019-08-21 - Processing slows after any ValueSets with many codes explicitly identified. This might * DM 2019-08-21 - Processing slows after any ValueSets with many codes explicitly identified. This might
* be due to the dark arts that is memory management. Will monitor but not do anything about this right now. * be due to the dark arts that is memory management. Will monitor but not do anything about this right now.
*/ */
BooleanQuery.setMaxClauseCount(10000); BooleanQuery.setMaxClauseCount(SearchBuilder.getMaximumPageSize());
StopWatch sw = new StopWatch(); StopWatch sw = new StopWatch();
AtomicInteger count = new AtomicInteger(0); AtomicInteger count = new AtomicInteger(0);
int maxResultsPerBatch = 10000; int maxResultsPerBatch = SearchBuilder.getMaximumPageSize();
/* /*
* If the accumulator is bounded, we may reduce the size of the query to * If the accumulator is bounded, we may reduce the size of the query to
@ -1083,8 +1091,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
.phrase() .phrase()
.withSlop(2) .withSlop(2)
.onField("myDisplay").boostedTo(4.0f) .onField("myDisplay").boostedTo(4.0f)
.andField("myDisplayEdgeNGram").boostedTo(2.0f) //.andField("myDisplayEdgeNGram").boostedTo(2.0f)
// .andField("myDisplayNGram").boostedTo(1.0f) .andField("myDisplayWordEdgeNGram").boostedTo(1.0f)
// .andField("myDisplayPhonetic").boostedTo(0.5f) // .andField("myDisplayPhonetic").boostedTo(0.5f)
.sentence(nextFilter.getValue().toLowerCase()).createQuery(); .sentence(nextFilter.getValue().toLowerCase()).createQuery();
bool.must(textQuery); bool.must(textQuery);
@ -1395,7 +1403,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
} }
} }
return createFailureCodeValidationResult(theSystem, theCode, " - Concept Display \"" + theDisplay + "\" does not match expected \"" + concepts.get(0).getDisplay() + "\"").setDisplay(concepts.get(0).getDisplay()); return createFailureCodeValidationResult(theSystem, theCode, " - Concept Display \"" + theDisplay + "\" does not match expected \"" + concepts.get(0).getDisplay() + "\"").setDisplay(concepts.get(0).getDisplay());
} }
if (!concepts.isEmpty()) { if (!concepts.isEmpty()) {
@ -1424,7 +1432,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
int versionIndex = theSystem.indexOf("|"); int versionIndex = theSystem.indexOf("|");
if (versionIndex >= 0) { if (versionIndex >= 0) {
String systemUrl = theSystem.substring(0, versionIndex); String systemUrl = theSystem.substring(0, versionIndex);
String systemVersion = theSystem.substring(versionIndex+1); String systemVersion = theSystem.substring(versionIndex + 1);
optionalTermValueSetConcept = myValueSetConceptDao.findByValueSetResourcePidSystemAndCodeWithVersion(theResourcePid.getIdAsLong(), systemUrl, systemVersion, theCode); optionalTermValueSetConcept = myValueSetConceptDao.findByValueSetResourcePidSystemAndCodeWithVersion(theResourcePid.getIdAsLong(), systemUrl, systemVersion, theCode);
} else { } else {
optionalTermValueSetConcept = myValueSetConceptDao.findByValueSetResourcePidSystemAndCode(theResourcePid.getIdAsLong(), theSystem, theCode); optionalTermValueSetConcept = myValueSetConceptDao.findByValueSetResourcePidSystemAndCode(theResourcePid.getIdAsLong(), theSystem, theCode);
@ -1526,7 +1534,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
private String getUrlFromIdentifier(String theUri) { private String getUrlFromIdentifier(String theUri) {
String retVal = theUri; String retVal = theUri;
if (StringUtils.isNotEmpty((theUri))){ if (StringUtils.isNotEmpty((theUri))) {
int versionSeparator = theUri.lastIndexOf('|'); int versionSeparator = theUri.lastIndexOf('|');
if (versionSeparator != -1) { if (versionSeparator != -1) {
retVal = theUri.substring(0, versionSeparator); retVal = theUri.substring(0, versionSeparator);
@ -1781,9 +1789,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
} else { } else {
String msg = myContext.getLocalizer().getMessage( String msg = myContext.getLocalizer().getMessage(
BaseTermReadSvcImpl.class, BaseTermReadSvcImpl.class,
"cannotCreateDuplicateConceptMapUrlAndVersion", "cannotCreateDuplicateConceptMapUrlAndVersion",
conceptMapUrl, conceptMapVersion, conceptMapUrl, conceptMapVersion,
existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue()); existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue());
throw new UnprocessableEntityException(msg); throw new UnprocessableEntityException(msg);
} }
} }
@ -1818,7 +1826,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
// We have a ValueSet to pre-expand. // We have a ValueSet to pre-expand.
try { try {
ValueSet valueSet = txTemplate.execute(t -> { ValueSet valueSet = txTemplate.execute(t -> {
TermValueSet refreshedValueSetToExpand = myValueSetDao.findById(valueSetToExpand.getId()).orElseThrow(()->new IllegalStateException("Unknown VS ID: " + valueSetToExpand.getId())); TermValueSet refreshedValueSetToExpand = myValueSetDao.findById(valueSetToExpand.getId()).orElseThrow(() -> new IllegalStateException("Unknown VS ID: " + valueSetToExpand.getId()));
return getValueSetFromResourceTable(refreshedValueSetToExpand.getResource()); return getValueSetFromResourceTable(refreshedValueSetToExpand.getResource());
}); });
assert valueSet != null; assert valueSet != null;
@ -1988,7 +1996,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
@Override @Override
@Transactional @Transactional
public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB,
IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) {
FhirVersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA); FhirVersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA);
FhirVersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB); FhirVersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB);
@ -2002,7 +2010,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
String codeASystemIdentifier; String codeASystemIdentifier;
if (StringUtils.isNotEmpty(conceptA.getSystemVersion())) { if (StringUtils.isNotEmpty(conceptA.getSystemVersion())) {
codeASystemIdentifier = conceptA.getSystem() + "|" + conceptA.getSystemVersion(); codeASystemIdentifier = conceptA.getSystem() + "|" + conceptA.getSystemVersion();
} else { } else {
codeASystemIdentifier = conceptA.getSystem(); codeASystemIdentifier = conceptA.getSystem();
} }
@ -2079,12 +2087,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
}); });
} }
private @Nullable @Nullable
ConceptSubsumptionOutcome testForSubsumption(FullTextEntityManager theEntityManager, TermConcept theLeft, TermConcept theRight, ConceptSubsumptionOutcome theOutput) { private ConceptSubsumptionOutcome testForSubsumption(FullTextEntityManager theEntityManager, TermConcept theLeft, TermConcept theRight, ConceptSubsumptionOutcome theOutput) {
QueryBuilder qb = theEntityManager.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get(); QueryBuilder qb = theEntityManager.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
BooleanJunction<?> bool = qb.bool(); BooleanJunction<?> bool = qb.bool();
bool.must(qb.keyword().onField("myId").matching(Long.toString(theLeft.getId())).createQuery()); bool.must(qb.keyword().onField("myId").matching(Long.toString(theRight.getId())).createQuery());
bool.must(qb.keyword().onField("myParentPids").matching(Long.toString(theRight.getId())).createQuery()); bool.must(qb.keyword().onField("myParentPids").matching(Long.toString(theLeft.getId())).createQuery());
Query luceneQuery = bool.createQuery(); Query luceneQuery = bool.createQuery();
FullTextQuery jpaQuery = theEntityManager.createFullTextQuery(luceneQuery, TermConcept.class); FullTextQuery jpaQuery = theEntityManager.createFullTextQuery(luceneQuery, TermConcept.class);
jpaQuery.setMaxResults(1); jpaQuery.setMaxResults(1);
@ -2342,7 +2350,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
Pageable page = PageRequest.of(0, 1); Pageable page = PageRequest.of(0, 1);
List<TermConceptMap> theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, List<TermConceptMap> theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page,
theTranslationRequest.getUrl().asStringValue()); theTranslationRequest.getUrl().asStringValue());
if (!theConceptMapList.isEmpty()) { if (!theConceptMapList.isEmpty()) {
return theConceptMapList.get(0).getVersion(); return theConceptMapList.get(0).getVersion();
} }
@ -2553,6 +2561,136 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
@Nullable @Nullable
protected abstract CodeableConcept toCanonicalCodeableConcept(@Nullable IBaseDatatype theCodeableConcept); protected abstract CodeableConcept toCanonicalCodeableConcept(@Nullable IBaseDatatype theCodeableConcept);
@NotNull
private FhirVersionIndependentConcept toConcept(IPrimitiveType<String> theCodeType, IPrimitiveType<String> theCodeSystemIdentifierType, IBaseCoding theCodingType) {
String code = theCodeType != null ? theCodeType.getValueAsString() : null;
String system = theCodeSystemIdentifierType != null ? getUrlFromIdentifier(theCodeSystemIdentifierType.getValueAsString()) : null;
String systemVersion = theCodeSystemIdentifierType != null ? getVersionFromIdentifier(theCodeSystemIdentifierType.getValueAsString()) : null;
if (theCodingType != null) {
Coding canonicalizedCoding = toCanonicalCoding(theCodingType);
assert canonicalizedCoding != null; // Shouldn't be null, since theCodingType isn't
code = canonicalizedCoding.getCode();
system = canonicalizedCoding.getSystem();
systemVersion = canonicalizedCoding.getVersion();
}
return new FhirVersionIndependentConcept(system, code, null, systemVersion);
}
@Override
@Transactional
public CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theCodeSystemUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept);
boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0;
Coding coding = toCanonicalCoding(theCoding);
boolean haveCoding = coding != null && coding.isEmpty() == false;
boolean haveCode = theCode != null && theCode.isEmpty() == false;
if (!haveCodeableConcept && !haveCoding && !haveCode) {
throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate.");
}
if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) {
throw new InvalidRequestException("$validate-code can only validate (code) OR (coding) OR (codeableConcept)");
}
boolean haveIdentifierParam = isNotBlank(theCodeSystemUrl);
String codeSystemUrl;
if (theCodeSystemId != null) {
IBaseResource codeSystem = myDaoRegistry.getResourceDao("CodeSystem").read(theCodeSystemId);
codeSystemUrl = CommonCodeSystemsTerminologyService.getCodeSystemUrl(codeSystem);
} else if (haveIdentifierParam) {
codeSystemUrl = theCodeSystemUrl;
} else {
throw new InvalidRequestException("Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.");
}
String code = theCode;
String display = theDisplay;
if (haveCodeableConcept) {
for (int i = 0; i < codeableConcept.getCoding().size(); i++) {
Coding nextCoding = codeableConcept.getCoding().get(i);
if (nextCoding.hasSystem()) {
if (!codeSystemUrl.equalsIgnoreCase(nextCoding.getSystem())) {
throw new InvalidRequestException("Coding.system '" + nextCoding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate.");
}
codeSystemUrl = nextCoding.getSystem();
}
code = nextCoding.getCode();
display = nextCoding.getDisplay();
CodeValidationResult nextValidation = codeSystemValidateCode(codeSystemUrl, theVersion, code, display);
if (nextValidation.isOk() || i == codeableConcept.getCoding().size() - 1) {
return nextValidation;
}
}
} else if (haveCoding) {
if (coding.hasSystem()) {
if (!codeSystemUrl.equalsIgnoreCase(coding.getSystem())) {
throw new InvalidRequestException("Coding.system '" + coding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate.");
}
codeSystemUrl = coding.getSystem();
}
code = coding.getCode();
display = coding.getDisplay();
}
return codeSystemValidateCode(codeSystemUrl, theVersion, code, display);
}
@SuppressWarnings("unchecked")
private CodeValidationResult codeSystemValidateCode(String theCodeSystemUrl, String theCodeSystemVersion, String theCode, String theDisplay) {
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TermConcept> query = criteriaBuilder.createQuery(TermConcept.class);
Root<TermConcept> root = query.from(TermConcept.class);
Fetch<TermCodeSystemVersion, TermConcept> systemVersionFetch = root.fetch("myCodeSystem", JoinType.INNER);
Join<TermCodeSystemVersion, TermConcept> systemVersionJoin = (Join<TermCodeSystemVersion, TermConcept>) systemVersionFetch;
Fetch<TermCodeSystem, TermCodeSystemVersion> systemFetch = systemVersionFetch.fetch("myCodeSystem", JoinType.INNER);
Join<TermCodeSystem, TermCodeSystemVersion> systemJoin = (Join<TermCodeSystem, TermCodeSystemVersion>) systemFetch;
ArrayList<Predicate> predicates = new ArrayList<>();
if (isNotBlank(theCode)) {
predicates.add(criteriaBuilder.equal(root.get("myCode"), theCode));
}
if (isNotBlank(theDisplay)) {
predicates.add(criteriaBuilder.equal(root.get("myDisplay"), theDisplay));
}
if (isNoneBlank(theCodeSystemUrl)) {
predicates.add(criteriaBuilder.equal(systemJoin.get("myCodeSystemUri"), theCodeSystemUrl));
}
if (isNoneBlank(theCodeSystemVersion)) {
predicates.add(criteriaBuilder.equal(systemVersionJoin.get("myCodeSystemVersionId"), theCodeSystemVersion));
} else {
query.orderBy(criteriaBuilder.desc(root.get("myUpdated")));
}
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
query.where(outerPredicate);
final TypedQuery<TermConcept> typedQuery = myEntityManager.createQuery(query.select(root));
org.hibernate.query.Query<TermConcept> hibernateQuery = (org.hibernate.query.Query<TermConcept>) typedQuery;
hibernateQuery.setFetchSize(SINGLE_FETCH_SIZE);
List<TermConcept> resultsList = hibernateQuery.getResultList();
if (!resultsList.isEmpty()) {
TermConcept concept = resultsList.get(0);
return new CodeValidationResult().setCode(concept.getCode()).setDisplay(concept.getDisplay());
}
if (isBlank(theDisplay))
return createFailureCodeValidationResult(theCodeSystemUrl, theCode);
else
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, " - Concept Display : " + theDisplay);
}
public static class Job implements HapiJob { public static class Job implements HapiJob {
@Autowired @Autowired
private ITermReadSvc myTerminologySvc; private ITermReadSvc myTerminologySvc;
@ -2640,21 +2778,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
return termConcept; return termConcept;
} }
@NotNull
private FhirVersionIndependentConcept toConcept(IPrimitiveType<String> theCodeType, IPrimitiveType<String> theCodeSystemIdentifierType, IBaseCoding theCodingType) {
String code = theCodeType != null ? theCodeType.getValueAsString() : null;
String system = theCodeSystemIdentifierType != null ? getUrlFromIdentifier(theCodeSystemIdentifierType.getValueAsString()): null;
String systemVersion = theCodeSystemIdentifierType != null ? getVersionFromIdentifier(theCodeSystemIdentifierType.getValueAsString()): null;
if (theCodingType != null) {
Coding canonicalizedCoding = toCanonicalCoding(theCodingType);
assert canonicalizedCoding != null; // Shouldn't be null, since theCodingType isn't
code = canonicalizedCoding.getCode();
system = canonicalizedCoding.getSystem();
systemVersion = canonicalizedCoding.getVersion();
}
return new FhirVersionIndependentConcept(system, code, null, systemVersion);
}
/** /**
* This method is present only for unit tests, do not call from client code * This method is present only for unit tests, do not call from client code
*/ */
@ -2686,120 +2809,4 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
static boolean isOurLastResultsFromTranslationWithReverseCache() { static boolean isOurLastResultsFromTranslationWithReverseCache() {
return ourLastResultsFromTranslationWithReverseCache; return ourLastResultsFromTranslationWithReverseCache;
} }
@Override
@Transactional
public CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theCodeSystemUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept);
boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0;
Coding coding = toCanonicalCoding(theCoding);
boolean haveCoding = coding != null && coding.isEmpty() == false;
boolean haveCode = theCode != null && theCode.isEmpty() == false;
if (!haveCodeableConcept && !haveCoding && !haveCode) {
throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate.");
}
if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) {
throw new InvalidRequestException("$validate-code can only validate (code) OR (coding) OR (codeableConcept)");
}
boolean haveIdentifierParam = isNotBlank(theCodeSystemUrl);
String codeSystemUrl;
if (theCodeSystemId != null) {
IBaseResource codeSystem = myDaoRegistry.getResourceDao("CodeSystem").read(theCodeSystemId);
codeSystemUrl = CommonCodeSystemsTerminologyService.getCodeSystemUrl(codeSystem);
} else if (haveIdentifierParam) {
codeSystemUrl = theCodeSystemUrl;
} else {
throw new InvalidRequestException("Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.");
}
String code = theCode;
String display = theDisplay;
if (haveCodeableConcept) {
for (int i = 0; i < codeableConcept.getCoding().size(); i++) {
Coding nextCoding = codeableConcept.getCoding().get(i);
if (nextCoding.hasSystem()) {
if (!codeSystemUrl.equalsIgnoreCase(nextCoding.getSystem())) {
throw new InvalidRequestException("Coding.system '" + nextCoding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate.");
}
codeSystemUrl = nextCoding.getSystem();
}
code = nextCoding.getCode();
display = nextCoding.getDisplay();
CodeValidationResult nextValidation = codeSystemValidateCode(codeSystemUrl, theVersion, code, display);
if (nextValidation.isOk() || i == codeableConcept.getCoding().size() - 1) {
return nextValidation;
}
}
} else if (haveCoding) {
if (coding.hasSystem()) {
if (!codeSystemUrl.equalsIgnoreCase(coding.getSystem())) {
throw new InvalidRequestException("Coding.system '" + coding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate.");
}
codeSystemUrl = coding.getSystem();
}
code = coding.getCode();
display = coding.getDisplay();
}
return codeSystemValidateCode(codeSystemUrl, theVersion, code, display);
}
@SuppressWarnings("unchecked")
private CodeValidationResult codeSystemValidateCode(String theCodeSystemUrl, String theCodeSystemVersion, String theCode, String theDisplay) {
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TermConcept> query = criteriaBuilder.createQuery(TermConcept.class);
Root<TermConcept> root = query.from(TermConcept.class);
Fetch<TermCodeSystemVersion, TermConcept> systemVersionFetch = root.fetch("myCodeSystem", JoinType.INNER);
Join<TermCodeSystemVersion, TermConcept> systemVersionJoin = (Join<TermCodeSystemVersion, TermConcept>)systemVersionFetch;
Fetch<TermCodeSystem, TermCodeSystemVersion> systemFetch = systemVersionFetch.fetch("myCodeSystem", JoinType.INNER);
Join<TermCodeSystem, TermCodeSystemVersion> systemJoin = (Join<TermCodeSystem, TermCodeSystemVersion>)systemFetch;
ArrayList<Predicate> predicates = new ArrayList<>();
if (isNotBlank(theCode)) {
predicates.add(criteriaBuilder.equal(root.get("myCode"), theCode));
}
if (isNotBlank(theDisplay)) {
predicates.add(criteriaBuilder.equal(root.get("myDisplay"), theDisplay));
}
if (isNoneBlank(theCodeSystemUrl)) {
predicates.add(criteriaBuilder.equal(systemJoin.get("myCodeSystemUri"), theCodeSystemUrl));
}
if (isNoneBlank(theCodeSystemVersion)) {
predicates.add(criteriaBuilder.equal(systemVersionJoin.get("myCodeSystemVersionId"), theCodeSystemVersion));
} else {
query.orderBy(criteriaBuilder.desc(root.get("myUpdated")));
}
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
query.where(outerPredicate);
final TypedQuery<TermConcept> typedQuery = myEntityManager.createQuery(query.select(root));
org.hibernate.query.Query<TermConcept> hibernateQuery = (org.hibernate.query.Query<TermConcept>) typedQuery;
hibernateQuery.setFetchSize(SINGLE_FETCH_SIZE);
List<TermConcept> resultsList = hibernateQuery.getResultList();
if (!resultsList.isEmpty()) {
TermConcept concept = resultsList.get(0);
return new CodeValidationResult().setCode(concept.getCode()).setDisplay(concept.getDisplay());
}
if (isBlank(theDisplay))
return createFailureCodeValidationResult(theCodeSystemUrl, theCode);
else
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, " - Concept Display : " + theDisplay);
}
} }

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.term;
* #L% * #L%
*/ */
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.util.FhirVersionIndependentConcept; import ca.uhn.fhir.util.FhirVersionIndependentConcept;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
@ -29,6 +30,7 @@ import javax.annotation.Nullable;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.apache.commons.lang3.StringUtils.isNoneBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
class ExpansionFilter { class ExpansionFilter {
@ -95,4 +97,19 @@ class ExpansionFilter {
public Integer getMaxCount() { public Integer getMaxCount() {
return myMaxCount; return myMaxCount;
} }
@Nonnull
public static ExpansionFilter fromFilterString(@Nullable String theFilter) {
ExpansionFilter filter;
if (isNoneBlank(theFilter)) {
List<ValueSet.ConceptSetFilterComponent> filters = Collections.singletonList(new ValueSet.ConceptSetFilterComponent()
.setProperty(JpaConstants.VALUESET_FILTER_DISPLAY)
.setOp(ValueSet.FilterOperator.EQUAL)
.setValue(theFilter));
filter = new ExpansionFilter(null, null, filters, null);
} else {
filter = ExpansionFilter.NO_FILTER;
}
return filter;
}
} }

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.term;
*/ */
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.TermConceptDesignation; import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException; import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
import ca.uhn.fhir.model.api.annotation.Block; import ca.uhn.fhir.model.api.annotation.Block;
@ -45,6 +46,15 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
private int myAddedConcepts; private int myAddedConcepts;
private Integer myTotalConcepts; private Integer myTotalConcepts;
/**
* Constructor
*
* @param theDaoConfig Will be used to determine the max capacity for this accumulator
*/
public ValueSetExpansionComponentWithConceptAccumulator(FhirContext theContext, DaoConfig theDaoConfig) {
this(theContext, theDaoConfig.getMaximumExpansionSize());
}
/** /**
* Constructor * Constructor
* *
@ -56,7 +66,7 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
myContext = theContext; myContext = theContext;
} }
@Nonnull @Nonnull
@Override @Override
public Integer getCapacityRemaining() { public Integer getCapacityRemaining() {
return (myMaxCapacity - myAddedConcepts) + mySkipCountRemaining; return (myMaxCapacity - myAddedConcepts) + mySkipCountRemaining;

View File

@ -20,6 +20,7 @@ import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -57,7 +58,11 @@ import java.util.Set;
*/ */
public interface ITermReadSvc extends IValidationSupport { public interface ITermReadSvc extends IValidationSupport {
ValueSet expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand); ValueSet expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull String theValueSetCanonicalUrl, @Nullable String theExpansionFilter);
ValueSet expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull ValueSet theValueSetToExpand);
ValueSet expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull ValueSet theValueSetToExpand, @Nullable String theFilter);
void expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator); void expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator);
@ -68,7 +73,7 @@ public interface ITermReadSvc extends IValidationSupport {
void expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator); void expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator);
List<FhirVersionIndependentConcept> expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet); List<FhirVersionIndependentConcept> expandValueSetIntoConceptList(ValueSetExpansionOptions theExpansionOptions, String theValueSetCanonicalUrl);
Optional<TermConcept> findCode(String theCodeSystem, String theCode); Optional<TermConcept> findCode(String theCodeSystem, String theCode);

View File

@ -38,8 +38,8 @@ import static org.apache.commons.lang3.StringUtils.trim;
public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implements IRecordHandler { public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implements IRecordHandler {
public static final String LOINC_IEEE_CM_ID = "loinc-to-ieee-device-codes"; public static final String LOINC_IEEE_CM_ID = "loinc-to-ieee-11073-10101";
public static final String LOINC_IEEE_CM_URI = "http://loinc.org/cm/loinc-to-ieee-device-codes"; public static final String LOINC_IEEE_CM_URI = "http://loinc.org/cm/loinc-to-ieee-11073-10101";
public static final String LOINC_IEEE_CM_NAME = "LOINC/IEEE Device Code Mappings"; public static final String LOINC_IEEE_CM_NAME = "LOINC/IEEE Device Code Mappings";
private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC/IEEE Medical Device Code Mapping Table contains content from IEEE (http://ieee.org), copyright © 2017 IEEE."; private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC/IEEE Medical Device Code Mapping Table contains content from IEEE (http://ieee.org), copyright © 2017 IEEE.";

View File

@ -42,11 +42,11 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme
public static final String LOINC_SCT_PART_MAP_ID = "loinc-parts-to-snomed-ct"; public static final String LOINC_SCT_PART_MAP_ID = "loinc-parts-to-snomed-ct";
public static final String LOINC_SCT_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-snomed-ct"; public static final String LOINC_SCT_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-snomed-ct";
public static final String LOINC_TERM_TO_RPID_PART_MAP_ID = "loinc-to-rpids"; public static final String LOINC_TERM_TO_RPID_PART_MAP_ID = "loinc-to-radlex";
public static final String LOINC_TERM_TO_RPID_PART_MAP_URI = "http://loinc.org/cm/loinc-to-rpids"; public static final String LOINC_TERM_TO_RPID_PART_MAP_URI = "http://loinc.org/cm/loinc-to-radlex";
public static final String LOINC_TERM_TO_RPID_PART_MAP_NAME = "LOINC Terms to RadLex RPIDs"; public static final String LOINC_TERM_TO_RPID_PART_MAP_NAME = "LOINC Terms to RadLex RPIDs";
public static final String LOINC_PART_TO_RID_PART_MAP_ID = "loinc-part-to-rids"; public static final String LOINC_PART_TO_RID_PART_MAP_ID = "loinc-parts-to-radlex";
public static final String LOINC_PART_TO_RID_PART_MAP_URI = "http://loinc.org/cm/loinc-part-to-rids"; public static final String LOINC_PART_TO_RID_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-radlex";
public static final String LOINC_PART_TO_RID_PART_MAP_NAME = "LOINC Parts to RadLex RIDs"; public static final String LOINC_PART_TO_RID_PART_MAP_NAME = "LOINC Parts to RadLex RIDs";
private static final String LOINC_SCT_PART_MAP_NAME = "LOINC Part Map to SNOMED CT"; private static final String LOINC_SCT_PART_MAP_NAME = "LOINC Part Map to SNOMED CT";
private static final String LOINC_RXNORM_PART_MAP_ID = "loinc-parts-to-rxnorm"; private static final String LOINC_RXNORM_PART_MAP_ID = "loinc-parts-to-rxnorm";

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.config; package ca.uhn.fhir.jpa.config;
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
import net.ttddyy.dsproxy.ExecutionInfo; import net.ttddyy.dsproxy.ExecutionInfo;
import net.ttddyy.dsproxy.QueryInfo; import net.ttddyy.dsproxy.QueryInfo;
import net.ttddyy.dsproxy.proxy.ParameterSetOperation; import net.ttddyy.dsproxy.proxy.ParameterSetOperation;

View File

@ -139,7 +139,7 @@ public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test {
myPatientDao.create(p); myPatientDao.create(p);
} catch (PreconditionFailedException e) { } catch (PreconditionFailedException e) {
// expected - This is as a result of the unique SP // expected - This is as a result of the unique SP
assertThat(e.getMessage(), containsString("duplicate index matching query: Patient?gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale")); assertThat(e.getMessage(), containsString("duplicate unique index matching query: Patient?gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"));
} catch (ResourceVersionConflictException e) { } catch (ResourceVersionConflictException e) {
// expected - This is as a result of the unique SP // expected - This is as a result of the unique SP
assertThat(e.getMessage(), containsString("would have resulted in a duplicate value for a unique index")); assertThat(e.getMessage(), containsString("would have resulted in a duplicate value for a unique index"));

View File

@ -1,5 +1,36 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.hamcrest.Matchers;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Meta;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.PlatformTransactionManager;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
@ -24,38 +55,8 @@ import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult; import ca.uhn.fhir.validation.ValidationResult;
import org.hamcrest.Matchers;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Meta;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.PlatformTransactionManager;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(SpringExtension.class) @ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {TestR4ConfigWithElasticSearch.class}) @ContextConfiguration(classes = {TestR4ConfigWithElasticSearch.class})
@ -155,11 +156,63 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest {
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains()); ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAA", "childAAB")); assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
} }
@Test
public void testExpandWithFilter() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
ValueSet result = myValueSetDao.expand(vs, "child");
logAndValidateValueSet(result);
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(resp);
assertThat(resp, stringContainsInOrder("<code value=\"childCA\"/>","<display value=\"Child CA\"/>"));
}
@Test
public void testExpandWithFilterContainsLeftMatchingValue() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
ValueSet result = myValueSetDao.expand(vs, "chi");
logAndValidateValueSet(result);
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(resp);
assertThat(resp, stringContainsInOrder("<code value=\"childCA\"/>","<display value=\"Child CA\"/>"));
}
@Test
public void testExpandWithFilterContainsNotLeftMatchingValue() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
ValueSet result = myValueSetDao.expand(vs, "hil");
logAndValidateValueSet(result);
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(resp);
assertThat(resp, not(stringContainsInOrder("<code value=\"childCA\"/>","<display value=\"Child CA\"/>")));
}
@Test @Test
public void testExpandVsWithMultiInclude_All() throws IOException { public void testExpandVsWithMultiInclude_All() throws IOException {
CodeSystem cs = loadResource(myFhirCtx, CodeSystem.class, "/r4/expand-multi-cs.json"); CodeSystem cs = loadResource(myFhirCtx, CodeSystem.class, "/r4/expand-multi-cs.json");
@ -271,7 +324,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest {
return retVal; return retVal;
} }
private void logAndValidateValueSet(ValueSet theResult) { private void logAndValidateValueSet(ValueSet theResult) {
IParser parser = myFhirCtx.newXmlParser().setPrettyPrint(true); IParser parser = myFhirCtx.newXmlParser().setPrettyPrint(true);
String encoded = parser.encodeResourceToString(theResult); String encoded = parser.encodeResourceToString(theResult);

View File

@ -803,7 +803,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
myPatientDao.create(pt1).getId().toUnqualifiedVersionless(); myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
fail(); fail();
} catch (PreconditionFailedException e) { } catch (PreconditionFailedException e) {
assertEquals("Can not create resource of type Patient as it would create a duplicate index matching query: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale (existing index belongs to Patient/" + id1.getIdPart() + ")", e.getMessage()); assertEquals("Can not create resource of type Patient as it would create a duplicate unique index matching query: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale (existing index belongs to Patient/" + id1.getIdPart() + ", new unique index created by SearchParameter/patient-gender-birthdate)", e.getMessage());
} }
} }
@ -1510,7 +1510,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
myPatientDao.create(pt1).getId().toUnqualifiedVersionless(); myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
fail(); fail();
} catch (PreconditionFailedException e) { } catch (PreconditionFailedException e) {
// good assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
} }
Patient pt2 = new Patient(); Patient pt2 = new Patient();
@ -1525,7 +1525,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
myPatientDao.update(pt2); myPatientDao.update(pt2);
fail(); fail();
} catch (PreconditionFailedException e) { } catch (PreconditionFailedException e) {
// good assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
} }
} }

View File

@ -423,8 +423,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentB")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ParentB"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.execute(); .execute();
@ -440,8 +440,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentC")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ParentC"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("1")) .andParameter("version", new StringType("1"))
.execute(); .execute();
@ -458,8 +458,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentB")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ParentB"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("2")) .andParameter("version", new StringType("2"))
.execute(); .execute();
@ -480,8 +480,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentA")) .withParameter(Parameters.class, "codeA", new CodeType("ParentB"))
.andParameter("codeB", new CodeType("ParentB")) .andParameter("codeB", new CodeType("ParentA"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.execute(); .execute();
@ -497,8 +497,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentA")) .withParameter(Parameters.class, "codeA", new CodeType("ParentC"))
.andParameter("codeB", new CodeType("ParentC")) .andParameter("codeB", new CodeType("ParentA"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("1")) .andParameter("version", new StringType("1"))
.execute(); .execute();
@ -515,8 +515,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentA")) .withParameter(Parameters.class, "codeA", new CodeType("ParentB"))
.andParameter("codeB", new CodeType("ParentB")) .andParameter("codeB", new CodeType("ParentA"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("2")) .andParameter("version", new StringType("2"))
.execute(); .execute();
@ -610,8 +610,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB"))
.execute(); .execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -626,8 +626,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1"))
.execute(); .execute();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -642,8 +642,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2"))
.execute(); .execute();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -663,8 +663,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
.execute(); .execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -679,8 +679,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1"))
.execute(); .execute();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -695,8 +695,8 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2"))
.execute(); .execute();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);

View File

@ -50,6 +50,8 @@ import java.util.List;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM; import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_VALUE_SET; import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_VALUE_SET;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder; import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@ -343,14 +345,14 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
.operation() .operation()
.onInstance(myExtensionalVsId) .onInstance(myExtensionalVsId)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, Matchers.containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, Matchers.containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, Matchers.not(Matchers.containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }
@ -857,6 +859,68 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
} }
@Test
public void testExpandByValueSetWithFilterContainsPrefixValue() throws IOException {
loadAndPersistCodeSystem();
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringType("blo"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, stringContainsInOrder("<code value=\"11378-7\"/>", "<display value=\"Systolic blood pressure at First encounter\"/>"));
}
@Test
public void testExpandByValueSetWithFilterContainsNoPrefixValue() throws IOException {
loadAndPersistCodeSystem();
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringType("lood"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, not(stringContainsInOrder("<code value=\"11378-7\"/>","<display value=\"Systolic blood pressure at First encounter\"/>")));
}
@Test
public void testExpandByValueSetWithFilterNotContainsAnyValue() throws IOException {
loadAndPersistCodeSystem();
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringType("loood"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, not(stringContainsInOrder("<code value=\"11378-7\"/>","<display value=\"Systolic blood pressure at First encounter\"/>")));
}
@Test @Test
public void testExpandByUrlWithFilter() throws Exception { public void testExpandByUrlWithFilter() throws Exception {
loadAndPersistCodeSystemAndValueSet(); loadAndPersistCodeSystemAndValueSet();
@ -866,7 +930,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
.onType(ValueSet.class) .onType(ValueSet.class)
.named("expand") .named("expand")
.withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.andParameter("filter", new StringType("first")) .andParameter("filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();

View File

@ -385,28 +385,28 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv
.operation() .operation()
.onInstance(myExtensionalVsId_v1) .onInstance(myExtensionalVsId_v1)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
// Verify ValueSet v2 // Verify ValueSet v2
respParam = ourClient respParam = ourClient
.operation() .operation()
.onInstance(myExtensionalVsId_v2) .onInstance(myExtensionalVsId_v2)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
expanded = (ValueSet) respParam.getParameter().get(0).getResource(); expanded = (ValueSet) respParam.getParameter().get(0).getResource();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration v2\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }
@ -425,28 +425,28 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv
.operation() .operation()
.onInstance(myExtensionalVsId_v1) .onInstance(myExtensionalVsId_v1)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
// Validate ValueSet v2 // Validate ValueSet v2
respParam = ourClient respParam = ourClient
.operation() .operation()
.onInstance(myExtensionalVsId_v2) .onInstance(myExtensionalVsId_v2)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
expanded = (ValueSet) respParam.getParameter().get(0).getResource(); expanded = (ValueSet) respParam.getParameter().get(0).getResource();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration v2\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }

View File

@ -274,8 +274,8 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ChildAA")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ChildAA"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.execute(); .execute();
@ -294,8 +294,8 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentA")) .withParameter(Parameters.class, "codeA", new CodeType("ChildAA"))
.andParameter("codeB", new CodeType("ChildAA")) .andParameter("codeB", new CodeType("ParentA"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.execute(); .execute();
@ -381,8 +381,8 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA"))
.execute(); .execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -400,8 +400,8 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
.execute(); .execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);

View File

@ -445,8 +445,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentB")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ParentB"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.execute(); .execute();
@ -462,8 +462,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentC")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ParentC"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("1")) .andParameter("version", new StringType("1"))
.execute(); .execute();
@ -480,8 +480,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentB")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ParentB"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("2")) .andParameter("version", new StringType("2"))
.execute(); .execute();
@ -502,8 +502,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentA")) .withParameter(Parameters.class, "codeA", new CodeType("ParentB"))
.andParameter("codeB", new CodeType("ParentB")) .andParameter("codeB", new CodeType("ParentA"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.execute(); .execute();
@ -519,8 +519,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentA")) .withParameter(Parameters.class, "codeA", new CodeType("ParentC"))
.andParameter("codeB", new CodeType("ParentC")) .andParameter("codeB", new CodeType("ParentA"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("1")) .andParameter("version", new StringType("1"))
.execute(); .execute();
@ -537,8 +537,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentA")) .withParameter(Parameters.class, "codeA", new CodeType("ParentB"))
.andParameter("codeB", new CodeType("ParentB")) .andParameter("codeB", new CodeType("ParentA"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("2")) .andParameter("version", new StringType("2"))
.execute(); .execute();
@ -632,8 +632,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB"))
.execute(); .execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -648,8 +648,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1"))
.execute(); .execute();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -664,8 +664,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2"))
.execute(); .execute();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -685,8 +685,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
.execute(); .execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -701,8 +701,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1"))
.execute(); .execute();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -717,8 +717,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2"))
.execute(); .execute();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);

View File

@ -287,14 +287,14 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
.operation() .operation()
.onInstance(myExtensionalVsId) .onInstance(myExtensionalVsId)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }
@ -312,14 +312,14 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
.operation() .operation()
.onInstance(myExtensionalVsId) .onInstance(myExtensionalVsId)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }
@ -1194,6 +1194,68 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
} }
@Test
public void testExpandByValueSetWithFilterContainsPrefixValue() throws IOException {
loadAndPersistCodeSystem();
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
Parameters respParam = myClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringType("blo"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, stringContainsInOrder("<code value=\"11378-7\"/>", "<display value=\"Systolic blood pressure at First encounter\"/>"));
}
@Test
public void testExpandByValueSetWithFilterContainsNoPrefixValue() throws IOException {
loadAndPersistCodeSystem();
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
Parameters respParam = myClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringType("lood"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, not(stringContainsInOrder("<code value=\"11378-7\"/>","<display value=\"Systolic blood pressure at First encounter\"/>")));
}
@Test
public void testExpandByValueSetWithFilterNotContainsAnyValue() throws IOException {
loadAndPersistCodeSystem();
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
Parameters respParam = myClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringType("loood"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, not(stringContainsInOrder("<code value=\"11378-7\"/>","<display value=\"Systolic blood pressure at First encounter\"/>")));
}
@Test @Test
public void testExpandByUrlWithFilter() throws Exception { public void testExpandByUrlWithFilter() throws Exception {
loadAndPersistCodeSystemAndValueSet(); loadAndPersistCodeSystemAndValueSet();
@ -1203,7 +1265,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
.onType(ValueSet.class) .onType(ValueSet.class)
.named("expand") .named("expand")
.withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.andParameter("filter", new StringType("first")) .andParameter("filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();

View File

@ -261,14 +261,14 @@ public class ResourceProviderR4ValueSetVerCSNoVerTest extends BaseResourceProvid
.operation() .operation()
.onInstance(myExtensionalVsId) .onInstance(myExtensionalVsId)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }
@ -286,14 +286,14 @@ public class ResourceProviderR4ValueSetVerCSNoVerTest extends BaseResourceProvid
.operation() .operation()
.onInstance(myExtensionalVsId) .onInstance(myExtensionalVsId)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.provider.r4; package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
@ -11,8 +12,11 @@ import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
@ -37,6 +41,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionCallbackWithoutResult;
@ -44,7 +49,9 @@ import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET;
@ -331,28 +338,28 @@ public class ResourceProviderR4ValueSetVerCSVerTest extends BaseResourceProvider
.operation() .operation()
.onInstance(myExtensionalVsId_v1) .onInstance(myExtensionalVsId_v1)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
// Verify ValueSet v2 // Verify ValueSet v2
respParam = myClient respParam = myClient
.operation() .operation()
.onInstance(myExtensionalVsId_v2) .onInstance(myExtensionalVsId_v2)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
expanded = (ValueSet) respParam.getParameter().get(0).getResource(); expanded = (ValueSet) respParam.getParameter().get(0).getResource();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration v2\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }
@ -372,28 +379,27 @@ public class ResourceProviderR4ValueSetVerCSVerTest extends BaseResourceProvider
.operation() .operation()
.onInstance(myExtensionalVsId_v1) .onInstance(myExtensionalVsId_v1)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>")));
// Validate ValueSet v2 // Validate ValueSet v2
respParam = myClient respParam = myClient
.operation() .operation()
.onInstance(myExtensionalVsId_v2) .onInstance(myExtensionalVsId_v2)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
expanded = (ValueSet) respParam.getParameter().get(0).getResource(); expanded = (ValueSet) respParam.getParameter().get(0).getResource();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration v2\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }

View File

@ -102,8 +102,8 @@ public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ChildAA")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ChildAA"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.execute(); .execute();
@ -121,8 +121,8 @@ public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA")) .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA"))
.andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA"))
.execute(); .execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);

View File

@ -220,8 +220,8 @@ public class ResourceProviderR5CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentB")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ParentB"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.execute(); .execute();
@ -237,8 +237,8 @@ public class ResourceProviderR5CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentC")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ParentC"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("1")) .andParameter("version", new StringType("1"))
.execute(); .execute();
@ -255,8 +255,8 @@ public class ResourceProviderR5CodeSystemVersionedTest extends BaseResourceProvi
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_SUBSUMES) .named(JpaConstants.OPERATION_SUBSUMES)
.withParameter(Parameters.class, "codeA", new CodeType("ParentB")) .withParameter(Parameters.class, "codeA", new CodeType("ParentA"))
.andParameter("codeB", new CodeType("ParentA")) .andParameter("codeB", new CodeType("ParentB"))
.andParameter("system", new UriType(SYSTEM_PARENTCHILD)) .andParameter("system", new UriType(SYSTEM_PARENTCHILD))
.andParameter("version", new StringType("2")) .andParameter("version", new StringType("2"))
.execute(); .execute();

View File

@ -70,7 +70,6 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
private IIdType myExtensionalCsId; private IIdType myExtensionalCsId;
private IIdType myExtensionalVsId; private IIdType myExtensionalVsId;
private IIdType myLocalValueSetId; private IIdType myLocalValueSetId;
private Long myExtensionalCsIdOnResourceTable;
private Long myExtensionalVsIdOnResourceTable; private Long myExtensionalVsIdOnResourceTable;
private ValueSet myLocalVs; private ValueSet myLocalVs;
@ -84,11 +83,6 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
loadAndPersistValueSet(theVerb); loadAndPersistValueSet(theVerb);
} }
private void loadAndPersistCodeSystemAndValueSetWithDesignationsAndExclude(HTTPVerb theVerb) throws IOException {
loadAndPersistCodeSystemWithDesignations(theVerb);
loadAndPersistValueSetWithExclude(theVerb);
}
private void loadAndPersistCodeSystem(HTTPVerb theVerb) throws IOException { private void loadAndPersistCodeSystem(HTTPVerb theVerb) throws IOException {
CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml");
codeSystem.setId("CodeSystem/cs"); codeSystem.setId("CodeSystem/cs");
@ -123,7 +117,6 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
default: default:
throw new IllegalArgumentException("HTTP verb is not supported: " + theVerb); throw new IllegalArgumentException("HTTP verb is not supported: " + theVerb);
} }
myExtensionalCsIdOnResourceTable = myCodeSystemDao.readEntity(myExtensionalCsId, null).getId();
} }
private void loadAndPersistValueSet(HTTPVerb theVerb) throws IOException { private void loadAndPersistValueSet(HTTPVerb theVerb) throws IOException {
@ -132,12 +125,6 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
persistValueSet(valueSet, theVerb); persistValueSet(valueSet, theVerb);
} }
private void loadAndPersistValueSetWithExclude(HTTPVerb theVerb) throws IOException {
ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs-with-exclude.xml");
valueSet.setId("ValueSet/vs");
persistValueSet(valueSet, theVerb);
}
@SuppressWarnings("EnumSwitchStatementWhichMissesCases") @SuppressWarnings("EnumSwitchStatementWhichMissesCases")
private void persistValueSet(ValueSet theValueSet, HTTPVerb theVerb) { private void persistValueSet(ValueSet theValueSet, HTTPVerb theVerb) {
switch (theVerb) { switch (theVerb) {
@ -404,14 +391,14 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
.operation() .operation()
.onInstance(myExtensionalVsId) .onInstance(myExtensionalVsId)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }
@ -426,14 +413,14 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
.operation() .operation()
.onInstance(myExtensionalVsId) .onInstance(myExtensionalVsId)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }
@ -1440,6 +1427,68 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
} }
@Test
public void testExpandByValueSetWithFilterContainsPrefixValue() throws IOException {
loadAndPersistCodeSystem(HTTPVerb.POST);
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
Parameters respParam = myClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringType("blo"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, stringContainsInOrder("<code value=\"11378-7\"/>", "<display value=\"Systolic blood pressure at First encounter\"/>"));
}
@Test
public void testExpandByValueSetWithFilterContainsNoPrefixValue() throws IOException {
loadAndPersistCodeSystem(HTTPVerb.POST);
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
Parameters respParam = myClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringType("lood"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, not(stringContainsInOrder("<code value=\"11378-7\"/>","<display value=\"Systolic blood pressure at First encounter\"/>")));
}
@Test
public void testExpandByValueSetWithFilterNotContainsAnyValue() throws IOException {
loadAndPersistCodeSystem(HTTPVerb.POST);
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
Parameters respParam = myClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("filter", new StringType("loood"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, not(stringContainsInOrder("<code value=\"11378-7\"/>","<display value=\"Systolic blood pressure at First encounter\"/>")));
}
@Test @Test
public void testExpandByUrlWithFilter() throws Exception { public void testExpandByUrlWithFilter() throws Exception {
loadAndPersistCodeSystemAndValueSet(HTTPVerb.POST); loadAndPersistCodeSystemAndValueSet(HTTPVerb.POST);
@ -1449,7 +1498,7 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
.onType(ValueSet.class) .onType(ValueSet.class)
.named("expand") .named("expand")
.withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.andParameter("filter", new StringType("first")) .andParameter("filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();

View File

@ -368,28 +368,28 @@ public class ResourceProviderR5ValueSetVersionedTest extends BaseResourceProvide
.operation() .operation()
.onInstance(myExtensionalVsId_v1) .onInstance(myExtensionalVsId_v1)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
// Verify ValueSet v2 // Verify ValueSet v2
respParam = myClient respParam = myClient
.operation() .operation()
.onInstance(myExtensionalVsId_v2) .onInstance(myExtensionalVsId_v2)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
expanded = (ValueSet) respParam.getParameter().get(0).getResource(); expanded = (ValueSet) respParam.getParameter().get(0).getResource();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration v2\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }
@ -409,28 +409,28 @@ public class ResourceProviderR5ValueSetVersionedTest extends BaseResourceProvide
.operation() .operation()
.onInstance(myExtensionalVsId_v1) .onInstance(myExtensionalVsId_v1)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
// Validate ValueSet v2 // Validate ValueSet v2
respParam = myClient respParam = myClient
.operation() .operation()
.onInstance(myExtensionalVsId_v2) .onInstance(myExtensionalVsId_v2)
.named("expand") .named("expand")
.withParameter(Parameters.class, "filter", new StringType("first")) .withParameter(Parameters.class, "filter", new StringType("systolic"))
.execute(); .execute();
expanded = (ValueSet) respParam.getParameter().get(0).getResource(); expanded = (ValueSet) respParam.getParameter().get(0).getResource();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>")); assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter v2\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration v2\"/>"))); assertThat(resp, not(containsString("\"Foo Code\"")));
} }

View File

@ -11,6 +11,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
import ca.uhn.fhir.jpa.util.SqlQuery; import ca.uhn.fhir.jpa.util.SqlQuery;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -22,10 +23,12 @@ import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.codesystems.HttpVerb; import org.hl7.fhir.r4.model.codesystems.HttpVerb;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mock; import org.mockito.Mock;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
@ -60,6 +63,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
@Mock @Mock
private IValueSetConceptAccumulator myValueSetCodeAccumulator; private IValueSetConceptAccumulator myValueSetCodeAccumulator;
@AfterEach
public void afterEach() {
SearchBuilder.setMaxPageSize50ForTest(false);
}
@Test @Test
public void testDeletePreExpandedValueSet() throws IOException { public void testDeletePreExpandedValueSet() throws IOException {
myDaoConfig.setPreExpandValueSets(true); myDaoConfig.setPreExpandValueSets(true);
@ -181,7 +189,53 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
// Make sure we used the pre-expanded version // Make sure we used the pre-expanded version
List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries(); List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries();
String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase(); String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase();
assertThat(lastSelectQuery, containsString("concept_display like 'display value 9%'")); assertThat(lastSelectQuery, containsString(" like 'display value 9%'"));
}
@Test
public void testExpandHugeValueSet_FilterOnDisplay_LeftMatch_SelectAll() {
SearchBuilder.setMaxPageSize50ForTest(true);
myDaoConfig.setPreExpandValueSets(true);
IIdType vsId = createConceptsCodeSystemAndValueSet(1005);
// Inline ValueSet
{
ValueSet input = new ValueSet();
input.getCompose()
.addInclude()
.addValueSet("http://foo/vs")
.addFilter()
.setProperty(JpaConstants.VALUESET_FILTER_DISPLAY)
.setOp(ValueSet.FilterOperator.EQUAL)
.setValue("display value 100");
// Expansion should contain all codes
myCaptureQueriesListener.clear();
ValueSet expandedValueSet = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), input);
List<String> codes = expandedValueSet.getExpansion().getContains().stream().map(t -> t.getCode()).collect(Collectors.toList());
assertThat(codes.toString(), codes, contains("code100", "code1000", "code1001", "code1002", "code1003", "code1004"));
// Make sure we used the pre-expanded version
List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries();
String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase();
ourLog.info("SQL: {}", lastSelectQuery);
assertThat(lastSelectQuery, containsString(" like 'display value 100%'"));
}
// ValueSet by ID
{
myCaptureQueriesListener.clear();
ValueSet expandedValueSet = myValueSetDao.expand(vsId, "display value 100", 0, 1000, mySrd);
List<String> codes = expandedValueSet.getExpansion().getContains().stream().map(t -> t.getCode()).collect(Collectors.toList());
assertThat(codes.toString(), codes, contains("code100", "code1000", "code1001", "code1002", "code1003", "code1004"));
// Make sure we used the pre-expanded version
List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries();
String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase();
ourLog.info("SQL: {}", lastSelectQuery);
assertThat(lastSelectQuery, containsString(" like 'display value 100%'"));
}
} }
@ -215,11 +269,38 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
// Make sure we used the pre-expanded version // Make sure we used the pre-expanded version
List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries(); List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries();
String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase(); String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase();
assertThat(lastSelectQuery, containsString("concept_display like 'display value 9%'")); assertThat(lastSelectQuery, containsString(" like 'display value 9%'"));
} }
@Test
public void testExpandInline_IncludePreExpandedValueSetByUri_FilterOnDisplay_LeftMatchCaseInsensitive() {
myDaoConfig.setPreExpandValueSets(true);
create100ConceptsCodeSystemAndValueSet();
ValueSet input = new ValueSet();
input.getCompose()
.addInclude()
.addValueSet("http://foo/vs")
.addFilter()
.setProperty(JpaConstants.VALUESET_FILTER_DISPLAY)
.setOp(ValueSet.FilterOperator.EQUAL)
.setValue("dIsPlAy valuE 99");
myCaptureQueriesListener.clear();
ValueSet expandedValueSet = myTermSvc.expandValueSet(null, input);
ourLog.debug("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet));
assertThat(toCodes(expandedValueSet).toString(), toCodes(expandedValueSet), contains( "code99" ));
// Make sure we used the pre-expanded version
List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries();
String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase();
assertThat(lastSelectQuery, containsString("like 'display value 99%'"));
}
@Test @Test
public void testExpandInline_IncludePreExpandedValueSetByUri_ExcludeCodes_FilterOnDisplay_LeftMatch_SelectAll() { public void testExpandInline_IncludePreExpandedValueSetByUri_ExcludeCodes_FilterOnDisplay_LeftMatch_SelectAll() {
myDaoConfig.setPreExpandValueSets(true); myDaoConfig.setPreExpandValueSets(true);
@ -254,7 +335,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
// Make sure we used the pre-expanded version // Make sure we used the pre-expanded version
List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries(); List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries();
String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase(); String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase();
assertThat(lastSelectQuery, containsString("concept_display like 'display value 90%'")); assertThat(lastSelectQuery, containsString(" like 'display value 90%'"));
} }
@ -292,24 +373,38 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
} }
} }
public void create100ConceptsCodeSystemAndValueSet() { public void create100ConceptsCodeSystemAndValueSet() {
createConceptsCodeSystemAndValueSet(100);
}
public IIdType createConceptsCodeSystemAndValueSet(int theCount) {
CodeSystem cs = new CodeSystem(); CodeSystem cs = new CodeSystem();
cs.setUrl("http://foo/cs"); cs.setUrl("http://foo/cs");
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
myCodeSystemDao.create(cs); myCodeSystemDao.create(cs);
CustomTerminologySet additions = new CustomTerminologySet(); CustomTerminologySet additions = new CustomTerminologySet();
for (int i = 0; i < 100; i++) { for (int i = 0; i < theCount; i++) {
additions.addRootConcept("code" + i, "display value " + i); additions.addRootConcept("code" + i, "display value " + i);
} }
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", additions); myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", additions);
myTerminologyDeferredStorageSvc.saveAllDeferred();
ValueSet vs = new ValueSet(); ValueSet vs = new ValueSet();
vs.setUrl("http://foo/vs"); vs.setUrl("http://foo/vs");
vs.getCompose().addInclude().setSystem("http://foo/cs"); vs.getCompose().addInclude().setSystem("http://foo/cs");
myValueSetDao.create(vs); IIdType vsId = myValueSetDao.create(vs).getId().toUnqualifiedVersionless();
myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); myTermSvc.preExpandDeferredValueSetsToTerminologyTables();
// Confirm we pre-expanded successfully
runInTransaction(() -> {
Pageable page = Pageable.unpaged();
List<TermValueSet> valueSets = myTermValueSetDao.findTermValueSetByUrl(page, "http://foo/vs");
assertEquals(1, valueSets.size());
assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, valueSets.get(0).getExpansionStatus());
});
return vsId;
} }
@Test @Test
@ -339,7 +434,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
// Make sure we used the pre-expanded version // Make sure we used the pre-expanded version
List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries(); List<SqlQuery> selectQueries = myCaptureQueriesListener.getSelectQueries();
String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase(); String lastSelectQuery = selectQueries.get(selectQueries.size() - 1).getSql(true, true).toLowerCase();
assertThat(lastSelectQuery, containsString("concept_display like 'display value 9%'")); assertThat(lastSelectQuery, containsString(" like 'display value 9%'"));
} }
@Nonnull @Nonnull

View File

@ -89,12 +89,12 @@
<code value="8490-5" /> <code value="8490-5" />
<display value="Systolic blood pressure 24 hour mean" /> <display value="Systolic blood pressure 24 hour mean" />
</concept> </concept>
<concept>
<code value="8491-3" />
<display value="Systolic blood pressure 1 hour minimum" />
</concept>
<concept> <concept>
<code value="8492-1" /> <code value="8492-1" />
<display value="Systolic blood pressure 8 hour minimum" /> <display value="Systolic blood pressure 8 hour minimum" />
</concept> </concept>
<concept>
<code value="9999-9" />
<display value="Foo Code" />
</concept>
</CodeSystem> </CodeSystem>

View File

@ -26,6 +26,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IIdType;
import javax.persistence.*; import javax.persistence.*;
@ -59,6 +60,8 @@ public class ResourceIndexedCompositeStringUnique extends BasePartitionable impl
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Column(name = PartitionablePartitionId.PARTITION_ID, insertable = false, updatable = false, nullable = true) @Column(name = PartitionablePartitionId.PARTITION_ID, insertable = false, updatable = false, nullable = true)
private Integer myPartitionIdValue; private Integer myPartitionIdValue;
@Transient
private IIdType mySearchParameterId;
/** /**
* Constructor * Constructor
@ -70,10 +73,11 @@ public class ResourceIndexedCompositeStringUnique extends BasePartitionable impl
/** /**
* Constructor * Constructor
*/ */
public ResourceIndexedCompositeStringUnique(ResourceTable theResource, String theIndexString) { public ResourceIndexedCompositeStringUnique(ResourceTable theResource, String theIndexString, IIdType theSearchParameterId) {
setResource(theResource); setResource(theResource);
setIndexString(theIndexString); setIndexString(theIndexString);
setPartitionId(theResource.getPartitionId()); setPartitionId(theResource.getPartitionId());
setSearchParameterId(theSearchParameterId);
} }
@Override @Override
@ -131,4 +135,18 @@ public class ResourceIndexedCompositeStringUnique extends BasePartitionable impl
.append("partition", getPartitionId()) .append("partition", getPartitionId())
.toString(); .toString();
} }
/**
* Note: This field is not persisted, so it will only be populated for new indexes
*/
public void setSearchParameterId(IIdType theSearchParameterId) {
mySearchParameterId = theSearchParameterId;
}
/**
* Note: This field is not persisted, so it will only be populated for new indexes
*/
public IIdType getSearchParameterId() {
return mySearchParameterId;
}
} }

View File

@ -684,6 +684,11 @@
<id>blangley28</id> <id>blangley28</id>
<organization>MITRE</organization> <organization>MITRE</organization>
</developer> </developer>
<developer>
<id>swagers</id>
<name>Steven Wagers</name>
<organization>Regenstrief Institute</organization>
</developer>
</developers> </developers>
<licenses> <licenses>
@ -733,7 +738,7 @@
<jena_version>3.16.0</jena_version> <jena_version>3.16.0</jena_version>
<jersey_version>2.25.1</jersey_version> <jersey_version>2.25.1</jersey_version>
<!-- 9.4.17 seems to have issues --> <!-- 9.4.17 seems to have issues -->
<jetty_version>9.4.30.v20200611</jetty_version> <jetty_version>9.4.34.v20201102</jetty_version>
<jsr305_version>3.0.2</jsr305_version> <jsr305_version>3.0.2</jsr305_version>
<junit_version>5.6.2</junit_version> <junit_version>5.6.2</junit_version>
<flyway_version>6.5.4</flyway_version> <flyway_version>6.5.4</flyway_version>