Custom search parameters could not chain an extension param

This commit is contained in:
James Agnew 2017-05-17 10:46:22 -04:00
parent 5ac91bfb94
commit 7bb9e5edd9
4 changed files with 336 additions and 210 deletions

View File

@ -24,6 +24,7 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.substring;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
@ -67,15 +68,7 @@ import org.springframework.transaction.annotation.Transactional;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.jpa.entity.BaseHasResource;
@ -438,27 +431,57 @@ public class SearchBuilder implements ISearchBuilder {
} else { } else {
List<Class<? extends IBaseResource>> resourceTypes; final List<Class<? extends IBaseResource>> resourceTypes;
String resourceId; String resourceId;
if (!ref.getValue().matches("[a-zA-Z]+\\/.*")) { if (!ref.getValue().matches("[a-zA-Z]+\\/.*")) {
RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceType); RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(myResourceName, theParamName);
String paramPath = myCallingDao.getSearchParamByName(resourceDef, theParamName).getPath(); resourceTypes = new ArrayList<Class<? extends IBaseResource>>();
if (paramPath.endsWith(".as(Reference)")) {
paramPath = paramPath.substring(0, paramPath.length() - ".as(Reference)".length()) + "Reference"; Set<String> targetTypes = param.getTargets();
if (targetTypes != null && !targetTypes.isEmpty()) {
for (String next : targetTypes) {
resourceTypes.add(myContext.getResourceDefinition(next).getImplementingClass());
}
} }
BaseRuntimeChildDefinition def = myContext.newTerser().getDefinition(myResourceType, paramPath); if (resourceTypes.isEmpty()) {
if (def instanceof RuntimeChildChoiceDefinition) { RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceType);
RuntimeChildChoiceDefinition choiceDef = (RuntimeChildChoiceDefinition) def; String paramPath = myCallingDao.getSearchParamByName(resourceDef, theParamName).getPath();
resourceTypes = choiceDef.getResourceTypes(); if (paramPath.endsWith(".as(Reference)")) {
} else if (def instanceof RuntimeChildResourceDefinition) { paramPath = paramPath.substring(0, paramPath.length() - ".as(Reference)".length()) + "Reference";
RuntimeChildResourceDefinition resDef = (RuntimeChildResourceDefinition) def; }
resourceTypes = resDef.getResourceTypes();
} else { if (paramPath.contains(".extension(")) {
throw new ConfigurationException("Property " + paramPath + " of type " + myResourceName + " is not a resource: " + def.getClass()); int startIdx = paramPath.indexOf(".extension(");
int endIdx = paramPath.indexOf(')', startIdx);
if (startIdx != -1 && endIdx != -1) {
paramPath = paramPath.substring(0, startIdx + 10) + paramPath.substring(endIdx + 1);
}
}
BaseRuntimeChildDefinition def = myContext.newTerser().getDefinition(myResourceType, paramPath);
if (def instanceof RuntimeChildChoiceDefinition) {
RuntimeChildChoiceDefinition choiceDef = (RuntimeChildChoiceDefinition) def;
resourceTypes.addAll(choiceDef.getResourceTypes());
} else if (def instanceof RuntimeChildResourceDefinition) {
RuntimeChildResourceDefinition resDef = (RuntimeChildResourceDefinition) def;
resourceTypes.addAll(resDef.getResourceTypes());
} else {
throw new ConfigurationException("Property " + paramPath + " of type " + myResourceName + " is not a resource: " + def.getClass());
}
} }
if (resourceTypes.isEmpty()) {
for (BaseRuntimeElementDefinition<?> next : myContext.getElementDefinitions()) {
if (next instanceof RuntimeResourceDefinition) {
RuntimeResourceDefinition nextResDef = (RuntimeResourceDefinition)next;
resourceTypes.add(nextResDef.getImplementingClass());
}
}
}
resourceId = ref.getValue(); resourceId = ref.getValue();
} else { } else {
@ -888,32 +911,32 @@ public class SearchBuilder implements ISearchBuilder {
private Predicate createCompositeParamPart(String theResourceName, Root<ResourceTable> theRoot, RuntimeSearchParam theParam, IQueryParameterType leftValue) { private Predicate createCompositeParamPart(String theResourceName, Root<ResourceTable> theRoot, RuntimeSearchParam theParam, IQueryParameterType leftValue) {
Predicate retVal = null; Predicate retVal = null;
switch (theParam.getParamType()) { switch (theParam.getParamType()) {
case STRING: { case STRING: {
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> stringJoin = theRoot.join("myParamsString", JoinType.INNER); From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> stringJoin = theRoot.join("myParamsString", JoinType.INNER);
retVal = createPredicateString(leftValue, theResourceName, theParam.getName(), myBuilder, stringJoin); retVal = createPredicateString(leftValue, theResourceName, theParam.getName(), myBuilder, stringJoin);
break; break;
} }
case TOKEN: { case TOKEN: {
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> tokenJoin = theRoot.join("myParamsToken", JoinType.INNER); From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> tokenJoin = theRoot.join("myParamsToken", JoinType.INNER);
retVal = createPredicateToken(leftValue, theResourceName, theParam.getName(), myBuilder, tokenJoin); retVal = createPredicateToken(leftValue, theResourceName, theParam.getName(), myBuilder, tokenJoin);
break; break;
} }
case DATE: { case DATE: {
From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> dateJoin = theRoot.join("myParamsDate", JoinType.INNER); From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> dateJoin = theRoot.join("myParamsDate", JoinType.INNER);
retVal = createPredicateDate(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin); retVal = createPredicateDate(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin);
break; break;
} }
case QUANTITY: { case QUANTITY: {
From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> dateJoin = theRoot.join("myParamsQuantity", JoinType.INNER); From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> dateJoin = theRoot.join("myParamsQuantity", JoinType.INNER);
retVal = createPredicateQuantity(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin); retVal = createPredicateQuantity(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin);
break; break;
} }
case COMPOSITE: case COMPOSITE:
case HAS: case HAS:
case NUMBER: case NUMBER:
case REFERENCE: case REFERENCE:
case URI: case URI:
break; break;
} }
if (retVal == null) { if (retVal == null) {
@ -984,41 +1007,41 @@ public class SearchBuilder implements ISearchBuilder {
String invalidMessageName) { String invalidMessageName) {
Predicate num; Predicate num;
switch (thePrefix) { switch (thePrefix) {
case GREATERTHAN: case GREATERTHAN:
num = builder.gt(thePath, theValue); num = builder.gt(thePath, theValue);
break; break;
case GREATERTHAN_OR_EQUALS: case GREATERTHAN_OR_EQUALS:
num = builder.ge(thePath, theValue); num = builder.ge(thePath, theValue);
break; break;
case LESSTHAN: case LESSTHAN:
num = builder.lt(thePath, theValue); num = builder.lt(thePath, theValue);
break; break;
case LESSTHAN_OR_EQUALS: case LESSTHAN_OR_EQUALS:
num = builder.le(thePath, theValue); num = builder.le(thePath, theValue);
break; break;
case APPROXIMATE: case APPROXIMATE:
case EQUAL: case EQUAL:
case NOT_EQUAL: case NOT_EQUAL:
BigDecimal mul = calculateFuzzAmount(thePrefix, theValue); BigDecimal mul = calculateFuzzAmount(thePrefix, theValue);
BigDecimal low = theValue.subtract(mul, MathContext.DECIMAL64); BigDecimal low = theValue.subtract(mul, MathContext.DECIMAL64);
BigDecimal high = theValue.add(mul, MathContext.DECIMAL64); BigDecimal high = theValue.add(mul, MathContext.DECIMAL64);
Predicate lowPred; Predicate lowPred;
Predicate highPred; Predicate highPred;
if (thePrefix != ParamPrefixEnum.NOT_EQUAL) { if (thePrefix != ParamPrefixEnum.NOT_EQUAL) {
lowPred = builder.ge(thePath.as(BigDecimal.class), low); lowPred = builder.ge(thePath.as(BigDecimal.class), low);
highPred = builder.le(thePath.as(BigDecimal.class), high); highPred = builder.le(thePath.as(BigDecimal.class), high);
num = builder.and(lowPred, highPred); num = builder.and(lowPred, highPred);
ourLog.trace("Searching for {} <= val <= {}", low, high); ourLog.trace("Searching for {} <= val <= {}", low, high);
} else { } else {
// Prefix was "ne", so reverse it! // Prefix was "ne", so reverse it!
lowPred = builder.lt(thePath.as(BigDecimal.class), low); lowPred = builder.lt(thePath.as(BigDecimal.class), low);
highPred = builder.gt(thePath.as(BigDecimal.class), high); highPred = builder.gt(thePath.as(BigDecimal.class), high);
num = builder.or(lowPred, highPred); num = builder.or(lowPred, highPred);
} }
break; break;
default: default:
String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, invalidMessageName, thePrefix.getValue(), theParam.getValueAsQueryToken(myContext)); String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, invalidMessageName, thePrefix.getValue(), theParam.getValueAsQueryToken(myContext));
throw new InvalidRequestException(msg); throw new InvalidRequestException(msg);
} }
if (theParamName == null) { if (theParamName == null) {
@ -1388,36 +1411,36 @@ public class SearchBuilder implements ISearchBuilder {
String[] sortAttrName; String[] sortAttrName;
switch (param.getParamType()) { switch (param.getParamType()) {
case STRING: case STRING:
joinAttrName = "myParamsString"; joinAttrName = "myParamsString";
sortAttrName = new String[] { "myValueExact" }; sortAttrName = new String[] { "myValueExact" };
break; break;
case DATE: case DATE:
joinAttrName = "myParamsDate"; joinAttrName = "myParamsDate";
sortAttrName = new String[] { "myValueLow" }; sortAttrName = new String[] { "myValueLow" };
break; break;
case REFERENCE: case REFERENCE:
joinAttrName = "myResourceLinks"; joinAttrName = "myResourceLinks";
sortAttrName = new String[] { "myTargetResourcePid" }; sortAttrName = new String[] { "myTargetResourcePid" };
break; break;
case TOKEN: case TOKEN:
joinAttrName = "myParamsToken"; joinAttrName = "myParamsToken";
sortAttrName = new String[] { "mySystem", "myValue" }; sortAttrName = new String[] { "mySystem", "myValue" };
break; break;
case NUMBER: case NUMBER:
joinAttrName = "myParamsNumber"; joinAttrName = "myParamsNumber";
sortAttrName = new String[] { "myValue" }; sortAttrName = new String[] { "myValue" };
break; break;
case URI: case URI:
joinAttrName = "myParamsUri"; joinAttrName = "myParamsUri";
sortAttrName = new String[] { "myUri" }; sortAttrName = new String[] { "myUri" };
break; break;
case QUANTITY: case QUANTITY:
joinAttrName = "myParamsQuantity"; joinAttrName = "myParamsQuantity";
sortAttrName = new String[] { "myValue" }; sortAttrName = new String[] { "myValue" };
break; break;
default: default:
throw new InvalidRequestException("This server does not support _sort specifications of type " + param.getParamType() + " - Can't serve _sort=" + theSort.getParamName()); throw new InvalidRequestException("This server does not support _sort specifications of type " + param.getParamType() + " - Can't serve _sort=" + theSort.getParamName());
} }
From<?, ?> join = theFrom.join(joinAttrName, JoinType.LEFT); From<?, ?> join = theFrom.join(joinAttrName, JoinType.LEFT);
@ -1500,10 +1523,10 @@ public class SearchBuilder implements ISearchBuilder {
for (int i = 0; i < pids.size(); i += maxLoad) { for (int i = 0; i < pids.size(); i += maxLoad) {
int to = i + maxLoad; int to = i + maxLoad;
to = Math.min(to, pids.size()); to = Math.min(to, pids.size());
List<Long> pidsSubList = pids.subList(i, to); List<Long> pidsSubList = pids.subList(i, to);
doLoadPids(theResourceListToPopulate, theRevIncludedPids, theForHistoryOperation, entityManager, context, theDao, position, pidsSubList); doLoadPids(theResourceListToPopulate, theRevIncludedPids, theForHistoryOperation, entityManager, context, theDao, position, pidsSubList);
} }
} }
private void doLoadPids(List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation, EntityManager entityManager, FhirContext context, IDao theDao, private void doLoadPids(List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation, EntityManager entityManager, FhirContext context, IDao theDao,
@ -1724,49 +1747,49 @@ public class SearchBuilder implements ISearchBuilder {
RuntimeSearchParam nextParamDef = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName); RuntimeSearchParam nextParamDef = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
if (nextParamDef != null) { if (nextParamDef != null) {
switch (nextParamDef.getParamType()) { switch (nextParamDef.getParamType()) {
case DATE: case DATE:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) { for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
addPredicateDate(theResourceName, theParamName, nextAnd); addPredicateDate(theResourceName, theParamName, nextAnd);
} }
break; break;
case QUANTITY: case QUANTITY:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) { for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
addPredicateQuantity(theResourceName, theParamName, nextAnd); addPredicateQuantity(theResourceName, theParamName, nextAnd);
} }
break; break;
case REFERENCE: case REFERENCE:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) { for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
addPredicateReference(theResourceName, theParamName, nextAnd); addPredicateReference(theResourceName, theParamName, nextAnd);
} }
break; break;
case STRING: case STRING:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) { for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
addPredicateString(theResourceName, theParamName, nextAnd); addPredicateString(theResourceName, theParamName, nextAnd);
} }
break; break;
case TOKEN: case TOKEN:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) { for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
addPredicateToken(theResourceName, theParamName, nextAnd); addPredicateToken(theResourceName, theParamName, nextAnd);
} }
break; break;
case NUMBER: case NUMBER:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) { for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
addPredicateNumber(theResourceName, theParamName, nextAnd); addPredicateNumber(theResourceName, theParamName, nextAnd);
} }
break; break;
case COMPOSITE: case COMPOSITE:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) { for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
addPredicateComposite(theResourceName, nextParamDef, nextAnd); addPredicateComposite(theResourceName, nextParamDef, nextAnd);
} }
break; break;
case URI: case URI:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) { for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
addPredicateUri(theResourceName, theParamName, nextAnd); addPredicateUri(theResourceName, theParamName, nextAnd);
} }
break; break;
case HAS: case HAS:
// should not happen // should not happen
break; break;
} }
} else { } else {
if (Constants.PARAM_CONTENT.equals(theParamName) || Constants.PARAM_TEXT.equals(theParamName)) { if (Constants.PARAM_CONTENT.equals(theParamName) || Constants.PARAM_TEXT.equals(theParamName)) {
@ -1786,35 +1809,35 @@ public class SearchBuilder implements ISearchBuilder {
private IQueryParameterType toParameterType(RuntimeSearchParam theParam) { private IQueryParameterType toParameterType(RuntimeSearchParam theParam) {
IQueryParameterType qp; IQueryParameterType qp;
switch (theParam.getParamType()) { switch (theParam.getParamType()) {
case DATE: case DATE:
qp = new DateParam(); qp = new DateParam();
break; break;
case NUMBER: case NUMBER:
qp = new NumberParam(); qp = new NumberParam();
break; break;
case QUANTITY: case QUANTITY:
qp = new QuantityParam(); qp = new QuantityParam();
break; break;
case STRING: case STRING:
qp = new StringParam(); qp = new StringParam();
break; break;
case TOKEN: case TOKEN:
qp = new TokenParam(); qp = new TokenParam();
break; break;
case COMPOSITE: case COMPOSITE:
List<RuntimeSearchParam> compositeOf = theParam.getCompositeOf(); List<RuntimeSearchParam> compositeOf = theParam.getCompositeOf();
if (compositeOf.size() != 2) { if (compositeOf.size() != 2) {
throw new InternalErrorException("Parameter " + theParam.getName() + " has " + compositeOf.size() + " composite parts. Don't know how handlt this."); throw new InternalErrorException("Parameter " + theParam.getName() + " has " + compositeOf.size() + " composite parts. Don't know how handlt this.");
} }
IQueryParameterType leftParam = toParameterType(compositeOf.get(0)); IQueryParameterType leftParam = toParameterType(compositeOf.get(0));
IQueryParameterType rightParam = toParameterType(compositeOf.get(1)); IQueryParameterType rightParam = toParameterType(compositeOf.get(1));
qp = new CompositeParam<IQueryParameterType, IQueryParameterType>(leftParam, rightParam); qp = new CompositeParam<IQueryParameterType, IQueryParameterType>(leftParam, rightParam);
break; break;
case REFERENCE: case REFERENCE:
qp = new ReferenceParam(); qp = new ReferenceParam();
break; break;
default: default:
throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType()); throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType());
} }
return qp; return qp;
} }
@ -1908,8 +1931,8 @@ public class SearchBuilder implements ISearchBuilder {
} }
private void fetchNext() { private void fetchNext() {
// If we don't have // If we don't have
if (myResultsIterator == null) { if (myResultsIterator == null) {
final TypedQuery<Long> query = createQuery(mySort); final TypedQuery<Long> query = createQuery(mySort);
myResultsIterator = query.getResultList().iterator(); myResultsIterator = query.getResultList().iterator();

View File

@ -38,7 +38,7 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
// TODO: make this nullable=false and a primitive (written may 2017) // TODO: make this nullable=false and a primitive (written may 2017)
@Field() @Field()
@Column(name = "SP_MISSING", nullable = true) @Column(name = "SP_MISSING", nullable = true)
private Boolean myMissing; private Boolean myMissing = Boolean.FALSE;
@Field @Field
@Column(name = "SP_NAME", length = MAX_SP_NAME, nullable = false) @Column(name = "SP_NAME", length = MAX_SP_NAME, nullable = false)

View File

@ -201,35 +201,134 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
} }
@Test @Test
public void testSearchForExtensionReference() { public void testSearchForExtensionReferenceWithNonMatchingTarget() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
SearchParameter eyeColourSp = new SearchParameter(); siblingSp.setCode("sibling");
eyeColourSp.addBase("Patient"); siblingSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE);
eyeColourSp.setCode("sibling"); siblingSp.setTitle("Sibling");
eyeColourSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE); siblingSp.setExpression("Patient.extension('http://acme.org/sibling')");
eyeColourSp.setTitle("Sibling"); siblingSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
eyeColourSp.setExpression("Patient.extension('http://acme.org/sibling')"); siblingSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
eyeColourSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL); siblingSp.getTarget().add(new CodeType("Organization"));
eyeColourSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE); mySearchParameterDao.create(siblingSp, mySrd);
mySearchParameterDao.create(eyeColourSp, mySrd);
mySearchParamRegsitry.forceRefresh(); mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient(); Patient p1 = new Patient();
p1.setActive(true); p1.addName().setFamily("P1");
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless(); IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient(); Patient p2 = new Patient();
p2.setActive(true); p2.addName().setFamily("P2");
p2.addExtension().setUrl("http://acme.org/sibling").setValue(new Reference(p1id)); p2.addExtension().setUrl("http://acme.org/sibling").setValue(new Reference(p1id));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless(); IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
// Try with custom gender SP SearchParameterMap map;
SearchParameterMap map = new SearchParameterMap(); IBundleProvider results;
List<String> foundResources;
// Search by ref
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam(p1id.getValue())); map.add("sibling", new ReferenceParam(p1id.getValue()));
IBundleProvider results = myPatientDao.search(map); results = myPatientDao.search(map);
List<String> foundResources = toUnqualifiedVersionlessIdValues(results); foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, empty());
// Search by chain
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam("name", "P1"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, empty());
}
@Test
public void testSearchForExtensionReferenceWithoutTarget() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("sibling");
siblingSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE);
siblingSp.setTitle("Sibling");
siblingSp.setExpression("Patient.extension('http://acme.org/sibling')");
siblingSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.addName().setFamily("P1");
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().setFamily("P2");
p2.addExtension().setUrl("http://acme.org/sibling").setValue(new Reference(p1id));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
// Search by ref
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam(p1id.getValue()));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
// Search by chain
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam("name", "P1"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
}
@Test
public void testSearchForExtensionReferenceWithTarget() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("sibling");
siblingSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE);
siblingSp.setTitle("Sibling");
siblingSp.setExpression("Patient.extension('http://acme.org/sibling')");
siblingSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
siblingSp.getTarget().add(new CodeType("Patient"));
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.addName().setFamily("P1");
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().setFamily("P2");
p2.addExtension().setUrl("http://acme.org/sibling").setValue(new Reference(p1id));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
// Search by ref
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam(p1id.getValue()));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
// Search by chain
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam("name", "P1"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue())); assertThat(foundResources, contains(p2id.getValue()));
} }

View File

@ -93,6 +93,10 @@
will live after all. Interceptors registered to this method will now be treated will live after all. Interceptors registered to this method will now be treated
appropriately if they implement IServerOperationInterceptor too. appropriately if they implement IServerOperationInterceptor too.
</action> </action>
<action type="fix">
JPA server did not correctly support searching on a custom search parameter whose
path pointed to an extension, where the client used a chained value.
</action>
</release> </release>
<release version="2.4" date="2017-04-19"> <release version="2.4" date="2017-04-19">
<action type="add"> <action type="add">