Add _has support (this is an experimental parameter being tested)
This commit is contained in:
parent
bca8589177
commit
3e77d3e37e
|
@ -82,7 +82,12 @@ public enum RestSearchParameterTypeEnum {
|
|||
*
|
||||
* A search parameter that searches on a quantity.
|
||||
*/
|
||||
URI("uri", "http://hl7.org/fhir/search-param-type"),
|
||||
URI("uri", "http://hl7.org/fhir/search-param-type"),
|
||||
|
||||
/**
|
||||
* _has parameter
|
||||
*/
|
||||
HAS("string", "http://hl7.org/fhir/search-param-type"),
|
||||
|
||||
;
|
||||
|
||||
|
@ -106,6 +111,10 @@ public enum RestSearchParameterTypeEnum {
|
|||
|
||||
static {
|
||||
for (RestSearchParameterTypeEnum next : RestSearchParameterTypeEnum.values()) {
|
||||
if (next == HAS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CODE_TO_ENUM.put(next.getCode(), next);
|
||||
|
||||
if (!SYSTEM_TO_CODE_TO_ENUM.containsKey(next.getSystem())) {
|
||||
|
|
|
@ -49,6 +49,9 @@ import ca.uhn.fhir.rest.param.DateAndListParam;
|
|||
import ca.uhn.fhir.rest.param.DateOrListParam;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.HasAndListParam;
|
||||
import ca.uhn.fhir.rest.param.HasOrListParam;
|
||||
import ca.uhn.fhir.rest.param.HasParam;
|
||||
import ca.uhn.fhir.rest.param.NumberAndListParam;
|
||||
import ca.uhn.fhir.rest.param.NumberOrListParam;
|
||||
import ca.uhn.fhir.rest.param.NumberParam;
|
||||
|
@ -130,6 +133,10 @@ public class SearchParameter extends BaseQueryParameter {
|
|||
ourParamTypes.put(CompositeOrListParam.class, RestSearchParameterTypeEnum.COMPOSITE);
|
||||
ourParamTypes.put(CompositeAndListParam.class, RestSearchParameterTypeEnum.COMPOSITE);
|
||||
ourParamQualifiers.put(RestSearchParameterTypeEnum.COMPOSITE, CollectionUtil.newSet(Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING));
|
||||
|
||||
ourParamTypes.put(HasParam.class, RestSearchParameterTypeEnum.HAS);
|
||||
ourParamTypes.put(HasOrListParam.class, RestSearchParameterTypeEnum.HAS);
|
||||
ourParamTypes.put(HasAndListParam.class, RestSearchParameterTypeEnum.HAS);
|
||||
}
|
||||
|
||||
private List<Class<? extends IQueryParameterType>> myCompositeTypes;
|
||||
|
@ -318,6 +325,8 @@ public class SearchParameter extends BaseQueryParameter {
|
|||
myParamType = RestSearchParameterTypeEnum.QUANTITY;
|
||||
} else if (ReferenceParam.class.isAssignableFrom(type)) {
|
||||
myParamType = RestSearchParameterTypeEnum.REFERENCE;
|
||||
} else if (HasParam.class.isAssignableFrom(type)) {
|
||||
myParamType = RestSearchParameterTypeEnum.STRING;
|
||||
} else {
|
||||
throw new ConfigurationException("Unknown search parameter type: " + type);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package ca.uhn.fhir.rest.param;
|
||||
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
|
||||
public class HasAndListParam extends BaseAndListParam<HasOrListParam> {
|
||||
|
||||
@Override
|
||||
HasOrListParam newInstance() {
|
||||
return new HasOrListParam();
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
@Override
|
||||
public HasAndListParam addAnd(HasOrListParam theValue) {
|
||||
addValue(theValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package ca.uhn.fhir.rest.param;
|
||||
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
|
||||
public class HasOrListParam extends BaseOrListParam<HasOrListParam, HasParam> {
|
||||
|
||||
@CoverageIgnore
|
||||
@Override
|
||||
HasParam newInstance() {
|
||||
return new HasParam();
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
@Override
|
||||
public HasOrListParam addOr(HasParam theParameter) {
|
||||
add(theParameter);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package ca.uhn.fhir.rest.param;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
/**
|
||||
* Implementation of the _has method parameter
|
||||
*/
|
||||
public class HasParam extends BaseParam implements IQueryParameterType {
|
||||
|
||||
private String myOwningFieldName;
|
||||
private String myParameterName;
|
||||
private String myParameterValue;
|
||||
private String myTargetResourceType;
|
||||
|
||||
public HasParam() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public HasParam(String theTargetResourceType, String theOwningFieldName, String theParameterName, String theParameterValue) {
|
||||
this();
|
||||
myTargetResourceType = theTargetResourceType;
|
||||
myOwningFieldName = theOwningFieldName;
|
||||
myParameterName = theParameterName;
|
||||
myParameterValue = theParameterValue;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
String doGetQueryParameterQualifier() {
|
||||
return myTargetResourceType + ':' + myParameterName + ':' + myParameterValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
String doGetValueAsQueryToken(FhirContext theContext) {
|
||||
return myParameterValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
void doSetValueAsQueryToken(String theQualifier, String theValue) {
|
||||
if (!theQualifier.startsWith(":")) {
|
||||
throwInvalidSyntaxException(theQualifier);
|
||||
}
|
||||
int colonIndex0 = theQualifier.indexOf(':', 1);
|
||||
validateColon(theQualifier, colonIndex0);
|
||||
int colonIndex1 = theQualifier.indexOf(':', colonIndex0 + 1);
|
||||
validateColon(theQualifier, colonIndex1);
|
||||
|
||||
myTargetResourceType = theQualifier.substring(1, colonIndex0);
|
||||
myOwningFieldName = theQualifier.substring(colonIndex0 + 1, colonIndex1);
|
||||
myParameterName = theQualifier.substring(colonIndex1 + 1);
|
||||
myParameterValue = theValue;
|
||||
}
|
||||
|
||||
public String getOwningFieldName() {
|
||||
return myOwningFieldName;
|
||||
}
|
||||
|
||||
public String getParameterName() {
|
||||
return myParameterName;
|
||||
}
|
||||
|
||||
public String getParameterValue() {
|
||||
return myParameterValue;
|
||||
}
|
||||
|
||||
public String getTargetResourceType() {
|
||||
return myTargetResourceType;
|
||||
}
|
||||
|
||||
private static void validateColon(String theParameterName, int colonIndex) {
|
||||
if (colonIndex == -1) {
|
||||
throwInvalidSyntaxException(theParameterName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void throwInvalidSyntaxException(String theParameterName) {
|
||||
throw new InvalidRequestException("Invalid _has parameter syntax: " + theParameterName);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class UrlPathTokenizer extends StringTokenizer {
|
||||
|
|
|
@ -944,7 +944,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
// nothing
|
||||
}
|
||||
|
||||
protected <R extends IBaseResource> Set<Long> processMatchUrl(String theMatchUrl, Class<R> theResourceType) {
|
||||
@Override
|
||||
public <R extends IBaseResource> Set<Long> processMatchUrl(String theMatchUrl, Class<R> theResourceType) {
|
||||
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
|
||||
|
||||
SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
|
@ -63,7 +65,9 @@ public interface IDao {
|
|||
*/
|
||||
void injectDependenciesIntoBundleProvider(PersistedJpaBundleProvider theProvider);
|
||||
|
||||
<R extends IBaseResource> Set<Long> processMatchUrl(String theMatchUrl, Class<R> theResourceType);
|
||||
|
||||
IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation);
|
||||
|
||||
|
||||
<R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation);
|
||||
}
|
||||
|
|
|
@ -112,12 +112,16 @@ import ca.uhn.fhir.model.dstu.resource.BaseResource;
|
|||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.param.CompositeParam;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.HasAndListParam;
|
||||
import ca.uhn.fhir.rest.param.HasOrListParam;
|
||||
import ca.uhn.fhir.rest.param.HasParam;
|
||||
import ca.uhn.fhir.rest.param.NumberParam;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
|
@ -133,6 +137,7 @@ import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
public class SearchBuilder {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class);
|
||||
|
@ -234,7 +239,80 @@ public class SearchBuilder {
|
|||
doSetPids(q.getResultList());
|
||||
}
|
||||
|
||||
private void addPredicateId(Set<Long> thePids, DateRangeParam theLastUpdated) {
|
||||
private void addPredicateHas(List<List<? extends IQueryParameterType>> theHasParameters, DateRangeParam theLastUpdated) {
|
||||
|
||||
for (List<? extends IQueryParameterType> nextOrList : theHasParameters) {
|
||||
|
||||
StringBuilder valueBuilder = new StringBuilder();
|
||||
String targetResourceType = null;
|
||||
String owningParameter = null;
|
||||
String parameterName = null;
|
||||
for (IQueryParameterType nextParam : nextOrList) {
|
||||
HasParam next = (HasParam) nextParam;
|
||||
if (valueBuilder.length() > 0) {
|
||||
valueBuilder.append(',');
|
||||
}
|
||||
valueBuilder.append(UrlUtil.escape(next.getValueAsQueryToken(myContext)));
|
||||
targetResourceType = next.getTargetResourceType();
|
||||
owningParameter = next.getOwningFieldName();
|
||||
parameterName = next.getParameterName();
|
||||
}
|
||||
|
||||
if (valueBuilder.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String matchUrl = targetResourceType + '?' + UrlUtil.escape(parameterName) + '=' + valueBuilder.toString();
|
||||
RuntimeResourceDefinition targetResourceDefinition;
|
||||
try {
|
||||
targetResourceDefinition = myContext.getResourceDefinition(targetResourceType);
|
||||
} catch (DataFormatException e) {
|
||||
throw new InvalidRequestException("Invalid resource type: " + targetResourceType);
|
||||
}
|
||||
|
||||
RuntimeSearchParam owningParameterDef = targetResourceDefinition.getSearchParam(parameterName.replaceAll("\\..*", ""));
|
||||
if (owningParameterDef == null) {
|
||||
throw new InvalidRequestException("Unknown parameter name: " + targetResourceType + ':' + parameterName);
|
||||
}
|
||||
|
||||
owningParameterDef = targetResourceDefinition.getSearchParam(owningParameter);
|
||||
if (owningParameterDef == null) {
|
||||
throw new InvalidRequestException("Unknown parameter name: " + targetResourceType + ':' + owningParameter);
|
||||
}
|
||||
|
||||
Class<? extends IBaseResource> resourceType = targetResourceDefinition.getImplementingClass();
|
||||
Set<Long> match = myCallingDao.processMatchUrl(matchUrl, resourceType);
|
||||
if (match.isEmpty()) {
|
||||
doSetPids(new ArrayList<Long>());
|
||||
return;
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceLink> from = cq.from(ResourceLink.class);
|
||||
cq.select(from.get("myTargetResourcePid").as(Long.class));
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(builder.equal(from.get("mySourceResourceType"), targetResourceType));
|
||||
predicates.add(from.get("mySourceResourcePid").in(match));
|
||||
predicates.add(createResourceLinkPathPredicate(myContext, owningParameter, from, resourceType));
|
||||
predicates.add(builder.equal(from.get("myTargetResourceType"), myResourceName));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
createPredicateLastUpdatedForResourceLink(builder, from, predicates);
|
||||
|
||||
cq.where(toArray(predicates));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
doSetPids(q.getResultList());
|
||||
if (doHaveNoResults()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void addPredicateId(Set<Long> thePids) {
|
||||
if (thePids == null || thePids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -256,7 +334,7 @@ public class SearchBuilder {
|
|||
doSetPids(q.getResultList());
|
||||
}
|
||||
|
||||
private void addPredicateLanguage(List<List<? extends IQueryParameterType>> theList, DateRangeParam theLastUpdated) {
|
||||
private void addPredicateLanguage(List<List<? extends IQueryParameterType>> theList) {
|
||||
for (List<? extends IQueryParameterType> nextList : theList) {
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
|
@ -286,7 +364,7 @@ public class SearchBuilder {
|
|||
predicates.add(from.get("myLanguage").as(String.class).in(values));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
createPredicateLastUpdatedForResourceTable(builder, from, predicates);
|
||||
|
||||
|
||||
predicates.add(builder.isNull(from.get("myDeleted")));
|
||||
|
||||
cq.where(toArray(predicates));
|
||||
|
@ -324,7 +402,7 @@ public class SearchBuilder {
|
|||
throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "multipleParamsWithSameNameOneIsMissingTrue", theParamName));
|
||||
}
|
||||
Predicate singleCode = from.get("mySourceResource").isNotNull();
|
||||
Predicate name = createResourceLinkPathPredicate(theParamName, theBuilder, from);
|
||||
Predicate name = createResourceLinkPathPredicate(theParamName, from);
|
||||
codePredicates.add(theBuilder.and(name, singleCode));
|
||||
missingFalse = true;
|
||||
}
|
||||
|
@ -379,7 +457,7 @@ public class SearchBuilder {
|
|||
predicates.add(builder.or(toArray(codePredicates)));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
|
||||
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
|
@ -424,7 +502,7 @@ public class SearchBuilder {
|
|||
subQ.select(subQfrom.get("mySourceResourcePid").as(Long.class));
|
||||
|
||||
// subQ.where(builder.equal(subQfrom.get("myParamName"), theParamName));
|
||||
Predicate path = createResourceLinkPathPredicate(theParamName, builder, subQfrom);
|
||||
Predicate path = createResourceLinkPathPredicate(theParamName, subQfrom);
|
||||
subQ.where(path);
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
|
@ -459,7 +537,7 @@ public class SearchBuilder {
|
|||
Predicate singleCode = createPredicateQuantity(builder, from, nextOr);
|
||||
codePredicates.add(singleCode);
|
||||
}
|
||||
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
|
@ -606,7 +684,7 @@ public class SearchBuilder {
|
|||
}
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(createResourceLinkPathPredicate(theParamName, builder, from));
|
||||
predicates.add(createResourceLinkPathPredicate(theParamName, from));
|
||||
predicates.add(builder.or(toArray(codePredicates)));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("mySourceResourcePid").as(Long.class));
|
||||
createPredicateLastUpdatedForResourceLink(builder, from, predicates);
|
||||
|
@ -643,10 +721,10 @@ public class SearchBuilder {
|
|||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
predicates.add(builder.or(toArray(codePredicates)));
|
||||
|
||||
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
|
||||
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
|
@ -666,63 +744,54 @@ public class SearchBuilder {
|
|||
}
|
||||
|
||||
/*
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
Subquery<Long> subQ = cq.subquery(Long.class);
|
||||
Root<? extends BaseResourceIndexedSearchParam> subQfrom = subQ.from(theParamTable);
|
||||
subQ.select(subQfrom.get("myResourcePid").as(Long.class));
|
||||
Predicate subQname = builder.equal(subQfrom.get("myParamName"), theParamName);
|
||||
Predicate subQtype = builder.equal(subQfrom.get("myResourceType"), myResourceName);
|
||||
subQ.where(builder.and(subQtype, subQname));
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.isNull(from.get("myDeleted")));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
*/
|
||||
* CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = builder.createQuery(Long.class); Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
* cq.select(from.get("myId").as(Long.class));
|
||||
*
|
||||
* Subquery<Long> subQ = cq.subquery(Long.class); Root<? extends BaseResourceIndexedSearchParam> subQfrom = subQ.from(theParamTable); subQ.select(subQfrom.get("myResourcePid").as(Long.class));
|
||||
* Predicate subQname = builder.equal(subQfrom.get("myParamName"), theParamName); Predicate subQtype = builder.equal(subQfrom.get("myResourceType"), myResourceName);
|
||||
* subQ.where(builder.and(subQtype, subQname));
|
||||
*
|
||||
* List<Predicate> predicates = new ArrayList<Predicate>(); predicates.add(builder.not(builder.in(from.get("myId")).value(subQ))); predicates.add(builder.equal(from.get("myResourceType"),
|
||||
* myResourceName)); predicates.add(builder.isNull(from.get("myDeleted"))); createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
*/
|
||||
|
||||
List<Pair<String, String>> notTags = Lists.newArrayList();
|
||||
for (List<? extends IQueryParameterType> nextAndParams : theList) {
|
||||
for (IQueryParameterType nextOrParams : nextAndParams) {
|
||||
if (nextOrParams instanceof TokenParam) {
|
||||
TokenParam param = (TokenParam)nextOrParams;
|
||||
TokenParam param = (TokenParam) nextOrParams;
|
||||
if (param.getModifier() == TokenParamModifier.NOT) {
|
||||
if (isNotBlank(param.getSystem()) || isNotBlank(param.getValue())) {
|
||||
notTags.add(Pair.of(param.getSystem(), param.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a parameter of ResourceType?_tag:not=foo
|
||||
* This means match resources that don't have the given tag(s)
|
||||
* We have a parameter of ResourceType?_tag:not=foo This means match resources that don't have the given tag(s)
|
||||
*/
|
||||
if (notTags.isEmpty() == false) {
|
||||
// CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
// CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
// Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
// cq.select(from.get("myId").as(Long.class));
|
||||
//
|
||||
// Subquery<Long> subQ = cq.subquery(Long.class);
|
||||
// Root<ResourceTag> subQfrom = subQ.from(ResourceTag.class);
|
||||
// subQ.select(subQfrom.get("myResourceId").as(Long.class));
|
||||
// Predicate subQname = builder.equal(subQfrom.get("myParamName"), theParamName);
|
||||
// Predicate subQtype = builder.equal(subQfrom.get("myResourceType"), myResourceName);
|
||||
// subQ.where(builder.and(subQtype, subQname));
|
||||
//
|
||||
// List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
// predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
||||
// predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
// predicates.add(builder.isNull(from.get("myDeleted")));
|
||||
// createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
// CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
// CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
// Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
// cq.select(from.get("myId").as(Long.class));
|
||||
//
|
||||
// Subquery<Long> subQ = cq.subquery(Long.class);
|
||||
// Root<ResourceTag> subQfrom = subQ.from(ResourceTag.class);
|
||||
// subQ.select(subQfrom.get("myResourceId").as(Long.class));
|
||||
// Predicate subQname = builder.equal(subQfrom.get("myParamName"), theParamName);
|
||||
// Predicate subQtype = builder.equal(subQfrom.get("myResourceType"), myResourceName);
|
||||
// subQ.where(builder.and(subQtype, subQname));
|
||||
//
|
||||
// List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
// predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
||||
// predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
// predicates.add(builder.isNull(from.get("myDeleted")));
|
||||
// createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
}
|
||||
|
||||
|
||||
for (List<? extends IQueryParameterType> nextAndParams : theList) {
|
||||
boolean haveTags = false;
|
||||
for (IQueryParameterType nextParamUncasted : nextAndParams) {
|
||||
|
@ -768,23 +837,23 @@ public class SearchBuilder {
|
|||
tokens.add(Pair.of(system, code));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (tokens.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (paramInverted) {
|
||||
ourLog.debug("Searching for _tag:not");
|
||||
|
||||
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> newFrom = cq.from(ResourceTable.class);
|
||||
|
||||
|
||||
Subquery<Long> subQ = cq.subquery(Long.class);
|
||||
Root<ResourceTag> subQfrom = subQ.from(ResourceTag.class);
|
||||
subQ.select(subQfrom.get("myResourceId").as(Long.class));
|
||||
|
||||
cq.select(newFrom.get("myId").as(Long.class));
|
||||
|
||||
|
||||
List<Predicate> andPredicates = new ArrayList<Predicate>();
|
||||
andPredicates = new ArrayList<Predicate>();
|
||||
andPredicates.add(builder.equal(newFrom.get("myResourceType"), myResourceName));
|
||||
|
@ -793,34 +862,34 @@ public class SearchBuilder {
|
|||
Subquery<Long> defJoin = subQ.subquery(Long.class);
|
||||
Root<TagDefinition> defJoinFrom = defJoin.from(TagDefinition.class);
|
||||
defJoin.select(defJoinFrom.get("myId").as(Long.class));
|
||||
|
||||
|
||||
subQ.where(subQfrom.get("myTagId").as(Long.class).in(defJoin));
|
||||
|
||||
|
||||
List<Predicate> orPredicates = createPredicateTagList(defJoinFrom, builder, tagType, tokens);
|
||||
defJoin.where(toArray(orPredicates));
|
||||
|
||||
|
||||
cq.where(toArray(andPredicates));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
Set<Long> pids = new HashSet<Long>(q.getResultList());
|
||||
doSetPids(pids);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTag> from = cq.from(ResourceTag.class);
|
||||
List<Predicate> andPredicates = new ArrayList<Predicate>();
|
||||
andPredicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
From<ResourceTag, TagDefinition> defJoin = from.join("myTag");
|
||||
|
||||
|
||||
Join<?, ResourceTable> defJoin2 = from.join("myResource");
|
||||
|
||||
|
||||
Predicate notDeletedPredicatePrediate = builder.isNull(defJoin2.get("myDeleted"));
|
||||
andPredicates.add(notDeletedPredicatePrediate);
|
||||
|
||||
|
||||
List<Predicate> orPredicates = createPredicateTagList(defJoin, builder, tagType, tokens);
|
||||
andPredicates.add(builder.or(toArray(orPredicates)));
|
||||
|
||||
|
||||
if (theLastUpdated != null) {
|
||||
andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin2));
|
||||
}
|
||||
|
@ -1172,7 +1241,7 @@ public class SearchBuilder {
|
|||
} else {
|
||||
singleCode = theBuilder.and(system, code, num);
|
||||
}
|
||||
|
||||
|
||||
return singleCode;
|
||||
}
|
||||
|
||||
|
@ -1192,8 +1261,7 @@ public class SearchBuilder {
|
|||
thePredicates.add(theExpression.in(myPids));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
|
||||
|
@ -1233,7 +1301,7 @@ public class SearchBuilder {
|
|||
|
||||
private List<Predicate> createPredicateTagList(Path<TagDefinition> theDefJoin, CriteriaBuilder theBuilder, TagTypeEnum theTagType, List<Pair<String, String>> theTokens) {
|
||||
Predicate typePrediate = theBuilder.equal(theDefJoin.get("myTagType"), theTagType);
|
||||
|
||||
|
||||
List<Predicate> orPredicates = Lists.newArrayList();
|
||||
for (Pair<String, String> next : theTokens) {
|
||||
Predicate codePrediate = theBuilder.equal(theDefJoin.get("myCode"), next.getRight());
|
||||
|
@ -1289,20 +1357,22 @@ public class SearchBuilder {
|
|||
singleCodePredicates.add(theBuilder.equal(theFrom.get("myValue"), code));
|
||||
} else {
|
||||
/*
|
||||
* As of HAPI FHIR 1.5, if the client searched for a token with a system but
|
||||
* no specified value this means to match all tokens with the given value.
|
||||
* As of HAPI FHIR 1.5, if the client searched for a token with a system but no specified value this means to match all tokens with the given value.
|
||||
*
|
||||
* I'm not sure I agree with this, but hey.. FHIR-I voted and this was
|
||||
* the result :)
|
||||
* I'm not sure I agree with this, but hey.. FHIR-I voted and this was the result :)
|
||||
*/
|
||||
//singleCodePredicates.add(theBuilder.isNull(theFrom.get("myValue")));
|
||||
// singleCodePredicates.add(theBuilder.isNull(theFrom.get("myValue")));
|
||||
}
|
||||
Predicate singleCode = theBuilder.and(toArray(singleCodePredicates));
|
||||
return singleCode;
|
||||
}
|
||||
|
||||
private Predicate createResourceLinkPathPredicate(String theParamName, CriteriaBuilder builder, Root<? extends ResourceLink> from) {
|
||||
RuntimeSearchParam param = myContext.getResourceDefinition(myResourceType).getSearchParam(theParamName);
|
||||
private Predicate createResourceLinkPathPredicate(String theParamName, Root<? extends ResourceLink> from) {
|
||||
return createResourceLinkPathPredicate(myContext, theParamName, from, myResourceType);
|
||||
}
|
||||
|
||||
private static Predicate createResourceLinkPathPredicate(FhirContext theContext, String theParamName, Root<? extends ResourceLink> from, Class<? extends IBaseResource> resourceType) {
|
||||
RuntimeSearchParam param = theContext.getResourceDefinition(resourceType).getSearchParam(theParamName);
|
||||
List<String> path = param.getPathsSplit();
|
||||
Predicate type = from.get("mySourcePath").in(path);
|
||||
return type;
|
||||
|
@ -1575,7 +1645,7 @@ public class SearchBuilder {
|
|||
for (Include next : myParams.getRevIncludes()) {
|
||||
mySearchEntity.getIncludes().add(new SearchInclude(mySearchEntity, next.getValue(), true, next.isRecurse()));
|
||||
}
|
||||
|
||||
|
||||
if (myParams.isPersistResults()) {
|
||||
myEntityManager.persist(mySearchEntity);
|
||||
for (SearchInclude next : mySearchEntity.getIncludes()) {
|
||||
|
@ -1583,7 +1653,7 @@ public class SearchBuilder {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IBundleProvider search(final SearchParameterMap theParams) {
|
||||
myParams = theParams;
|
||||
StopWatch w = new StopWatch();
|
||||
|
@ -1738,7 +1808,7 @@ public class SearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
addPredicateId(joinPids, theLastUpdated);
|
||||
addPredicateId(joinPids);
|
||||
if (doHaveNoResults()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1747,7 +1817,11 @@ public class SearchBuilder {
|
|||
|
||||
} else if (nextParamName.equals(BaseResource.SP_RES_LANGUAGE)) {
|
||||
|
||||
addPredicateLanguage(nextParamEntry.getValue(), theLastUpdated);
|
||||
addPredicateLanguage(nextParamEntry.getValue());
|
||||
|
||||
} else if (nextParamName.equals("_has")) {
|
||||
|
||||
addPredicateHas(nextParamEntry.getValue(), theLastUpdated);
|
||||
|
||||
} else if (nextParamName.equals(Constants.PARAM_TAG) || nextParamName.equals(Constants.PARAM_PROFILE) || nextParamName.equals(Constants.PARAM_SECURITY)) {
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import javax.persistence.EntityManager;
|
|||
import org.apache.commons.io.IOUtils;
|
||||
import org.hibernate.search.jpa.Search;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -53,6 +54,7 @@ import ca.uhn.fhir.rest.method.IRequestOperationCallback;
|
|||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class BaseJpaTest {
|
||||
|
@ -128,6 +130,7 @@ public class BaseJpaTest {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
protected List<IIdType> toUnqualifiedVersionlessIds(List<IBaseResource> theFound) {
|
||||
List<IIdType> retVal = new ArrayList<IIdType>();
|
||||
for (IBaseResource next : theFound) {
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.hl7.fhir.dstu3.model.CodeableConcept;
|
|||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.ConceptMap;
|
||||
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.dstu3.model.DateTimeType;
|
||||
import org.hl7.fhir.dstu3.model.DateType;
|
||||
import org.hl7.fhir.dstu3.model.Device;
|
||||
|
@ -78,6 +79,7 @@ import ca.uhn.fhir.rest.api.SortSpec;
|
|||
import ca.uhn.fhir.rest.param.CompositeParam;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.HasParam;
|
||||
import ca.uhn.fhir.rest.param.NumberParam;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
|
@ -323,6 +325,124 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
assertTrue(patients.size() >= 2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHasParameter() {
|
||||
IIdType pid0, pid1;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("001");
|
||||
patient.addName().addFamily("Tester").addGiven("Joe");
|
||||
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("001");
|
||||
patient.addName().addFamily("Tester").addGiven("Joe");
|
||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
{
|
||||
Observation obs = new Observation();
|
||||
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
|
||||
obs.getSubject().setReferenceElement(pid0);
|
||||
myObservationDao.create(obs, mySrd);
|
||||
}
|
||||
{
|
||||
Device device = new Device();
|
||||
device.addIdentifier().setValue("DEVICEID");
|
||||
IIdType devId = myDeviceDao.create(device, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.addIdentifier().setSystem("urn:system").setValue("NOLINK");
|
||||
obs.setDevice(new Reference(devId));
|
||||
myObservationDao.create(obs, mySrd);
|
||||
}
|
||||
|
||||
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_has", new HasParam("Observation", "subject", "identifier", "urn:system|FOO"));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(pid0.getValue()));
|
||||
|
||||
// No targets exist
|
||||
params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_has", new HasParam("Observation", "subject", "identifier", "urn:system|UNKNOWN"));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), empty());
|
||||
|
||||
// Target exists but doesn't link to us
|
||||
params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_has", new HasParam("Observation", "subject", "identifier", "urn:system|NOLINK"));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasParameterChained() {
|
||||
IIdType pid0;
|
||||
{
|
||||
Device device = new Device();
|
||||
device.addIdentifier().setSystem("urn:system").setValue("DEVICEID");
|
||||
IIdType devId = myDeviceDao.create(device, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setGender(AdministrativeGender.MALE);
|
||||
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
|
||||
obs.setDevice(new Reference(devId));
|
||||
obs.setSubject(new Reference(pid0));
|
||||
myObservationDao.create(obs, mySrd).getId();
|
||||
}
|
||||
|
||||
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_has", new HasParam("Observation", "subject", "device.identifier", "urn:system|DEVICEID"));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(pid0.getValue()));
|
||||
|
||||
// No targets exist
|
||||
params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_has", new HasParam("Observation", "subject", "identifier", "urn:system|UNKNOWN"));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), empty());
|
||||
|
||||
// Target exists but doesn't link to us
|
||||
params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_has", new HasParam("Observation", "subject", "identifier", "urn:system|NOLINK"));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasParameterInvalidResourceType() {
|
||||
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_has", new HasParam("Observation__", "subject", "identifier", "urn:system|FOO"));
|
||||
try {
|
||||
myPatientDao.search(params);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid resource type: Observation__", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasParameterInvalidTargetPath() {
|
||||
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_has", new HasParam("Observation", "soooooobject", "identifier", "urn:system|FOO"));
|
||||
try {
|
||||
myPatientDao.search(params);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown parameter name: Observation:soooooobject", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasParameterInvalidSearchParam() {
|
||||
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_has", new HasParam("Observation", "subject", "IIIIDENFIEYR", "urn:system|FOO"));
|
||||
try {
|
||||
myPatientDao.search(params);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown parameter name: Observation:IIIIDENFIEYR", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByIdParam() {
|
||||
IIdType id1;
|
||||
|
|
|
@ -29,7 +29,9 @@ import java.net.SocketTimeoutException;
|
|||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -88,6 +90,7 @@ import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
|
|||
import org.hl7.fhir.dstu3.model.TemporalPrecisionEnum;
|
||||
import org.hl7.fhir.dstu3.model.UnsignedIntType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -95,11 +98,13 @@ import org.junit.Ignore;
|
|||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.HasParam;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||
|
@ -112,6 +117,7 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
|
@ -123,7 +129,74 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
protected List<String> toUnqualifiedVersionlessIdValues(IBaseBundle theFound) {
|
||||
List<String> retVal = new ArrayList<String>();
|
||||
|
||||
List<IBaseResource> res = BundleUtil.toListOfResources(myFhirCtx, theFound);
|
||||
int size = res.size();
|
||||
ourLog.info("Found {} results", size);
|
||||
for (IBaseResource next : res) {
|
||||
retVal.add(next.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasParameter() throws Exception {
|
||||
IIdType pid0, pid1;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("001");
|
||||
patient.addName().addFamily("Tester").addGiven("Joe");
|
||||
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("001");
|
||||
patient.addName().addFamily("Tester").addGiven("Joe");
|
||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
{
|
||||
Observation obs = new Observation();
|
||||
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
|
||||
obs.getSubject().setReferenceElement(pid0);
|
||||
myObservationDao.create(obs, mySrd);
|
||||
}
|
||||
{
|
||||
Device device = new Device();
|
||||
device.addIdentifier().setValue("DEVICEID");
|
||||
IIdType devId = myDeviceDao.create(device, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.addIdentifier().setSystem("urn:system").setValue("NOLINK");
|
||||
obs.setDevice(new Reference(devId));
|
||||
myObservationDao.create(obs, mySrd);
|
||||
}
|
||||
|
||||
String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escape("urn:system|FOO");
|
||||
List<String> ids = searchAndReturnUnqualifiedIdValues(uri);
|
||||
assertThat(ids, contains(pid0.getValue()));
|
||||
|
||||
}
|
||||
private List<String> searchAndReturnUnqualifiedIdValues(String uri) throws IOException, ClientProtocolException {
|
||||
List<String> ids;
|
||||
HttpGet get = new HttpGet(uri);
|
||||
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent());
|
||||
ourLog.info(resp);
|
||||
Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, resp);
|
||||
ids = toUnqualifiedVersionlessIdValues(bundle);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Issue submitted by Bryn
|
||||
*/
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.hl7.fhir.utilities.Table;
|
|||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.ucum.Decimal;
|
||||
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.ElementUtil;
|
||||
|
||||
/**
|
||||
|
@ -1297,7 +1298,7 @@ public class FHIRPathEngine {
|
|||
if ((lunit == null && runit == null) || lunit.equals(runit)) {
|
||||
return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"));
|
||||
} else {
|
||||
throw new Error("Canonical Comparison isn't done yet");
|
||||
throw new InternalErrorException("Canonical Comparison isn't done yet");
|
||||
}
|
||||
}
|
||||
return new ArrayList<Base>();
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu3.model.HumanName;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.param.HasAndListParam;
|
||||
import ca.uhn.fhir.rest.param.HasParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class SearchHasParamDstu3Test {
|
||||
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamDstu3Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static String ourLastMethod;
|
||||
private static HasAndListParam ourLastParam;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourLastMethod = null;
|
||||
ourLastParam = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_has:Encounter:patient:type=SURG");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("search", ourLastMethod);
|
||||
|
||||
HasParam param = ourLastParam.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0);
|
||||
assertEquals("Encounter", param.getTargetResourceType());
|
||||
assertEquals("patient", param.getOwningFieldName());
|
||||
assertEquals("type", param.getParameterName());
|
||||
assertEquals("SURG", param.getParameterValue());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||
|
||||
servlet.setResourceProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Search()
|
||||
public List search(
|
||||
@OptionalParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier,
|
||||
@OptionalParam(name="_has") HasAndListParam theParam
|
||||
) {
|
||||
ourLastMethod = "search";
|
||||
ourLastParam = theParam;
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("1"));
|
||||
return retVal;
|
||||
}
|
||||
//@formatter:on
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -109,6 +109,21 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
ourLog.info("Validated the following:\n{}", ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* FHIRPathEngine was throwing Error...
|
||||
*/
|
||||
@Test
|
||||
public void testValidateCrucibleCarePlan() throws Exception {
|
||||
org.hl7.fhir.dstu3.model.Bundle bundle;
|
||||
String name = "profiles-resources";
|
||||
ourLog.info("Uploading " + name);
|
||||
String vsContents;
|
||||
vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/crucible-condition.xml"), "UTF-8");
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(vsContents);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testValidateBundleWithObservations() throws Exception {
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
|
||||
<Condition xmlns="http://hl7.org/fhir">
|
||||
<language value="en-US"/>
|
||||
<text>
|
||||
<status value="generated"/>
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">LFX0OELiP7YLinZcLkDvGg==
|
||||
</div>
|
||||
</text>
|
||||
<identifier>
|
||||
<use value="usual"/>
|
||||
<type>
|
||||
<coding>
|
||||
<system value="http://hl7.org/fhir/v2/0203"/>
|
||||
<code value="NIIP"/>
|
||||
<display value="Agxy+U8E4tAl2YMAJr3W5A=="/>
|
||||
<userSelected value="true"/>
|
||||
</coding>
|
||||
<text value="2yIAYV0uJhUo8/jA9BFrNw=="/>
|
||||
</type>
|
||||
<system value="http://www.example.com/ARFxP1cRuBsiiRm8ItsLDw=="/>
|
||||
<value value="SSuSavjIfCfV1CmHkB/5dw=="/>
|
||||
<period>
|
||||
<start value="2016-05-10T11:15:50.926Z"/>
|
||||
<end value="2016-05-10T11:15:50.926Z"/>
|
||||
</period>
|
||||
<assigner>
|
||||
<display value="Organization /C0aapiYwhfYXKEbVqd1pQ=="/>
|
||||
</assigner>
|
||||
</identifier>
|
||||
<patient>
|
||||
<display value="Patient juQzQzr118UocFDUTDWU1A=="/>
|
||||
</patient>
|
||||
<encounter>
|
||||
<display value="Encounter M3JplRlbYvurXozRWwa4SA=="/>
|
||||
</encounter>
|
||||
<asserter>
|
||||
<display value="Patient rM3hr4legsdSyMdeOCuNCA=="/>
|
||||
</asserter>
|
||||
<dateRecorded value="2016-05-10"/>
|
||||
<code>
|
||||
<coding>
|
||||
<system value="http://snomed.info/sct"/>
|
||||
<code value="24043009"/>
|
||||
<display value="mvD9sUIGVZ5VlVlpT0Bi+Q=="/>
|
||||
<userSelected value="true"/>
|
||||
</coding>
|
||||
<text value="6Yxhlofy4YfGYnoaugMglg=="/>
|
||||
</code>
|
||||
<category>
|
||||
<coding>
|
||||
<system value="http://hl7.org/fhir/condition-category"/>
|
||||
<code value="diagnosis"/>
|
||||
<display value="LGhObAmHrUkk1R1XVDfTqQ=="/>
|
||||
<userSelected value="false"/>
|
||||
</coding>
|
||||
<text value="mgeqavdXG3vDFQDPvj3Ezw=="/>
|
||||
</category>
|
||||
<clinicalStatus value="relapse"/>
|
||||
<verificationStatus value="entered-in-error"/>
|
||||
<severity>
|
||||
<coding>
|
||||
<system value="http://snomed.info/sct"/>
|
||||
<code value="6736007"/>
|
||||
<display value="UkiGVgl4AgzXz7EAIWamDQ=="/>
|
||||
<userSelected value="true"/>
|
||||
</coding>
|
||||
<text value="ta92LvfcafCmLDuO7FnPxQ=="/>
|
||||
</severity>
|
||||
<onsetRange>
|
||||
<low>
|
||||
<value value="0.8595246484876637"/>
|
||||
<comparator value="<"/>
|
||||
<unit value="nOYYJzovcpNk1Xkv0lxPJg=="/>
|
||||
<system value="http://www.example.com/A7CUYaICHEikpE+P3dn/Tw=="/>
|
||||
<code value="eURHPiUCEHOjjj8onWeCeQ=="/>
|
||||
</low>
|
||||
<high>
|
||||
<value value="0.6433405008182949"/>
|
||||
<comparator value="<"/>
|
||||
<unit value="Gw/H12dT45hubnfT2Huzag=="/>
|
||||
<system value="http://www.example.com/3IE059a8KnG2sCAltoTe3w=="/>
|
||||
<code value="6U7C4akdF5ebCAzi7yadMA=="/>
|
||||
</high>
|
||||
</onsetRange>
|
||||
<abatementString value="ULx7Wh1Za6evfMcYlzdaFw=="/>
|
||||
<stage>
|
||||
<summary>
|
||||
<coding>
|
||||
<system value="http://snomed.info/sct"/>
|
||||
<code value="369845003"/>
|
||||
<display value="eLtqGjb34NIU4N+S0/UAGg=="/>
|
||||
<userSelected value="true"/>
|
||||
</coding>
|
||||
<text value="QHlmzLaSqS5YTy8x8FJ5TQ=="/>
|
||||
</summary>
|
||||
<assessment>
|
||||
<display value="DiagnosticReport GXL2HO68TMjjIKnAA/hVWA=="/>
|
||||
</assessment>
|
||||
</stage>
|
||||
<evidence>
|
||||
<code>
|
||||
<coding>
|
||||
<system value="http://snomed.info/sct"/>
|
||||
<code value="33552005"/>
|
||||
<display value="as3M2/YDGLNGTKV4A+Lyuw=="/>
|
||||
<userSelected value="false"/>
|
||||
</coding>
|
||||
<text value="aX2ZT5h9E3NdckmCx0xjwQ=="/>
|
||||
</code>
|
||||
<detail>
|
||||
<display value="Resource jzni40//4JvMxTzrIBmZpw=="/>
|
||||
</detail>
|
||||
</evidence>
|
||||
<bodySite>
|
||||
<coding>
|
||||
<system value="http://snomed.info/sct"/>
|
||||
<code value="2841007"/>
|
||||
<display value="w2Dr0BZ1fORpTGd9HIhjww=="/>
|
||||
<userSelected value="false"/>
|
||||
</coding>
|
||||
<text value="v/MGWdCezPpmiY+pth6amQ=="/>
|
||||
</bodySite>
|
||||
<notes value="nUQSRKDFhDwxvvVeJH6oVQ=="/>
|
||||
</Condition>
|
|
@ -23,9 +23,6 @@ import ca.uhn.fhir.model.${version}.resource.*; //
|
|||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.param.*;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
// import ca.uhn.fhir.model.dstu.resource.Binary;
|
||||
// import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
// import ca.uhn.fhir.model.api.Bundle;
|
||||
|
||||
public class ${className}ResourceProvider extends
|
||||
## We have specialized base classes for RPs that handle certain resource types. These
|
||||
|
@ -76,6 +73,10 @@ public class ${className}ResourceProvider extends
|
|||
@OptionalParam(name=ca.uhn.fhir.rest.server.Constants.PARAM_PROFILE)
|
||||
UriAndListParam theSearchForProfile,
|
||||
|
||||
@Description(shortDefinition="Return resources linked to by the given target")
|
||||
@OptionalParam(name="_has")
|
||||
HasAndListParam theHas,
|
||||
|
||||
#foreach ( $param in $searchParams ) #{if}(true) #{end}
|
||||
|
||||
@Description(shortDefinition="${param.description}")
|
||||
|
@ -147,6 +148,7 @@ public class ${className}ResourceProvider extends
|
|||
paramMap.add(ca.uhn.fhir.rest.server.Constants.PARAM_TAG, theSearchForTag);
|
||||
paramMap.add(ca.uhn.fhir.rest.server.Constants.PARAM_SECURITY, theSearchForSecurity);
|
||||
paramMap.add(ca.uhn.fhir.rest.server.Constants.PARAM_PROFILE, theSearchForProfile);
|
||||
paramMap.add("_has", theHas);
|
||||
#foreach ( $param in $searchParams )
|
||||
paramMap.add("${param.name}", the${param.nameCapitalized});
|
||||
#end
|
||||
|
|
Loading…
Reference in New Issue