Work on extractor
This commit is contained in:
parent
c455a46160
commit
6580c98ed7
|
@ -43,6 +43,7 @@ import java.io.Writer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -354,17 +355,18 @@ public class XmlParser extends BaseParser {
|
|||
}
|
||||
|
||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||
INarrative narr = (INarrative) nextChild.getAccessor().getFirstValueOrNull(theElement);
|
||||
|
||||
Optional<IBase> narr = nextChild.getAccessor().getFirstValueOrNull(theElement);
|
||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
||||
if (gen != null && (narr == null || narr.isEmpty())) {
|
||||
if (gen != null && narr.isPresent() == false) {
|
||||
gen.populateResourceNarrative(myContext, theResource);
|
||||
}
|
||||
if (narr != null && narr.isEmpty() == false) {
|
||||
|
||||
narr = nextChild.getAccessor().getFirstValueOrNull(theElement);
|
||||
if (narr.isPresent()) {
|
||||
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
||||
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
|
||||
BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
|
||||
encodeChildElementToStreamWriter(theResource, theEventWriter, nextChild, narr, childName, type, null, theContainedResource, nextChildElem, theEncodeContext);
|
||||
encodeChildElementToStreamWriter(theResource, theEventWriter, nextChild, narr.get(), childName, type, null, theContainedResource, nextChildElem, theEncodeContext);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -475,11 +475,10 @@ public class FhirTerser {
|
|||
* @param thePath The path for the element to be accessed.
|
||||
* @return A list of values of type {@link Object}.
|
||||
*/
|
||||
public List<Object> getValues(IBaseResource theResource, String thePath) {
|
||||
public List<IBase> getValues(IBaseResource theResource, String thePath) {
|
||||
Class<IBase> wantedClass = IBase.class;
|
||||
|
||||
List values = getValues(theResource, thePath, wantedClass);
|
||||
return values;
|
||||
return getValues(theResource, thePath, wantedClass);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -491,11 +490,10 @@ public class FhirTerser {
|
|||
* @param theCreate When set to <code>true</code>, the terser will create a null-valued element where none exists.
|
||||
* @return A list of values of type {@link Object}.
|
||||
*/
|
||||
public List<Object> getValues(IBaseResource theResource, String thePath, boolean theCreate) {
|
||||
public List<IBase> getValues(IBaseResource theResource, String thePath, boolean theCreate) {
|
||||
Class<IBase> wantedClass = IBase.class;
|
||||
|
||||
List retVal = getValues(theResource, thePath, wantedClass, theCreate);
|
||||
return retVal;
|
||||
return getValues(theResource, thePath, wantedClass, theCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -508,11 +506,10 @@ public class FhirTerser {
|
|||
* @param theAddExtension When set to <code>true</code>, the terser will add a null-valued extension where one or more such extensions already exist.
|
||||
* @return A list of values of type {@link Object}.
|
||||
*/
|
||||
public List<Object> getValues(IBaseResource theResource, String thePath, boolean theCreate, boolean theAddExtension) {
|
||||
public List<IBase> getValues(IBaseResource theResource, String thePath, boolean theCreate, boolean theAddExtension) {
|
||||
Class<IBase> wantedClass = IBase.class;
|
||||
|
||||
List retVal = getValues(theResource, thePath, wantedClass, theCreate, theAddExtension);
|
||||
return retVal;
|
||||
return getValues(theResource, thePath, wantedClass, theCreate, theAddExtension);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,34 +19,34 @@ package ca.uhn.fhir.rest.client.method;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationMethodBinding.class);
|
||||
private BundleTypeEnum myBundleType;
|
||||
private boolean myCanOperateAtInstanceLevel;
|
||||
private boolean myCanOperateAtServerLevel;
|
||||
private boolean myCanOperateAtTypeLevel;
|
||||
private String myDescription;
|
||||
private final boolean myIdempotent;
|
||||
private final Integer myIdParamIndex;
|
||||
|
@ -63,15 +63,6 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
myBundleType = theBundleType;
|
||||
myIdempotent = theIdempotent;
|
||||
myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod, getContext());
|
||||
if (myIdParamIndex != null) {
|
||||
for (Annotation next : theMethod.getParameterAnnotations()[myIdParamIndex]) {
|
||||
if (next instanceof IdParam) {
|
||||
myCanOperateAtTypeLevel = ((IdParam) next).optional() == true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
myCanOperateAtTypeLevel = true;
|
||||
}
|
||||
|
||||
Description description = theMethod.getAnnotation(Description.class);
|
||||
if (description != null) {
|
||||
|
@ -113,7 +104,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE;
|
||||
}
|
||||
|
||||
myReturnParams = new ArrayList<OperationMethodBinding.ReturnType>();
|
||||
myReturnParams = new ArrayList<>();
|
||||
if (theReturnParams != null) {
|
||||
for (OperationParam next : theReturnParams) {
|
||||
ReturnType type = new ReturnType();
|
||||
|
@ -133,13 +124,6 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
}
|
||||
|
||||
if (myIdParamIndex != null) {
|
||||
myCanOperateAtInstanceLevel = true;
|
||||
}
|
||||
if (getResourceName() == null) {
|
||||
myCanOperateAtServerLevel = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
|
||||
|
@ -169,10 +153,6 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return myOtherOperatiopnType;
|
||||
}
|
||||
|
||||
public List<ReturnType> getReturnParams() {
|
||||
return Collections.unmodifiableList(myReturnParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReturnTypeEnum getReturnType() {
|
||||
return myReturnType;
|
||||
|
@ -197,18 +177,6 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return createOperationInvocation(getContext(), getResourceName(), id, null, myName, parameters, false);
|
||||
}
|
||||
|
||||
public boolean isCanOperateAtInstanceLevel() {
|
||||
return this.myCanOperateAtInstanceLevel;
|
||||
}
|
||||
|
||||
public boolean isCanOperateAtServerLevel() {
|
||||
return this.myCanOperateAtServerLevel;
|
||||
}
|
||||
|
||||
public boolean isCanOperateAtTypeLevel() {
|
||||
return myCanOperateAtTypeLevel;
|
||||
}
|
||||
|
||||
public boolean isIdempotent() {
|
||||
return myIdempotent;
|
||||
}
|
||||
|
@ -243,9 +211,9 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return new HttpPostClientInvocation(theContext, theInput, b.toString());
|
||||
}
|
||||
FhirTerser t = theContext.newTerser();
|
||||
List<Object> parameters = t.getValues(theInput, "Parameters.parameter");
|
||||
List<IBase> parameters = t.getValues(theInput, "Parameters.parameter");
|
||||
|
||||
Map<String, List<String>> params = new LinkedHashMap<String, List<String>>();
|
||||
Map<String, List<String>> params = new LinkedHashMap<>();
|
||||
for (Object nextParameter : parameters) {
|
||||
IPrimitiveType<?> nextNameDt = (IPrimitiveType<?>) t.getSingleValueOrNull((IBase) nextParameter, "name");
|
||||
if (nextNameDt == null || nextNameDt.isEmpty()) {
|
||||
|
@ -254,7 +222,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
String nextName = nextNameDt.getValueAsString();
|
||||
if (!params.containsKey(nextName)) {
|
||||
params.put(nextName, new ArrayList<String>());
|
||||
params.put(nextName, new ArrayList<>());
|
||||
}
|
||||
|
||||
IBaseDatatype value = (IBaseDatatype) t.getSingleValueOrNull((IBase) nextParameter, "value[x]");
|
||||
|
|
|
@ -143,14 +143,10 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
}
|
||||
ResourceIndexedSearchParamToken obj = (ResourceIndexedSearchParamToken) theObj;
|
||||
EqualsBuilder b = new EqualsBuilder();
|
||||
b.append(getResourceType(), obj.getResourceType());
|
||||
b.append(getParamName(), obj.getParamName());
|
||||
b.append(getResource(), obj.getResource());
|
||||
b.append(getSystem(), obj.getSystem());
|
||||
b.append(getValue(), obj.getValue());
|
||||
b.append(getHashIdentity(), obj.getHashIdentity());
|
||||
b.append(getHashSystem(), obj.getHashSystem());
|
||||
b.append(getHashSystemAndValue(), obj.getHashSystemAndValue());
|
||||
b.append(getHashValue(), obj.getHashValue());
|
||||
return b.isEquals();
|
||||
}
|
||||
|
||||
|
@ -163,11 +159,6 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
myHashSystem = theHashSystem;
|
||||
}
|
||||
|
||||
private Long getHashIdentity() {
|
||||
calculateHashes();
|
||||
return myHashIdentity;
|
||||
}
|
||||
|
||||
private void setHashIdentity(Long theHashIdentity) {
|
||||
myHashIdentity = theHashIdentity;
|
||||
}
|
||||
|
@ -223,8 +214,8 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
public int hashCode() {
|
||||
calculateHashes();
|
||||
HashCodeBuilder b = new HashCodeBuilder();
|
||||
b.append(getResourceType());
|
||||
b.append(getParamName());
|
||||
b.append(getResource());
|
||||
b.append(getSystem());
|
||||
b.append(getValue());
|
||||
return b.toHashCode();
|
||||
|
@ -238,8 +229,8 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
b.append("resourceType", getResourceType());
|
||||
b.append("paramName", getParamName());
|
||||
b.append("resourceId", getResourcePid());
|
||||
b.append("system", getSystem());
|
||||
b.append("value", getValue());
|
||||
return b.build();
|
||||
|
|
|
@ -20,29 +20,34 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.measure.quantity.Quantity;
|
||||
import javax.measure.unit.NonSI;
|
||||
import javax.measure.unit.Unit;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public abstract class BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||
|
||||
public static final Pattern SPLIT = Pattern.compile("\\||( or )");
|
||||
public static final Pattern SPLIT_R4 = Pattern.compile("\\|");
|
||||
private static final Pattern ASPLIT = Pattern.compile("\\||( or )");
|
||||
private static final Pattern ASPLIT_R4 = Pattern.compile("\\|");
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSearchParamExtractor.class);
|
||||
@Autowired
|
||||
private FhirContext myContext;
|
||||
|
@ -66,20 +71,6 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
return myIgnoredForSearchDatatypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
||||
List<PathAndRef> refs = new ArrayList<PathAndRef>();
|
||||
String[] nextPathsSplit = theNextSpDef.getPath().split("\\|");
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
nextPath = nextPath.trim();
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject != null) {
|
||||
refs.add(new PathAndRef(nextPath, nextObject));
|
||||
}
|
||||
}
|
||||
}
|
||||
return refs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override parent because we're using FHIRPath here
|
||||
|
@ -87,12 +78,17 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
protected List<IBase> extractValues(String thePaths, IBaseResource theResource) {
|
||||
List<IBase> values = new ArrayList<>();
|
||||
if (isNotBlank(thePaths)) {
|
||||
String[] nextPathsSplit = SPLIT_R4.split(thePaths);
|
||||
String[] nextPathsSplit = split(thePaths);
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
List<? extends IBase> allValues;
|
||||
|
||||
Supplier<List<? extends IBase>> allValuesFunc = getPathValueExtractor(theResource, nextPath);
|
||||
allValues = allValuesFunc.get();
|
||||
IValueExtractor allValuesFunc = getPathValueExtractor(theResource, nextPath);
|
||||
try {
|
||||
allValues = allValuesFunc.get();
|
||||
} catch (Exception e) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
|
||||
throw new InternalErrorException(msg, e);
|
||||
}
|
||||
|
||||
values.addAll(allValues);
|
||||
}
|
||||
|
@ -109,7 +105,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
return values;
|
||||
}
|
||||
|
||||
protected abstract Supplier<List<? extends IBase>> getPathValueExtractor(IBaseResource theResource, String theNextPath);
|
||||
protected abstract IValueExtractor getPathValueExtractor(IBaseResource theResource, String thePaths);
|
||||
|
||||
protected FhirContext getContext() {
|
||||
return myContext;
|
||||
|
@ -141,6 +137,685 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
addIgnoredType(getContext(), "LocationPositionComponent", myIgnoredForSearchDatatypes);
|
||||
}
|
||||
|
||||
private void addQuantity_Quantity(ResourceTable theEntity, Set<ResourceIndexedSearchParamQuantity> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> quantityDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Quantity");
|
||||
BaseRuntimeChildDefinition quantityValueChild = quantityDefinition.getChildByName("value");
|
||||
BaseRuntimeChildDefinition quantitySystemChild = quantityDefinition.getChildByName("system");
|
||||
BaseRuntimeChildDefinition quantityCodeChild = quantityDefinition.getChildByName("code");
|
||||
|
||||
Optional<IPrimitiveType<BigDecimal>> valueField = quantityValueChild.getAccessor().getFirstValueOrNull(theValue);
|
||||
if (valueField.isPresent() && valueField.get().getValue() != null) {
|
||||
BigDecimal nextValueValue = valueField.get().getValue();
|
||||
String system = extractValueAsString(quantitySystemChild, theValue);
|
||||
String code = extractValueAsString(quantityCodeChild, theValue);
|
||||
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(theSearchParam.getName(), nextValueValue, system, code);
|
||||
nextEntity.setResource(theEntity);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addQuantity_Money(ResourceTable theEntity, Set<ResourceIndexedSearchParamQuantity> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> moneyDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Money");
|
||||
BaseRuntimeChildDefinition moneyValueChild = moneyDefinition.getChildByName("value");
|
||||
BaseRuntimeChildDefinition moneyCurrencyChild = moneyDefinition.getChildByName("currency");
|
||||
|
||||
Optional<IPrimitiveType<BigDecimal>> valueField = moneyValueChild.getAccessor().getFirstValueOrNull(theValue);
|
||||
if (valueField.isPresent() && valueField.get().getValue() != null) {
|
||||
BigDecimal nextValueValue = valueField.get().getValue();
|
||||
|
||||
String nextValueString = "urn:iso:std:iso:4217";
|
||||
String nextValueCode = extractValueAsString(moneyCurrencyChild, theValue);
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(theSearchParam.getName(), nextValueValue, nextValueString, nextValueCode);
|
||||
nextEntity.setResource(theEntity);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addQuantity_Range(ResourceTable theEntity, Set<ResourceIndexedSearchParamQuantity> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> rangeDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Range");
|
||||
BaseRuntimeChildDefinition rangeLowValueChild = rangeDefinition.getChildByName("low");
|
||||
BaseRuntimeChildDefinition rangeHighValueChild = rangeDefinition.getChildByName("high");
|
||||
|
||||
Optional<IBase> low = rangeLowValueChild.getAccessor().getFirstValueOrNull(theValue);
|
||||
low.ifPresent(theIBase -> addQuantity_Quantity(theEntity, theParams, theSearchParam, theIBase));
|
||||
|
||||
Optional<IBase> high = rangeHighValueChild.getAccessor().getFirstValueOrNull(theValue);
|
||||
high.ifPresent(theIBase -> addQuantity_Quantity(theEntity, theParams, theSearchParam, theIBase));
|
||||
}
|
||||
|
||||
private void addToken_Identifier(Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> identifierDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Identifier");
|
||||
BaseRuntimeChildDefinition identifierSystemValueChild = identifierDefinition.getChildByName("system");
|
||||
BaseRuntimeChildDefinition identifierValueValueChild = identifierDefinition.getChildByName("value");
|
||||
BaseRuntimeChildDefinition identifierTextValueChild = identifierDefinition.getChildByName("text");
|
||||
|
||||
String system = extractValueAsString(identifierSystemValueChild, theValue);
|
||||
String value = extractValueAsString(identifierValueValueChild, theValue);
|
||||
if (isNotBlank(value)) {
|
||||
addTokenIfNotBlank(theParams, theSearchParam, system, value);
|
||||
}
|
||||
|
||||
String text = extractValueAsString(identifierTextValueChild, theValue);
|
||||
if (isNotBlank(text)) {
|
||||
addSearchTermIfNotBlank(theParams, theSearchParam, text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addToken_CodeableConcept(Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> codeableConceptDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("CodeableConcept");
|
||||
BaseRuntimeChildDefinition codeableConceptCodingValueChild = codeableConceptDefinition.getChildByName("coding");
|
||||
BaseRuntimeChildDefinition codeableConceptTextValueChild = codeableConceptDefinition.getChildByName("text");
|
||||
|
||||
List<IBase> codings = codeableConceptCodingValueChild.getAccessor().getValues(theValue);
|
||||
for (IBase nextCoding : codings) {
|
||||
addToken_Coding(theParams, theSearchParam, nextCoding);
|
||||
}
|
||||
|
||||
String text = extractValueAsString(codeableConceptTextValueChild, theValue);
|
||||
if (isNotBlank(text)) {
|
||||
addSearchTermIfNotBlank(theParams, theSearchParam, text);
|
||||
}
|
||||
}
|
||||
|
||||
private void addToken_Coding(Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> codingDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Coding");
|
||||
BaseRuntimeChildDefinition codingSystemValueChild = codingDefinition.getChildByName("system");
|
||||
BaseRuntimeChildDefinition codingCodeValueChild = codingDefinition.getChildByName("code");
|
||||
BaseRuntimeChildDefinition codingDisplayValueChild = codingDefinition.getChildByName("display");
|
||||
|
||||
String system = extractValueAsString(codingSystemValueChild, theValue);
|
||||
String code = extractValueAsString(codingCodeValueChild, theValue);
|
||||
addTokenIfNotBlank(theParams, theSearchParam, system, code);
|
||||
|
||||
String text = extractValueAsString(codingDisplayValueChild, theValue);
|
||||
addSearchTermIfNotBlank(theParams, theSearchParam, text);
|
||||
}
|
||||
|
||||
private void addToken_ContactPoint(Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> contactPointDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("ContactPoint");
|
||||
BaseRuntimeChildDefinition contactPointSystemValueChild = contactPointDefinition.getChildByName("system");
|
||||
BaseRuntimeChildDefinition contactPointValueValueChild = contactPointDefinition.getChildByName("value");
|
||||
|
||||
String system = extractValueAsString(contactPointSystemValueChild, theValue);
|
||||
String value = extractValueAsString(contactPointValueValueChild, theValue);
|
||||
addTokenIfNotBlank(theParams, theSearchParam, system, value);
|
||||
}
|
||||
|
||||
private void addToken_PatientCommunication(Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> patientDefinition = getContext().getResourceDefinition("Patient");
|
||||
BaseRuntimeChildDefinition patientCommunicationValueChild = patientDefinition.getChildByName("communication");
|
||||
BaseRuntimeElementCompositeDefinition<?> patientCommunicationDefinition = (BaseRuntimeElementCompositeDefinition<?>) patientCommunicationValueChild.getChildByName("communication");
|
||||
BaseRuntimeChildDefinition patientCommunicationLanguageValueChild = patientCommunicationDefinition.getChildByName("language");
|
||||
|
||||
List<IBase> values = patientCommunicationLanguageValueChild.getAccessor().getValues(theValue);
|
||||
for (IBase next : values) {
|
||||
addToken_CodeableConcept(theParams, theSearchParam, next);
|
||||
}
|
||||
}
|
||||
|
||||
private void addToken_CapabilityStatementRestSecurity(Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> capabilityStatementDefinition = getContext().getResourceDefinition("CapabilityStatement");
|
||||
BaseRuntimeChildDefinition capabilityStatementRestChild = capabilityStatementDefinition.getChildByName("rest");
|
||||
BaseRuntimeElementCompositeDefinition<?> capabilityStatementRestDefinition = (BaseRuntimeElementCompositeDefinition<?>) capabilityStatementRestChild.getChildByName("rest");
|
||||
BaseRuntimeChildDefinition capabilityStatementRestSecurityValueChild = capabilityStatementRestDefinition.getChildByName("security");
|
||||
BaseRuntimeElementCompositeDefinition<?> capabilityStatementRestSecurityDefinition = (BaseRuntimeElementCompositeDefinition<?>) capabilityStatementRestSecurityValueChild.getChildByName("security");
|
||||
BaseRuntimeChildDefinition capabilityStatementRestSecurityServiceValueChild = capabilityStatementRestSecurityDefinition.getChildByName("service");
|
||||
|
||||
List<IBase> values = capabilityStatementRestSecurityServiceValueChild.getAccessor().getValues(theValue);
|
||||
for (IBase nextValue : values) {
|
||||
addToken_CodeableConcept(theParams, theSearchParam, nextValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addDate_Period(Set<ResourceIndexedSearchParamDate> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> periodDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Period");
|
||||
BaseRuntimeChildDefinition periodStartValueChild = periodDefinition.getChildByName("start");
|
||||
BaseRuntimeChildDefinition periodEndValueChild = periodDefinition.getChildByName("end");
|
||||
|
||||
Date start = extractValueAsDate(periodStartValueChild, theValue);
|
||||
String startAsString = extractValueAsString(periodStartValueChild, theValue);
|
||||
Date end = extractValueAsDate(periodEndValueChild, theValue);
|
||||
|
||||
if (start != null || end != null) {
|
||||
ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(theSearchParam.getName(), start, end, startAsString);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void addDate_Timing(Set<ResourceIndexedSearchParamDate> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> timingDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Timing");
|
||||
BaseRuntimeChildDefinition timingEventValueChild = timingDefinition.getChildByName("event");
|
||||
BaseRuntimeChildDefinition timingRepeatValueChild = timingDefinition.getChildByName("repeat");
|
||||
BaseRuntimeElementCompositeDefinition<?> timingRepeatDefinition = (BaseRuntimeElementCompositeDefinition<?>) timingRepeatValueChild.getChildByName("repeat");
|
||||
BaseRuntimeChildDefinition timingRepeatBoundsValueChild = timingRepeatDefinition.getChildByName("bounds");
|
||||
|
||||
List<IPrimitiveType<Date>> values = extractValuesAsFhirDates(timingEventValueChild, theValue);
|
||||
|
||||
TreeSet<Date> dates = new TreeSet<>();
|
||||
String firstValue = null;
|
||||
for (IPrimitiveType<Date> nextEvent : values) {
|
||||
if (nextEvent.getValue() != null) {
|
||||
dates.add(nextEvent.getValue());
|
||||
if (firstValue == null) {
|
||||
firstValue = nextEvent.getValueAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Optional<IBase> repeat = timingRepeatValueChild.getAccessor().getFirstValueOrNull(theValue);
|
||||
if (repeat.isPresent()) {
|
||||
Optional<IBase> bounds = timingRepeatBoundsValueChild.getAccessor().getFirstValueOrNull(repeat.get());
|
||||
if (bounds.isPresent()) {
|
||||
String boundsType = toTypeName(bounds.get());
|
||||
switch (boundsType) {
|
||||
case "Period":
|
||||
BaseRuntimeElementCompositeDefinition<?> periodDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Period");
|
||||
BaseRuntimeChildDefinition periodStartValueChild = periodDefinition.getChildByName("start");
|
||||
BaseRuntimeChildDefinition periodEndValueChild = periodDefinition.getChildByName("end");
|
||||
Date start = extractValueAsDate(periodStartValueChild, bounds.get());
|
||||
Date end = extractValueAsDate(periodEndValueChild, bounds.get());
|
||||
dates.add(start);
|
||||
dates.add(end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dates.isEmpty()) {
|
||||
ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(theSearchParam.getName(), dates.first(), dates.last(), firstValue);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void addNumber_Duration(Set<ResourceIndexedSearchParamNumber> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> durationDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Duration");
|
||||
BaseRuntimeChildDefinition durationSystemValueChild = durationDefinition.getChildByName("system");
|
||||
BaseRuntimeChildDefinition durationCodeValueChild = durationDefinition.getChildByName("code");
|
||||
BaseRuntimeChildDefinition durationValueValueChild = durationDefinition.getChildByName("value");
|
||||
|
||||
String system = extractValueAsString(durationSystemValueChild, theValue);
|
||||
String code = extractValueAsString(durationCodeValueChild, theValue);
|
||||
BigDecimal value = extractValueAsBigDecimal(durationValueValueChild, theValue);
|
||||
if (value != null) {
|
||||
|
||||
if (SearchParamConstants.UCUM_NS.equals(system)) {
|
||||
if (isNotBlank(code)) {
|
||||
Unit<? extends Quantity> unit = Unit.valueOf(code);
|
||||
javax.measure.converter.UnitConverter dayConverter = unit.getConverterTo(NonSI.DAY);
|
||||
double dayValue = dayConverter.convert(value.doubleValue());
|
||||
value = new BigDecimal(dayValue);
|
||||
}
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(theSearchParam.getName(), value);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void addString_HumanName(Set<ResourceIndexedSearchParamString> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> humanNameDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("HumanName");
|
||||
BaseRuntimeChildDefinition humanNameFamilyValueChild = humanNameDefinition.getChildByName("family");
|
||||
BaseRuntimeChildDefinition humanNameGivenValueChild = humanNameDefinition.getChildByName("given");
|
||||
|
||||
List<String> families = extractValuesAsStrings(humanNameFamilyValueChild, theValue);
|
||||
for (String next : families) {
|
||||
addSearchTermIfNotBlank(theParams, theSearchParam, next);
|
||||
}
|
||||
|
||||
List<String> givens = extractValuesAsStrings(humanNameGivenValueChild, theValue);
|
||||
for (String next : givens) {
|
||||
addSearchTermIfNotBlank(theParams, theSearchParam, next);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addString_Quantity(Set<ResourceIndexedSearchParamString> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> quantityDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Quantity");
|
||||
BaseRuntimeChildDefinition quantityValueChild = quantityDefinition.getChildByName("value");
|
||||
|
||||
BigDecimal value = extractValueAsBigDecimal(quantityValueChild, theValue);
|
||||
if (value != null) {
|
||||
addSearchTermIfNotBlank(theParams, theSearchParam, value.toPlainString());
|
||||
}
|
||||
}
|
||||
|
||||
private void addString_Range(Set<ResourceIndexedSearchParamString> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> rangeDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Range");
|
||||
BaseRuntimeChildDefinition rangeLowValueChild = rangeDefinition.getChildByName("low");
|
||||
|
||||
BigDecimal value = extractValueAsBigDecimal(rangeLowValueChild, theValue);
|
||||
if (value != null) {
|
||||
addSearchTermIfNotBlank(theParams, theSearchParam, value.toPlainString());
|
||||
}
|
||||
}
|
||||
|
||||
private void addString_ContactPoint(Set<ResourceIndexedSearchParamString> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> contactPointDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("ContactPoint");
|
||||
BaseRuntimeChildDefinition contactPointValueValueChild = contactPointDefinition.getChildByName("value");
|
||||
|
||||
String value = extractValueAsString(contactPointValueValueChild, theValue);
|
||||
if (isNotBlank(value)) {
|
||||
addSearchTermIfNotBlank(theParams, theSearchParam, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void addString_Address(Set<ResourceIndexedSearchParamString> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> addressDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Address");
|
||||
BaseRuntimeChildDefinition addressLineValueChild = addressDefinition.getChildByName("line");
|
||||
BaseRuntimeChildDefinition addressCityValueChild = addressDefinition.getChildByName("city");
|
||||
BaseRuntimeChildDefinition addressStateValueChild = addressDefinition.getChildByName("state");
|
||||
BaseRuntimeChildDefinition addressCountryValueChild = addressDefinition.getChildByName("country");
|
||||
BaseRuntimeChildDefinition addressPostalCodeValueChild = addressDefinition.getChildByName("postalCode");
|
||||
|
||||
List<String> allNames = new ArrayList<>(extractValuesAsStrings(addressLineValueChild, theValue));
|
||||
|
||||
String city = extractValueAsString(addressCityValueChild, theValue);
|
||||
if (isNotBlank(city)) {
|
||||
allNames.add(city);
|
||||
}
|
||||
|
||||
String state = extractValueAsString(addressStateValueChild, theValue);
|
||||
if (isNotBlank(state)) {
|
||||
allNames.add(state);
|
||||
}
|
||||
|
||||
String country = extractValueAsString(addressCountryValueChild, theValue);
|
||||
if (isNotBlank(country)) {
|
||||
allNames.add(country);
|
||||
}
|
||||
|
||||
String postalCode = extractValueAsString(addressPostalCodeValueChild, theValue);
|
||||
if (isNotBlank(postalCode)) {
|
||||
allNames.add(postalCode);
|
||||
}
|
||||
|
||||
for (String nextName : allNames) {
|
||||
addSearchTermIfNotBlank(theParams, theSearchParam, nextName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addNumber_Quantity(Set<ResourceIndexedSearchParamNumber> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> quantityDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Duration");
|
||||
BaseRuntimeChildDefinition quantityValueValueChild = quantityDefinition.getChildByName("value");
|
||||
|
||||
BigDecimal value = extractValueAsBigDecimal(quantityValueValueChild, theValue);
|
||||
if (value != null) {
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(theSearchParam.getName(), value);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addNumber_Integer(Set<ResourceIndexedSearchParamNumber> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
IPrimitiveType<Integer> value = (IPrimitiveType<Integer>) theValue;
|
||||
if (value.getValue() != null) {
|
||||
BigDecimal valueDecimal = new BigDecimal(value.getValue());
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(theSearchParam.getName(), valueDecimal);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addNumber_Decimal(Set<ResourceIndexedSearchParamNumber> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
IPrimitiveType<BigDecimal> value = (IPrimitiveType<BigDecimal>) theValue;
|
||||
if (value.getValue() != null) {
|
||||
BigDecimal valueDecimal = value.getValue();
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(theSearchParam.getName(), valueDecimal);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||
// TODO: implement
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
||||
IExtractor<ResourceIndexedSearchParamDate> extractor = (params, searchParam, value, path) -> {
|
||||
String nextType = toTypeName(value);
|
||||
switch (nextType) {
|
||||
case "date":
|
||||
case "dateTime":
|
||||
case "instant":
|
||||
addDateTimeTypes(params, searchParam, value);
|
||||
break;
|
||||
case "Period":
|
||||
addDate_Period(params, searchParam, value);
|
||||
break;
|
||||
case "Timing":
|
||||
addDate_Timing(params, searchParam, value);
|
||||
break;
|
||||
case "string":
|
||||
// CarePlan.activitydate can be a string - ignored for now
|
||||
break;
|
||||
default:
|
||||
throw new ConfigurationException("Search param " + searchParam.getName() + " is of unexpected datatype: " + value.getClass());
|
||||
}
|
||||
};
|
||||
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.DATE);
|
||||
}
|
||||
|
||||
private <T extends BaseResourceIndexedSearchParam> Set<T> extractSearchParams(IBaseResource theResource, IExtractor<T> theExtractor, RestSearchParameterTypeEnum theSearchParamType) {
|
||||
Set<T> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != theSearchParamType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (IBase nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject != null) {
|
||||
theExtractor.extract(retVal, nextSpDef, nextObject, nextPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private String toTypeName(IBase nextObject) {
|
||||
return getContext().getElementDefinition(nextObject.getClass()).getName();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addDateTimeTypes(Set<ResourceIndexedSearchParamDate> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
IPrimitiveType<Date> nextBaseDateTime = (IPrimitiveType<Date>) theValue;
|
||||
if (nextBaseDateTime.getValue() != null) {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(theSearchParam.getName(), nextBaseDateTime.getValue(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString());
|
||||
theParams.add(param);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
|
||||
IExtractor<ResourceIndexedSearchParamNumber> extractor = (params, searchParam, value, path) -> {
|
||||
String nextType = toTypeName(value);
|
||||
switch (nextType) {
|
||||
case "Duration":
|
||||
addNumber_Duration(params, searchParam, value);
|
||||
break;
|
||||
case "Quantity":
|
||||
addNumber_Quantity(params, searchParam, value);
|
||||
break;
|
||||
case "integer":
|
||||
addNumber_Integer(params, searchParam, value);
|
||||
break;
|
||||
case "decimal":
|
||||
addNumber_Decimal(params, searchParam, value);
|
||||
break;
|
||||
default:
|
||||
throw new ConfigurationException("Search param " + searchParam.getName() + " is of unexpected datatype: " + value.getClass());
|
||||
}
|
||||
};
|
||||
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.NUMBER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IBaseResource theResource) {
|
||||
IExtractor<ResourceIndexedSearchParamQuantity> extractor = (params, searchParam, value, path) -> {
|
||||
String nextType = toTypeName(value);
|
||||
switch (nextType) {
|
||||
case "Quantity":
|
||||
addQuantity_Quantity(theEntity, params, searchParam, value);
|
||||
break;
|
||||
case "Money":
|
||||
addQuantity_Money(theEntity, params, searchParam, value);
|
||||
break;
|
||||
case "Range":
|
||||
addQuantity_Range(theEntity, params, searchParam, value);
|
||||
break;
|
||||
default:
|
||||
throw new ConfigurationException("Search param " + searchParam.getName() + " is of unexpected datatype: " + value.getClass());
|
||||
}
|
||||
};
|
||||
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IBaseResource theResource) {
|
||||
IExtractor<ResourceIndexedSearchParamString> extractor = (params, searchParam, value, path) -> {
|
||||
if (value instanceof IPrimitiveType) {
|
||||
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) value;
|
||||
String valueAsString = nextValue.getValueAsString();
|
||||
addSearchTermIfNotBlank(params, searchParam, valueAsString);
|
||||
return;
|
||||
}
|
||||
|
||||
String nextType = toTypeName(value);
|
||||
switch (nextType) {
|
||||
case "HumanName":
|
||||
addString_HumanName(params, searchParam, value);
|
||||
break;
|
||||
case "Address":
|
||||
addString_Address(params, searchParam, value);
|
||||
break;
|
||||
case "ContactPoint":
|
||||
addString_ContactPoint(params, searchParam, value);
|
||||
break;
|
||||
case "Quantity":
|
||||
addString_Quantity(params, searchParam, value);
|
||||
break;
|
||||
case "Range":
|
||||
addString_Range(params, searchParam, value);
|
||||
break;
|
||||
default:
|
||||
throw new ConfigurationException("Search param " + searchParam.getName() + " is of unexpected datatype: " + value.getClass());
|
||||
}
|
||||
};
|
||||
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.STRING);
|
||||
}
|
||||
|
||||
private void addUri_Uri(Set<ResourceIndexedSearchParamUri> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
IPrimitiveType<?> value = (IPrimitiveType<?>) theValue;
|
||||
String valueAsString = value.getValueAsString();
|
||||
if (isNotBlank(valueAsString)) {
|
||||
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(theSearchParam.getName(), valueAsString);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
|
||||
BaseRuntimeElementCompositeDefinition<?> codeSystemDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("CodeSystem");
|
||||
assert codeSystemDefinition != null;
|
||||
BaseRuntimeChildDefinition codeSystemUrlValueChild = codeSystemDefinition.getChildByName("url");
|
||||
|
||||
String resourceTypeName = toTypeName(theResource);
|
||||
String useSystem;
|
||||
if (resourceTypeName.equals("CodeSystem")) {
|
||||
useSystem = extractValueAsString(codeSystemUrlValueChild, theResource);
|
||||
} else {
|
||||
useSystem = null;
|
||||
}
|
||||
|
||||
IExtractor<BaseResourceIndexedSearchParam> extractor = (params, searchParam, value, path) -> {
|
||||
|
||||
if (value instanceof IBaseEnumeration<?>) {
|
||||
IBaseEnumeration<?> obj = (IBaseEnumeration<?>) value;
|
||||
String system = extractSystem(obj);
|
||||
String code = obj.getValueAsString();
|
||||
addTokenIfNotBlank(params, searchParam, system, code);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof IPrimitiveType) {
|
||||
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) value;
|
||||
String systemAsString = null;
|
||||
String valueAsString = nextValue.getValueAsString();
|
||||
if ("CodeSystem.concept.code".equals(path)) {
|
||||
systemAsString = useSystem;
|
||||
}
|
||||
|
||||
addTokenIfNotBlank(params, searchParam, systemAsString, valueAsString);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (path) {
|
||||
case "Patient.communication":
|
||||
addToken_PatientCommunication(params, searchParam, value);
|
||||
return;
|
||||
case "Consent.source":
|
||||
// Consent#source-identifier has a path that isn't typed - This is a one-off to deal with that
|
||||
return;
|
||||
case "Location.position":
|
||||
ourLog.warn("Position search not currently supported, not indexing location");
|
||||
return;
|
||||
case "StructureDefinition.context":
|
||||
// TODO: implement this
|
||||
ourLog.warn("StructureDefinition context indexing not currently supported");
|
||||
return;
|
||||
case "CapabilityStatement.rest.security":
|
||||
addToken_CapabilityStatementRestSecurity(params, searchParam, value);
|
||||
return;
|
||||
}
|
||||
|
||||
String nextType = toTypeName(value);
|
||||
switch (nextType) {
|
||||
case "Identifier":
|
||||
addToken_Identifier(params, searchParam, value);
|
||||
break;
|
||||
case "CodeableConcept":
|
||||
addToken_CodeableConcept(params, searchParam, value);
|
||||
break;
|
||||
case "Coding":
|
||||
addToken_Coding(params, searchParam, value);
|
||||
break;
|
||||
case "ContactPoint":
|
||||
addToken_ContactPoint(params, searchParam, value);
|
||||
break;
|
||||
default:
|
||||
throw new ConfigurationException("Search param " + searchParam.getName() + " is of unexpected datatype: " + value.getClass());
|
||||
}
|
||||
};
|
||||
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.TOKEN);
|
||||
}
|
||||
|
||||
private void addTokenIfNotBlank(Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, String theSystem, String theValue) {
|
||||
String system = theSystem;
|
||||
String value = theValue;
|
||||
if (isNotBlank(system) || isNotBlank(value)) {
|
||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
system = system.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
if (value != null && value.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
value = value.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamToken nextEntity;
|
||||
nextEntity = new ResourceIndexedSearchParamToken(theSearchParam.getName(), system, value);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IBaseResource theResource) {
|
||||
IExtractor<ResourceIndexedSearchParamUri> extractor = (params, searchParam, value, path) -> {
|
||||
String nextType = toTypeName(value);
|
||||
switch (nextType) {
|
||||
case "uri":
|
||||
case "url":
|
||||
addUri_Uri(params, searchParam, value);
|
||||
break;
|
||||
default:
|
||||
throw new ConfigurationException("Search param " + searchParam.getName() + " is of unexpected datatype: " + value.getClass());
|
||||
}
|
||||
};
|
||||
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.URI);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
|
||||
private void addSearchTermIfNotBlank(Set<? extends BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, String theValue) {
|
||||
if (isNotBlank(theValue)) {
|
||||
if (theValue.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
theValue = theValue.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
|
||||
String searchParamName = theSearchParam.getName();
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), searchParamName, StringNormalizer.normalizeString(theValue), theValue);
|
||||
|
||||
Set params = theParams;
|
||||
params.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void addStringParam(ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> retVal, RuntimeSearchParam nextSpDef, String value) {
|
||||
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), nextSpDef.getName(), StringNormalizer.normalizeString(value), value);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
protected String[] split(String thePaths) {
|
||||
if (getContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
|
||||
if (!thePaths.contains("|")) {
|
||||
return new String[]{thePaths};
|
||||
}
|
||||
return ASPLIT_R4.split(thePaths);
|
||||
} else {
|
||||
if (!thePaths.contains("|") && !thePaths.contains(" or ")) {
|
||||
return new String[]{thePaths};
|
||||
}
|
||||
return ASPLIT.split(thePaths);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
||||
ArrayList<PathAndRef> retVal = new ArrayList<>();
|
||||
|
||||
String[] nextPathsSplit = split(theNextSpDef.getPath());
|
||||
for (String path : nextPathsSplit) {
|
||||
path = path.trim();
|
||||
if (isNotBlank(path)) {
|
||||
|
||||
for (Object next : extractValues(path, theResource)) {
|
||||
retVal.add(new PathAndRef(path, next));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
public interface IValueExtractor {
|
||||
|
||||
List<? extends IBase> get() throws FHIRException;
|
||||
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface IExtractor<T> {
|
||||
|
||||
|
||||
void extract(Set<T> theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath);
|
||||
|
||||
}
|
||||
|
||||
private static void addIgnoredType(FhirContext theCtx, String theType, Set<Class<?>> theIgnoredTypes) {
|
||||
BaseRuntimeElementDefinition<?> elementDefinition = theCtx.getElementDefinition(theType);
|
||||
if (elementDefinition != null) {
|
||||
|
@ -148,5 +823,62 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
}
|
||||
|
||||
private static String extractValueAsString(BaseRuntimeChildDefinition theChildDefinition, IBase theElement) {
|
||||
return theChildDefinition
|
||||
.getAccessor()
|
||||
.<IPrimitiveType<?>>getFirstValueOrNull(theElement)
|
||||
.map(t -> t.getValueAsString())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static Date extractValueAsDate(BaseRuntimeChildDefinition theChildDefinition, IBase theElement) {
|
||||
return theChildDefinition
|
||||
.getAccessor()
|
||||
.<IPrimitiveType<Date>>getFirstValueOrNull(theElement)
|
||||
.map(t -> t.getValue())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static Integer extractValueAsInteger(BaseRuntimeChildDefinition theChildDefinition, IBase theElement) {
|
||||
return theChildDefinition
|
||||
.getAccessor()
|
||||
.<IPrimitiveType<Integer>>getFirstValueOrNull(theElement)
|
||||
.map(t -> t.getValue())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static BigDecimal extractValueAsBigDecimal(BaseRuntimeChildDefinition theChildDefinition, IBase theElement) {
|
||||
return theChildDefinition
|
||||
.getAccessor()
|
||||
.<IPrimitiveType<BigDecimal>>getFirstValueOrNull(theElement)
|
||||
.map(t -> t.getValue())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<IPrimitiveType<Date>> extractValuesAsFhirDates(BaseRuntimeChildDefinition theChildDefinition, IBase theElement) {
|
||||
return (List) theChildDefinition
|
||||
.getAccessor()
|
||||
.getValues(theElement);
|
||||
}
|
||||
|
||||
private static List<String> extractValuesAsStrings(BaseRuntimeChildDefinition theChildDefinition, IBase theValue) {
|
||||
return theChildDefinition
|
||||
.getAccessor()
|
||||
.getValues(theValue)
|
||||
.stream()
|
||||
.map(t -> (IPrimitiveType) t)
|
||||
.map(t -> t.getValueAsString())
|
||||
.filter(t -> isNotBlank(t))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static <T extends Enum<?>> String extractSystem(IBaseEnumeration<T> theBoundCode) {
|
||||
if (theBoundCode.getValue() != null) {
|
||||
return theBoundCode.getEnumFactory().toSystem(theBoundCode.getValue());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -20,656 +20,40 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
import ca.uhn.fhir.model.api.IDatatype;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
||||
import ca.uhn.fhir.model.base.composite.BaseHumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.*;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestSecurity;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Location;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient.Communication;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
|
||||
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.RestfulSecurityServiceEnum;
|
||||
import ca.uhn.fhir.model.primitive.*;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.measure.quantity.Quantity;
|
||||
import javax.measure.unit.NonSI;
|
||||
import javax.measure.unit.Unit;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorDstu2.class);
|
||||
|
||||
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
|
||||
if (isBlank(searchTerm)) {
|
||||
return;
|
||||
}
|
||||
if (searchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), resourceName, StringNormalizer.normalizeString(searchTerm), searchTerm);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
private void addStringParam(ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> retVal, RuntimeSearchParam nextSpDef, String value) {
|
||||
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), nextSpDef.getName(), StringNormalizer.normalizeString(value), value);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||
// TODO: implement
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable,
|
||||
* ca.uhn.fhir.model.api.IResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.DATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean multiType = false;
|
||||
if (nextPath.endsWith("[x]")) {
|
||||
multiType = true;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamDate nextEntity;
|
||||
if (nextObject instanceof BaseDateTimeDt) {
|
||||
BaseDateTimeDt nextValue = (BaseDateTimeDt) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue(), nextValue.getValueAsString());
|
||||
} else if (nextObject instanceof PeriodDt) {
|
||||
PeriodDt nextValue = (PeriodDt) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd(), nextValue.getStartElement().getValueAsString());
|
||||
} else {
|
||||
if (!multiType) {
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (nextEntity != null) {
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable,
|
||||
* ca.uhn.fhir.model.api.IResource)
|
||||
*/
|
||||
@Override
|
||||
public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<ResourceIndexedSearchParamNumber>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.NUMBER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
boolean multiType = false;
|
||||
if (nextPath.endsWith("[x]")) {
|
||||
multiType = true;
|
||||
}
|
||||
|
||||
if (nextObject instanceof DurationDt) {
|
||||
DurationDt nextValue = (DurationDt) nextObject;
|
||||
if (nextValue.getValueElement().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (new UriDt(SearchParamConstants.UCUM_NS).equals(nextValue.getSystemElement())) {
|
||||
if (isNotBlank(nextValue.getCode())) {
|
||||
|
||||
Unit<? extends Quantity> unit = Unit.valueOf(nextValue.getCode());
|
||||
javax.measure.converter.UnitConverter dayConverter = unit.getConverterTo(NonSI.DAY);
|
||||
double dayValue = dayConverter.convert(nextValue.getValue().doubleValue());
|
||||
DurationDt newValue = new DurationDt();
|
||||
newValue.setSystem(SearchParamConstants.UCUM_NS);
|
||||
newValue.setCode(NonSI.DAY.toString());
|
||||
newValue.setValue(dayValue);
|
||||
nextValue = newValue;
|
||||
|
||||
/*
|
||||
* @SuppressWarnings("unchecked") PhysicsUnit<? extends
|
||||
* org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends
|
||||
* org.unitsofmeasurement.quantity.Quantity<?>>)
|
||||
* UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if
|
||||
* (unit.isCompatible(UCUM.DAY)) {
|
||||
*
|
||||
* @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit =
|
||||
* (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY); double
|
||||
* dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); DurationDt newValue =
|
||||
* new DurationDt(); newValue.setSystem(UCUM_NS); newValue.setCode(UCUM.DAY.getSymbol());
|
||||
* newValue.setValue(dayValue); nextValue=newValue; }
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof QuantityDt) {
|
||||
QuantityDt nextValue = (QuantityDt) nextObject;
|
||||
if (nextValue.getValueElement().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof IntegerDt) {
|
||||
IntegerDt nextValue = (IntegerDt) nextObject;
|
||||
if (nextValue.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, new BigDecimal(nextValue.getValue()));
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof DecimalDt) {
|
||||
DecimalDt nextValue = (DecimalDt) nextObject;
|
||||
if (nextValue.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
if (!multiType) {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable,
|
||||
* ca.uhn.fhir.model.api.IResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamQuantity> retVal = new HashSet<ResourceIndexedSearchParamQuantity>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.QUANTITY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
boolean multiType = false;
|
||||
if (nextPath.endsWith("[x]")) {
|
||||
multiType = true;
|
||||
}
|
||||
|
||||
if (nextObject instanceof QuantityDt) {
|
||||
QuantityDt nextValue = (QuantityDt) nextObject;
|
||||
if (nextValue.getValueElement().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValue.getValueElement().getValue(), nextValue.getSystemElement().getValueAsString(), nextValue.getCode());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
if (!multiType) {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable,
|
||||
* ca.uhn.fhir.model.api.IResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamString> retVal = new HashSet<ResourceIndexedSearchParamString>();
|
||||
|
||||
String resourceName = getContext().getResourceDefinition(theResource).getName();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.STRING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
String nextSpName = nextSpDef.getName();
|
||||
|
||||
if (isBlank(nextPath)) {
|
||||
|
||||
// TODO: implement phonetic, and any others that have no path
|
||||
|
||||
if ("Questionnaire".equals(resourceName) && nextSpDef.getName().equals("title")) {
|
||||
Questionnaire q = (Questionnaire) theResource;
|
||||
String title = q.getGroup().getTitle();
|
||||
addSearchTerm(theEntity, retVal, nextSpName, title);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean multiType = false;
|
||||
if (nextPath.endsWith("[x]")) {
|
||||
multiType = true;
|
||||
}
|
||||
|
||||
if (nextObject instanceof IPrimitiveDatatype<?>) {
|
||||
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
|
||||
String searchTerm = nextValue.getValueAsString();
|
||||
addSearchTerm(theEntity, retVal, nextSpName, searchTerm);
|
||||
} else {
|
||||
if (nextObject instanceof BaseHumanNameDt) {
|
||||
ArrayList<StringDt> allNames = new ArrayList<StringDt>();
|
||||
HumanNameDt nextHumanName = (HumanNameDt) nextObject;
|
||||
allNames.addAll(nextHumanName.getFamily());
|
||||
allNames.addAll(nextHumanName.getGiven());
|
||||
for (StringDt nextName : allNames) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof AddressDt) {
|
||||
ArrayList<StringDt> allNames = new ArrayList<StringDt>();
|
||||
AddressDt nextAddress = (AddressDt) nextObject;
|
||||
allNames.addAll(nextAddress.getLine());
|
||||
allNames.add(nextAddress.getCityElement());
|
||||
allNames.add(nextAddress.getStateElement());
|
||||
allNames.add(nextAddress.getCountryElement());
|
||||
allNames.add(nextAddress.getPostalCodeElement());
|
||||
for (StringDt nextName : allNames) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof ContactPointDt) {
|
||||
ContactPointDt nextContact = (ContactPointDt) nextObject;
|
||||
if (nextContact.getValueElement().isEmpty() == false) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextContact.getValue());
|
||||
protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String thePaths) {
|
||||
return () -> {
|
||||
List<IBase> values = new ArrayList<>();
|
||||
String[] nextPathsSplit = split(thePaths);
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
String nextPathTrimmed = nextPath.trim();
|
||||
List<IBase> allValues = t.getValues(theResource, nextPathTrimmed);
|
||||
for (IBase next : allValues) {
|
||||
if (next instanceof IBaseExtension) {
|
||||
IBaseDatatype value = ((IBaseExtension) next).getValue();
|
||||
if (value != null) {
|
||||
values.add(value);
|
||||
}
|
||||
} else {
|
||||
if (!multiType) {
|
||||
throw new ConfigurationException("Search param " + nextSpName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
values.add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable,
|
||||
* ca.uhn.fhir.model.api.IResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<BaseResourceIndexedSearchParam>();
|
||||
|
||||
String useSystem = null;
|
||||
if (theResource instanceof ValueSet) {
|
||||
ValueSet vs = (ValueSet) theResource;
|
||||
useSystem = vs.getCodeSystem().getSystem();
|
||||
}
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.TOKEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean multiType = false;
|
||||
if (nextPath.endsWith("[x]")) {
|
||||
multiType = true;
|
||||
}
|
||||
|
||||
List<String> systems = new ArrayList<String>();
|
||||
List<String> codes = new ArrayList<String>();
|
||||
|
||||
String needContactPointSystem = null;
|
||||
if (nextPath.endsWith("(system=phone)")) {
|
||||
nextPath = nextPath.substring(0, nextPath.length() - "(system=phone)".length());
|
||||
needContactPointSystem = "phone";
|
||||
}
|
||||
if (nextPath.endsWith("(system=email)")) {
|
||||
nextPath = nextPath.substring(0, nextPath.length() - "(system=email)".length());
|
||||
needContactPointSystem = "email";
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
|
||||
// Patient:language
|
||||
if (nextObject instanceof Patient.Communication) {
|
||||
Communication nextValue = (Patient.Communication) nextObject;
|
||||
nextObject = nextValue.getLanguage();
|
||||
}
|
||||
|
||||
if (nextObject instanceof IdentifierDt) {
|
||||
IdentifierDt nextValue = (IdentifierDt) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String system = StringUtils.defaultIfBlank(nextValue.getSystemElement().getValueAsString(), null);
|
||||
String value = nextValue.getValueElement().getValue();
|
||||
if (isNotBlank(value)) {
|
||||
systems.add(system);
|
||||
codes.add(value);
|
||||
}
|
||||
|
||||
if (isNotBlank(nextValue.getType().getText())) {
|
||||
addStringParam(theEntity, retVal, nextSpDef, nextValue.getType().getText());
|
||||
}
|
||||
|
||||
} else if (nextObject instanceof ContactPointDt) {
|
||||
ContactPointDt nextValue = (ContactPointDt) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (isNotBlank(needContactPointSystem)) {
|
||||
if (!needContactPointSystem.equals(nextValue.getSystemElement().getValueAsString())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
systems.add(nextValue.getSystemElement().getValueAsString());
|
||||
codes.add(nextValue.getValueElement().getValue());
|
||||
} else if (nextObject instanceof BoundCodeDt) {
|
||||
BoundCodeDt<?> obj = (BoundCodeDt<?>) nextObject;
|
||||
String system = extractSystem(obj);
|
||||
String code = obj.getValue();
|
||||
if (isNotBlank(code)) {
|
||||
systems.add(system);
|
||||
codes.add(code);
|
||||
}
|
||||
} else if (nextObject instanceof IPrimitiveDatatype<?>) {
|
||||
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if ("ValueSet.codeSystem.concept.code".equals(nextPath)) {
|
||||
systems.add(useSystem);
|
||||
} else {
|
||||
systems.add(null);
|
||||
}
|
||||
codes.add(nextValue.getValueAsString());
|
||||
} else if (nextObject instanceof CodingDt) {
|
||||
CodingDt nextValue = (CodingDt) nextObject;
|
||||
extractTokensFromCoding(systems, codes, theEntity, retVal, nextSpDef, nextValue);
|
||||
} else if (nextObject instanceof CodeableConceptDt) {
|
||||
CodeableConceptDt nextCC = (CodeableConceptDt) nextObject;
|
||||
if (!nextCC.getTextElement().isEmpty()) {
|
||||
addStringParam(theEntity, retVal, nextSpDef, nextCC.getTextElement().getValue());
|
||||
}
|
||||
|
||||
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
|
||||
} else if (nextObject instanceof RestSecurity) {
|
||||
// Conformance.security search param points to something kind of useless right now - This should probably
|
||||
// be fixed.
|
||||
RestSecurity sec = (RestSecurity) nextObject;
|
||||
for (BoundCodeableConceptDt<RestfulSecurityServiceEnum> nextCC : sec.getService()) {
|
||||
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
|
||||
}
|
||||
} else if (nextObject instanceof Location.Position) {
|
||||
ourLog.warn("Position search not currently supported, not indexing location");
|
||||
continue;
|
||||
} else {
|
||||
if (!multiType) {
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert systems.size() == codes.size() : "Systems contains " + systems + ", codes contains: " + codes;
|
||||
|
||||
Set<Pair<String, String>> haveValues = new HashSet<Pair<String, String>>();
|
||||
for (int i = 0; i < systems.size(); i++) {
|
||||
String system = systems.get(i);
|
||||
String code = codes.get(i);
|
||||
if (isBlank(system) && isBlank(code)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
system = system.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
code = code.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
|
||||
Pair<String, String> nextPair = Pair.of(system, code);
|
||||
if (haveValues.contains(nextPair)) {
|
||||
continue;
|
||||
}
|
||||
haveValues.add(nextPair);
|
||||
|
||||
ResourceIndexedSearchParamToken nextEntity;
|
||||
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), system, code);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamUri> retVal = new HashSet<ResourceIndexedSearchParamUri>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.URI) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
boolean multiType = false;
|
||||
if (nextPath.endsWith("[x]")) {
|
||||
multiType = true;
|
||||
}
|
||||
|
||||
if (nextObject instanceof UriDt) {
|
||||
UriDt nextValue = (UriDt) nextObject;
|
||||
if (isBlank(nextValue.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ourLog.trace("Adding param: {}, {}", resourceName, nextValue.getValue());
|
||||
|
||||
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue());
|
||||
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
if (!multiType) {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConceptDt theCodeableConcept, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) {
|
||||
for (CodingDt nextCoding : theCodeableConcept.getCoding()) {
|
||||
extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding);
|
||||
}
|
||||
}
|
||||
|
||||
private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef, CodingDt nextCoding) {
|
||||
if (nextCoding != null && !nextCoding.isEmpty()) {
|
||||
|
||||
String nextSystem = nextCoding.getSystemElement().getValueAsString();
|
||||
String nextCode = nextCoding.getCodeElement().getValue();
|
||||
if (isNotBlank(nextSystem) || isNotBlank(nextCode)) {
|
||||
theSystems.add(nextSystem);
|
||||
theCodes.add(nextCode);
|
||||
}
|
||||
|
||||
if (!nextCoding.getDisplayElement().isEmpty()) {
|
||||
addStringParam(theEntity, theListToPopulate, theParameterDef, nextCoding.getDisplayElement().getValue());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
|
||||
List<Object> values = new ArrayList<>();
|
||||
String[] nextPathsSplit = SPLIT.split(thePaths);
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
String nextPathTrimmed = nextPath.trim();
|
||||
List<Object> allValues;
|
||||
try {
|
||||
allValues = t.getValues(theResource, nextPathTrimmed);
|
||||
} catch (Exception e) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
|
||||
throw new InternalErrorException(msg, e);
|
||||
}
|
||||
for (Object next : allValues) {
|
||||
if (next instanceof IBaseExtension) {
|
||||
IBaseDatatype value = ((IBaseExtension) next).getValue();
|
||||
if (value != null) {
|
||||
values.add(value);
|
||||
}
|
||||
} else {
|
||||
values.add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
private static <T extends Enum<?>> String extractSystem(BoundCodeDt<T> theBoundCode) {
|
||||
if (theBoundCode.getValueAsEnum() != null) {
|
||||
IValueSetEnumBinder<T> binder = theBoundCode.getBinder();
|
||||
return binder.toSystemString(theBoundCode.getValueAsEnum());
|
||||
}
|
||||
return null;
|
||||
return values;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,48 +20,31 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Enumeration;
|
||||
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestSecurityComponent;
|
||||
import org.hl7.fhir.dstu3.model.Location.LocationPositionComponent;
|
||||
import org.hl7.fhir.dstu3.model.Patient.PatientCommunicationComponent;
|
||||
import org.hl7.fhir.dstu3.model.Base;
|
||||
import org.hl7.fhir.dstu3.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.measure.unit.NonSI;
|
||||
import javax.measure.unit.Unit;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
import static org.apache.commons.lang3.StringUtils.trim;
|
||||
|
||||
public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorDstu3.class);
|
||||
|
||||
@Autowired
|
||||
private org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport myValidationSupport;
|
||||
|
||||
private HapiWorkerContext myWorkerContext;
|
||||
private FHIRPathEngine myFhirPathEngine;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -77,653 +60,27 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
|||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Quantity nextValue) {
|
||||
if (!nextValue.getValueElement().isEmpty()) {
|
||||
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
|
||||
String nextValueString = nextValue.getSystemElement().getValueAsString();
|
||||
String nextValueCode = nextValue.getCode();
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
|
||||
if (isBlank(searchTerm)) {
|
||||
return;
|
||||
}
|
||||
if (searchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), resourceName, StringNormalizer.normalizeString(searchTerm), searchTerm);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
private void addStringParam(ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> retVal, RuntimeSearchParam nextSpDef, String value) {
|
||||
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), nextSpDef.getName(), StringNormalizer.normalizeString(value), value);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
||||
ArrayList<PathAndRef> retVal = new ArrayList<PathAndRef>();
|
||||
|
||||
String[] nextPathsSplit = SPLIT.split(theNextSpDef.getPath());
|
||||
for (String path : nextPathsSplit) {
|
||||
path = path.trim();
|
||||
if (isNotBlank(path)) {
|
||||
for (Object next : extractValues(path, theResource)) {
|
||||
retVal.add(new PathAndRef(path, next));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||
// TODO: implement
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<>();
|
||||
String resourceType = theEntity.getResourceType();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.DATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamDate nextEntity;
|
||||
if (nextObject instanceof BaseDateTimeType) {
|
||||
BaseDateTimeType nextValue = (BaseDateTimeType) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue(), nextValue.getValueAsString());
|
||||
} else if (nextObject instanceof Period) {
|
||||
Period nextValue = (Period) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd(), nextValue.getStartElement().getValueAsString());
|
||||
} else if (nextObject instanceof Timing) {
|
||||
Timing nextValue = (Timing) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String firstValue = null;
|
||||
TreeSet<Date> dates = new TreeSet<>();
|
||||
for (DateTimeType nextEvent : nextValue.getEvent()) {
|
||||
if (nextEvent.getValue() != null) {
|
||||
dates.add(nextEvent.getValue());
|
||||
if (firstValue == null) {
|
||||
firstValue = nextEvent.getValueAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextValue.getRepeat().hasBounds()) {
|
||||
if (nextValue.getRepeat().getBoundsPeriod().getStart() != null) {
|
||||
dates.add(nextValue.getRepeat().getBoundsPeriod().getStart());
|
||||
}
|
||||
if (nextValue.getRepeat().getBoundsPeriod().getEnd() != null) {
|
||||
dates.add(nextValue.getRepeat().getBoundsPeriod().getEnd());
|
||||
}
|
||||
}
|
||||
if (dates.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), dates.first(), dates.last(), firstValue);
|
||||
} else if (nextObject instanceof StringType) {
|
||||
// CarePlan.activitydate can be a string
|
||||
continue;
|
||||
} else if (resourceType.equals("Consent") && nextPath.equals("Consent.source")) {
|
||||
// Consent#source-identifier has a path that isn't typed - This is a one-off to deal with that
|
||||
continue;
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
if (nextEntity != null) {
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.NUMBER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
|
||||
if (nextObject instanceof Duration) {
|
||||
Duration nextValue = (Duration) nextObject;
|
||||
if (nextValue.getValueElement().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SearchParamConstants.UCUM_NS.equals(nextValue.getSystem())) {
|
||||
if (isNotBlank(nextValue.getCode())) {
|
||||
|
||||
Unit<? extends javax.measure.quantity.Quantity> unit = Unit.valueOf(nextValue.getCode());
|
||||
javax.measure.converter.UnitConverter dayConverter = unit.getConverterTo(NonSI.DAY);
|
||||
double dayValue = dayConverter.convert(nextValue.getValue().doubleValue());
|
||||
Duration newValue = new Duration();
|
||||
newValue.setSystem(SearchParamConstants.UCUM_NS);
|
||||
newValue.setCode(NonSI.DAY.toString());
|
||||
newValue.setValue(dayValue);
|
||||
nextValue = newValue;
|
||||
|
||||
/*
|
||||
* @SuppressWarnings("unchecked") PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>>)
|
||||
* UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if (unit.isCompatible(UCUM.DAY)) {
|
||||
*
|
||||
* @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit = (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY);
|
||||
* double dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); Duration newValue = new Duration(); newValue.setSystem(UCUM_NS);
|
||||
* newValue.setCode(UCUM.DAY.getSymbol()); newValue.setValue(dayValue); nextValue=newValue; }
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof Quantity) {
|
||||
Quantity nextValue = (Quantity) nextObject;
|
||||
if (nextValue.getValueElement().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof IntegerType) {
|
||||
IntegerType nextValue = (IntegerType) nextObject;
|
||||
if (nextValue.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, new BigDecimal(nextValue.getValue()));
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof DecimalType) {
|
||||
DecimalType nextValue = (DecimalType) nextObject;
|
||||
if (nextValue.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamQuantity> retVal = new HashSet<ResourceIndexedSearchParamQuantity>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.QUANTITY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
|
||||
if (nextObject instanceof Quantity) {
|
||||
Quantity nextValue = (Quantity) nextObject;
|
||||
addQuantity(theEntity, retVal, resourceName, nextValue);
|
||||
} else if (nextObject instanceof Range) {
|
||||
Range nextValue = (Range) nextObject;
|
||||
addQuantity(theEntity, retVal, resourceName, nextValue.getLow());
|
||||
addQuantity(theEntity, retVal, resourceName, nextValue.getHigh());
|
||||
} else if (nextObject instanceof LocationPositionComponent) {
|
||||
continue;
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamString> retVal = new HashSet<ResourceIndexedSearchParamString>();
|
||||
|
||||
String resourceName = getContext().getResourceDefinition(theResource).getName();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.STRING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
String nextSpName = nextSpDef.getName();
|
||||
|
||||
if (isBlank(nextPath)) {
|
||||
|
||||
// // TODO: implement phonetic, and any others that have no path
|
||||
//
|
||||
// // TODO: do we still need this check?
|
||||
// if ("Questionnaire".equals(nextSpName) && nextSpDef.getName().equals("title")) {
|
||||
// Questionnaire q = (Questionnaire) theResource;
|
||||
// String title = "";// q.getGroup().getTitle();
|
||||
// addSearchTerm(theEntity, retVal, nextSpName, title);
|
||||
// }
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextObject instanceof IPrimitiveType<?>) {
|
||||
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) nextObject;
|
||||
String searchTerm = nextValue.getValueAsString();
|
||||
addSearchTerm(theEntity, retVal, nextSpName, searchTerm);
|
||||
} else {
|
||||
if (nextObject instanceof HumanName) {
|
||||
ArrayList<StringType> allNames = new ArrayList<>();
|
||||
HumanName nextHumanName = (HumanName) nextObject;
|
||||
if (isNotBlank(nextHumanName.getFamily())) {
|
||||
allNames.add(nextHumanName.getFamilyElement());
|
||||
}
|
||||
allNames.addAll(nextHumanName.getGiven());
|
||||
for (StringType nextName : allNames) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof Address) {
|
||||
ArrayList<StringType> allNames = new ArrayList<StringType>();
|
||||
Address nextAddress = (Address) nextObject;
|
||||
allNames.addAll(nextAddress.getLine());
|
||||
allNames.add(nextAddress.getCityElement());
|
||||
allNames.add(nextAddress.getStateElement());
|
||||
allNames.add(nextAddress.getCountryElement());
|
||||
allNames.add(nextAddress.getPostalCodeElement());
|
||||
for (StringType nextName : allNames) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof ContactPoint) {
|
||||
ContactPoint nextContact = (ContactPoint) nextObject;
|
||||
if (nextContact.getValueElement().isEmpty() == false) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextContact.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof Quantity) {
|
||||
BigDecimal value = ((Quantity) nextObject).getValue();
|
||||
if (value != null) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
|
||||
}
|
||||
} else if (nextObject instanceof Range) {
|
||||
SimpleQuantity low = ((Range) nextObject).getLow();
|
||||
if (low != null) {
|
||||
BigDecimal value = low.getValue();
|
||||
if (value != null) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + nextSpName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<BaseResourceIndexedSearchParam>();
|
||||
|
||||
String useSystem = null;
|
||||
if (theResource instanceof CodeSystem) {
|
||||
CodeSystem cs = (CodeSystem) theResource;
|
||||
useSystem = cs.getUrl();
|
||||
}
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.TOKEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<String> systems = new ArrayList<>();
|
||||
List<String> codes = new ArrayList<>();
|
||||
|
||||
// String needContactPointSystem = null;
|
||||
// if (nextPath.contains(".where(system='phone')")) {
|
||||
// nextPath = nextPath.replace(".where(system='phone')", "");
|
||||
// needContactPointSystem = "phone";
|
||||
// }
|
||||
// if (nextPath.contains(".where(system='email')")) {
|
||||
// nextPath = nextPath.replace(".where(system='email')", "");
|
||||
// needContactPointSystem = "email";
|
||||
// }
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Patient:language
|
||||
if (nextObject instanceof PatientCommunicationComponent) {
|
||||
PatientCommunicationComponent nextValue = (PatientCommunicationComponent) nextObject;
|
||||
nextObject = nextValue.getLanguage();
|
||||
}
|
||||
|
||||
if (nextObject instanceof Identifier) {
|
||||
Identifier nextValue = (Identifier) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String system = StringUtils.defaultIfBlank(nextValue.getSystemElement().getValueAsString(), null);
|
||||
String value = nextValue.getValueElement().getValue();
|
||||
if (isNotBlank(value)) {
|
||||
systems.add(system);
|
||||
codes.add(value);
|
||||
}
|
||||
|
||||
if (isNotBlank(nextValue.getType().getText())) {
|
||||
addStringParam(theEntity, retVal, nextSpDef, nextValue.getType().getText());
|
||||
}
|
||||
|
||||
} else if (nextObject instanceof ContactPoint) {
|
||||
ContactPoint nextValue = (ContactPoint) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
systems.add(nextValue.getSystemElement().getValueAsString());
|
||||
codes.add(nextValue.getValueElement().getValue());
|
||||
} else if (nextObject instanceof Enumeration<?>) {
|
||||
Enumeration<?> obj = (Enumeration<?>) nextObject;
|
||||
String system = extractSystem(obj);
|
||||
String code = obj.getValueAsString();
|
||||
if (isNotBlank(code)) {
|
||||
systems.add(system);
|
||||
codes.add(code);
|
||||
}
|
||||
} else if (nextObject instanceof IPrimitiveType<?>) {
|
||||
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if ("CodeSystem.concept.code".equals(nextPath)) {
|
||||
systems.add(useSystem);
|
||||
} else {
|
||||
systems.add(null);
|
||||
}
|
||||
codes.add(nextValue.getValueAsString());
|
||||
} else if (nextObject instanceof Coding) {
|
||||
Coding nextValue = (Coding) nextObject;
|
||||
extractTokensFromCoding(systems, codes, theEntity, retVal, nextSpDef, nextValue);
|
||||
} else if (nextObject instanceof CodeableConcept) {
|
||||
CodeableConcept nextCC = (CodeableConcept) nextObject;
|
||||
if (!nextCC.getTextElement().isEmpty()) {
|
||||
addStringParam(theEntity, retVal, nextSpDef, nextCC.getTextElement().getValue());
|
||||
}
|
||||
|
||||
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
|
||||
} else if (nextObject instanceof CapabilityStatementRestSecurityComponent) {
|
||||
// Conformance.security search param points to something kind of useless right now - This should probably
|
||||
// be fixed.
|
||||
CapabilityStatementRestSecurityComponent sec = (CapabilityStatementRestSecurityComponent) nextObject;
|
||||
for (CodeableConcept nextCC : sec.getService()) {
|
||||
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
|
||||
}
|
||||
} else if (nextObject instanceof LocationPositionComponent) {
|
||||
ourLog.warn("Position search not currently supported, not indexing location");
|
||||
continue;
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
||||
protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String thePaths) {
|
||||
return () -> {
|
||||
List<IBase> values = new ArrayList<>();
|
||||
String[] nextPathsSplit = split(thePaths);
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
List<Base> allValues = myFhirPathEngine.evaluate((Base) theResource, trim(nextPath));
|
||||
if (allValues.isEmpty() == false) {
|
||||
values.addAll(allValues);
|
||||
}
|
||||
}
|
||||
|
||||
assert systems.size() == codes.size() : "Systems contains " + systems + ", codes contains: " + codes;
|
||||
|
||||
Set<Pair<String, String>> haveValues = new HashSet<Pair<String, String>>();
|
||||
for (int i = 0; i < systems.size(); i++) {
|
||||
String system = systems.get(i);
|
||||
String code = codes.get(i);
|
||||
if (isBlank(system) && isBlank(code)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
system = system.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
code = code.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
|
||||
Pair<String, String> nextPair = Pair.of(system, code);
|
||||
if (haveValues.contains(nextPair)) {
|
||||
continue;
|
||||
}
|
||||
haveValues.add(nextPair);
|
||||
|
||||
ResourceIndexedSearchParamToken nextEntity;
|
||||
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), system, code);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamUri> retVal = new HashSet<ResourceIndexedSearchParamUri>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.URI) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
|
||||
if (nextObject instanceof UriType) {
|
||||
UriType nextValue = (UriType) nextObject;
|
||||
if (isBlank(nextValue.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ourLog.trace("Adding param: {}, {}", resourceName, nextValue.getValue());
|
||||
|
||||
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue());
|
||||
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConcept theCodeableConcept, ResourceTable theEntity,
|
||||
Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) {
|
||||
for (Coding nextCoding : theCodeableConcept.getCoding()) {
|
||||
extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding);
|
||||
}
|
||||
}
|
||||
|
||||
private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate,
|
||||
RuntimeSearchParam theParameterDef, Coding nextCoding) {
|
||||
if (nextCoding != null && !nextCoding.isEmpty()) {
|
||||
|
||||
String nextSystem = nextCoding.getSystemElement().getValueAsString();
|
||||
String nextCode = nextCoding.getCodeElement().getValue();
|
||||
if (isNotBlank(nextSystem) || isNotBlank(nextCode)) {
|
||||
theSystems.add(nextSystem);
|
||||
theCodes.add(nextCode);
|
||||
}
|
||||
|
||||
if (!nextCoding.getDisplayElement().isEmpty()) {
|
||||
addStringParam(theEntity, theListToPopulate, theParameterDef, nextCoding.getDisplayElement().getValue());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override parent because we're using FHIRPath here
|
||||
*/
|
||||
@Override
|
||||
protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
|
||||
FHIRPathEngine fp = new FHIRPathEngine(myWorkerContext);
|
||||
|
||||
List<Object> values = new ArrayList<>();
|
||||
String[] nextPathsSplit = SPLIT.split(thePaths);
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
List<Base> allValues;
|
||||
try {
|
||||
allValues = fp.evaluate((Base) theResource, trim(nextPath));
|
||||
} catch (FHIRException e) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
|
||||
throw new InternalErrorException(msg, e);
|
||||
}
|
||||
if (allValues.isEmpty() == false) {
|
||||
values.addAll(allValues);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
Object nextObject = values.get(i);
|
||||
if (nextObject instanceof Extension) {
|
||||
Extension nextExtension = (Extension) nextObject;
|
||||
nextObject = nextExtension.getValue();
|
||||
values.set(i, nextObject);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setValidationSupportForTesting(org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport theValidationSupport) {
|
||||
myValidationSupport = theValidationSupport;
|
||||
return values;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
myWorkerContext = new HapiWorkerContext(getContext(), myValidationSupport);
|
||||
}
|
||||
|
||||
private static <T extends Enum<?>> String extractSystem(Enumeration<T> theBoundCode) {
|
||||
if (theBoundCode.getValue() != null) {
|
||||
return theBoundCode.getEnumFactory().toSystem(theBoundCode.getValue());
|
||||
}
|
||||
return null;
|
||||
myFhirPathEngine = new FHIRPathEngine(myWorkerContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,47 +20,30 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.PathEngineException;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.Enumeration;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestSecurityComponent;
|
||||
import org.hl7.fhir.r4.model.Location.LocationPositionComponent;
|
||||
import org.hl7.fhir.r4.model.Patient.PatientCommunicationComponent;
|
||||
import org.hl7.fhir.r4.utils.FHIRPathEngine;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.measure.unit.NonSI;
|
||||
import javax.measure.unit.Unit;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorR4.class);
|
||||
private static final Set<Class<?>> ourIgnoredForSearchDatatypes;
|
||||
|
||||
static {
|
||||
|
@ -98,6 +81,20 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
|||
initFhirPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String thePaths) {
|
||||
return () -> {
|
||||
List<IBase> values = new ArrayList<>();
|
||||
String[] nextPathsSplit = split(thePaths);
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
List<Base> allValues = myFhirPathEngine.evaluate((Base) theResource, nextPath);
|
||||
values.addAll(allValues);
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initFhirPath() {
|
||||
IWorkerContext worker = new HapiWorkerContext(getContext(), myValidationSupport);
|
||||
|
@ -105,648 +102,8 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
|||
myFhirPathEngine.setHostServices(new SearchParamExtractorR4HostServices());
|
||||
}
|
||||
|
||||
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Quantity nextValue) {
|
||||
if (!nextValue.getValueElement().isEmpty()) {
|
||||
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
|
||||
String nextValueString = nextValue.getSystemElement().getValueAsString();
|
||||
String nextValueCode = nextValue.getCode();
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void addMoney(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Money nextValue) {
|
||||
if (!nextValue.getValueElement().isEmpty()) {
|
||||
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
|
||||
String nextValueString = "urn:iso:std:iso:4217";
|
||||
String nextValueCode = nextValue.getCurrency();
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
|
||||
if (isBlank(searchTerm)) {
|
||||
return;
|
||||
}
|
||||
if (searchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), resourceName, StringNormalizer.normalizeString(searchTerm), searchTerm);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
private void addStringParam(ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> retVal, RuntimeSearchParam nextSpDef, String value) {
|
||||
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), nextSpDef.getName(), StringNormalizer.normalizeString(value), value);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
||||
ArrayList<PathAndRef> retVal = new ArrayList<>();
|
||||
|
||||
String[] nextPathsSplit = SPLIT_R4.split(theNextSpDef.getPath());
|
||||
for (String path : nextPathsSplit) {
|
||||
path = path.trim();
|
||||
if (isNotBlank(path)) {
|
||||
|
||||
for (Object next : extractValues(path, theResource)) {
|
||||
retVal.add(new PathAndRef(path, next));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||
// TODO: implement
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.DATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamDate nextEntity;
|
||||
if (nextObject instanceof BaseDateTimeType) {
|
||||
BaseDateTimeType nextValue = (BaseDateTimeType) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue(), nextValue.getValueAsString());
|
||||
} else if (nextObject instanceof Period) {
|
||||
Period nextValue = (Period) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd(), nextValue.getStartElement().getValueAsString());
|
||||
} else if (nextObject instanceof Timing) {
|
||||
Timing nextValue = (Timing) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
TreeSet<Date> dates = new TreeSet<>();
|
||||
String firstValue = null;
|
||||
for (DateTimeType nextEvent : nextValue.getEvent()) {
|
||||
if (nextEvent.getValue() != null) {
|
||||
dates.add(nextEvent.getValue());
|
||||
if (firstValue == null) {
|
||||
firstValue = nextEvent.getValueAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextValue.getRepeat().hasBounds()) {
|
||||
if (nextValue.getRepeat().getBoundsPeriod().getStart() != null) {
|
||||
dates.add(nextValue.getRepeat().getBoundsPeriod().getStart());
|
||||
}
|
||||
if (nextValue.getRepeat().getBoundsPeriod().getEnd() != null) {
|
||||
dates.add(nextValue.getRepeat().getBoundsPeriod().getEnd());
|
||||
}
|
||||
}
|
||||
if (dates.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), dates.first(), dates.last(), firstValue);
|
||||
} else if (nextObject instanceof StringType) {
|
||||
// CarePlan.activitydate can be a string
|
||||
continue;
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
if (nextEntity != null) {
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.NUMBER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
|
||||
if (nextObject instanceof Duration) {
|
||||
Duration nextValue = (Duration) nextObject;
|
||||
if (nextValue.getValueElement().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SearchParamConstants.UCUM_NS.equals(nextValue.getSystem())) {
|
||||
if (isNotBlank(nextValue.getCode())) {
|
||||
|
||||
Unit<? extends javax.measure.quantity.Quantity> unit = Unit.valueOf(nextValue.getCode());
|
||||
javax.measure.converter.UnitConverter dayConverter = unit.getConverterTo(NonSI.DAY);
|
||||
double dayValue = dayConverter.convert(nextValue.getValue().doubleValue());
|
||||
Duration newValue = new Duration();
|
||||
newValue.setSystem(SearchParamConstants.UCUM_NS);
|
||||
newValue.setCode(NonSI.DAY.toString());
|
||||
newValue.setValue(dayValue);
|
||||
nextValue = newValue;
|
||||
|
||||
/*
|
||||
* @SuppressWarnings("unchecked") PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>>)
|
||||
* UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if (unit.isCompatible(UCUM.DAY)) {
|
||||
*
|
||||
* @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit = (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY);
|
||||
* double dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); Duration newValue = new Duration(); newValue.setSystem(UCUM_NS);
|
||||
* newValue.setCode(UCUM.DAY.getSymbol()); newValue.setValue(dayValue); nextValue=newValue; }
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof Quantity) {
|
||||
Quantity nextValue = (Quantity) nextObject;
|
||||
if (nextValue.getValueElement().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof IntegerType) {
|
||||
IntegerType nextValue = (IntegerType) nextObject;
|
||||
if (nextValue.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, new BigDecimal(nextValue.getValue()));
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof DecimalType) {
|
||||
DecimalType nextValue = (DecimalType) nextObject;
|
||||
if (nextValue.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamQuantity> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.QUANTITY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
|
||||
if (nextObject instanceof Quantity) {
|
||||
Quantity nextValue = (Quantity) nextObject;
|
||||
addQuantity(theEntity, retVal, resourceName, nextValue);
|
||||
} else if (nextObject instanceof Money) {
|
||||
Money nextValue = (Money) nextObject;
|
||||
addMoney(theEntity, retVal, resourceName, nextValue);
|
||||
} else if (nextObject instanceof Range) {
|
||||
Range nextValue = (Range) nextObject;
|
||||
addQuantity(theEntity, retVal, resourceName, nextValue.getLow());
|
||||
addQuantity(theEntity, retVal, resourceName, nextValue.getHigh());
|
||||
} else if (ourIgnoredForSearchDatatypes.contains(nextObject.getClass())) {
|
||||
continue;
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamString> retVal = new HashSet<>();
|
||||
|
||||
String resourceName = getContext().getResourceDefinition(theResource).getName();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.STRING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
String nextSpName = nextSpDef.getName();
|
||||
|
||||
if (isBlank(nextPath)) {
|
||||
|
||||
// // TODO: implement phonetic, and any others that have no path
|
||||
//
|
||||
// // TODO: do we still need this check?
|
||||
// if ("Questionnaire".equals(nextSpName) && nextSpDef.getName().equals("title")) {
|
||||
// Questionnaire q = (Questionnaire) theResource;
|
||||
// String title = "";// q.getGroup().getTitle();
|
||||
// addSearchTerm(theEntity, retVal, nextSpName, title);
|
||||
// }
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextObject instanceof IPrimitiveType<?>) {
|
||||
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) nextObject;
|
||||
String searchTerm = nextValue.getValueAsString();
|
||||
addSearchTerm(theEntity, retVal, nextSpName, searchTerm);
|
||||
} else {
|
||||
if (nextObject instanceof HumanName) {
|
||||
ArrayList<StringType> allNames = new ArrayList<>();
|
||||
HumanName nextHumanName = (HumanName) nextObject;
|
||||
if (isNotBlank(nextHumanName.getFamily())) {
|
||||
allNames.add(nextHumanName.getFamilyElement());
|
||||
}
|
||||
allNames.addAll(nextHumanName.getGiven());
|
||||
for (StringType nextName : allNames) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof Address) {
|
||||
ArrayList<StringType> allNames = new ArrayList<>();
|
||||
Address nextAddress = (Address) nextObject;
|
||||
allNames.addAll(nextAddress.getLine());
|
||||
allNames.add(nextAddress.getCityElement());
|
||||
allNames.add(nextAddress.getStateElement());
|
||||
allNames.add(nextAddress.getCountryElement());
|
||||
allNames.add(nextAddress.getPostalCodeElement());
|
||||
for (StringType nextName : allNames) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof ContactPoint) {
|
||||
ContactPoint nextContact = (ContactPoint) nextObject;
|
||||
if (nextContact.getValueElement().isEmpty() == false) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextContact.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof Quantity) {
|
||||
BigDecimal value = ((Quantity) nextObject).getValue();
|
||||
if (value != null) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
|
||||
}
|
||||
} else if (nextObject instanceof Range) {
|
||||
Quantity low = ((Range) nextObject).getLow();
|
||||
if (low != null) {
|
||||
BigDecimal value = low.getValue();
|
||||
if (value != null) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + nextSpName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<>();
|
||||
|
||||
String useSystem = null;
|
||||
if (theResource instanceof CodeSystem) {
|
||||
CodeSystem cs = (CodeSystem) theResource;
|
||||
useSystem = cs.getUrl();
|
||||
}
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.TOKEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceType = theEntity.getResourceType();
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<String> systems = new ArrayList<>();
|
||||
List<String> codes = new ArrayList<>();
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Patient:language
|
||||
if (nextObject instanceof PatientCommunicationComponent) {
|
||||
PatientCommunicationComponent nextValue = (PatientCommunicationComponent) nextObject;
|
||||
nextObject = nextValue.getLanguage();
|
||||
}
|
||||
|
||||
if (nextObject instanceof Identifier) {
|
||||
Identifier nextValue = (Identifier) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String system = StringUtils.defaultIfBlank(nextValue.getSystemElement().getValueAsString(), null);
|
||||
String value = nextValue.getValueElement().getValue();
|
||||
if (isNotBlank(value)) {
|
||||
systems.add(system);
|
||||
codes.add(value);
|
||||
}
|
||||
|
||||
if (isNotBlank(nextValue.getType().getText())) {
|
||||
addStringParam(theEntity, retVal, nextSpDef, nextValue.getType().getText());
|
||||
}
|
||||
|
||||
} else if (nextObject instanceof ContactPoint) {
|
||||
ContactPoint nextValue = (ContactPoint) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
systems.add(nextValue.getSystemElement().getValueAsString());
|
||||
codes.add(nextValue.getValueElement().getValue());
|
||||
} else if (nextObject instanceof Enumeration<?>) {
|
||||
Enumeration<?> obj = (Enumeration<?>) nextObject;
|
||||
String system = extractSystem(obj);
|
||||
String code = obj.getValueAsString();
|
||||
if (isNotBlank(code)) {
|
||||
systems.add(system);
|
||||
codes.add(code);
|
||||
}
|
||||
} else if (nextObject instanceof IPrimitiveType<?>) {
|
||||
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if ("CodeSystem.concept.code".equals(nextPath)) {
|
||||
systems.add(useSystem);
|
||||
} else {
|
||||
systems.add(null);
|
||||
}
|
||||
codes.add(nextValue.getValueAsString());
|
||||
} else if (nextObject instanceof Coding) {
|
||||
Coding nextValue = (Coding) nextObject;
|
||||
extractTokensFromCoding(systems, codes, theEntity, retVal, nextSpDef, nextValue);
|
||||
} else if (nextObject instanceof CodeableConcept) {
|
||||
CodeableConcept nextCC = (CodeableConcept) nextObject;
|
||||
if (!nextCC.getTextElement().isEmpty()) {
|
||||
addStringParam(theEntity, retVal, nextSpDef, nextCC.getTextElement().getValue());
|
||||
}
|
||||
|
||||
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
|
||||
} else if (nextObject instanceof CapabilityStatementRestSecurityComponent) {
|
||||
// Conformance.security search param points to something kind of useless right now - This should probably
|
||||
// be fixed.
|
||||
CapabilityStatementRestSecurityComponent sec = (CapabilityStatementRestSecurityComponent) nextObject;
|
||||
for (CodeableConcept nextCC : sec.getService()) {
|
||||
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
|
||||
}
|
||||
} else if (nextObject instanceof LocationPositionComponent) {
|
||||
ourLog.warn("Position search not currently supported, not indexing location");
|
||||
continue;
|
||||
} else if (nextObject instanceof StructureDefinition.StructureDefinitionContextComponent) {
|
||||
ourLog.warn("StructureDefinition context indexing not currently supported"); // TODO: implement this
|
||||
continue;
|
||||
} else if (resourceType.equals("Consent") && nextPath.equals("Consent.source")) {
|
||||
// Consent#source-identifier has a path that isn't typed - This is a one-off to deal with that
|
||||
continue;
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " with path " + nextPath + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
assert systems.size() == codes.size() : "Systems contains " + systems + ", codes contains: " + codes;
|
||||
|
||||
Set<Pair<String, String>> haveValues = new HashSet<>();
|
||||
for (int i = 0; i < systems.size(); i++) {
|
||||
String system = systems.get(i);
|
||||
String code = codes.get(i);
|
||||
if (isBlank(system) && isBlank(code)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
system = system.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
code = code.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
|
||||
Pair<String, String> nextPair = Pair.of(system, code);
|
||||
if (haveValues.contains(nextPair)) {
|
||||
continue;
|
||||
}
|
||||
haveValues.add(nextPair);
|
||||
|
||||
ResourceIndexedSearchParamToken nextEntity;
|
||||
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), system, code);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamUri> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.URI) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
|
||||
if (nextObject instanceof UriType) {
|
||||
UriType nextValue = (UriType) nextObject;
|
||||
if (isBlank(nextValue.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ourLog.trace("Adding param: {}, {}", resourceName, nextValue.getValue());
|
||||
|
||||
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue());
|
||||
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConcept theCodeableConcept, ResourceTable theEntity,
|
||||
Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) {
|
||||
for (Coding nextCoding : theCodeableConcept.getCoding()) {
|
||||
extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding);
|
||||
}
|
||||
}
|
||||
|
||||
private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate,
|
||||
RuntimeSearchParam theParameterDef, Coding nextCoding) {
|
||||
if (nextCoding != null && !nextCoding.isEmpty()) {
|
||||
|
||||
String nextSystem = nextCoding.getSystemElement().getValueAsString();
|
||||
String nextCode = nextCoding.getCodeElement().getValue();
|
||||
if (isNotBlank(nextSystem) || isNotBlank(nextCode)) {
|
||||
theSystems.add(nextSystem);
|
||||
theCodes.add(nextCode);
|
||||
}
|
||||
|
||||
if (!nextCoding.getDisplayElement().isEmpty()) {
|
||||
addStringParam(theEntity, theListToPopulate, theParameterDef, nextCoding.getDisplayElement().getValue());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override parent because we're using FHIRPath here
|
||||
*/
|
||||
@Override
|
||||
protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
|
||||
List<Object> values = new ArrayList<>();
|
||||
String[] nextPathsSplit = SPLIT_R4.split(thePaths);
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
List<Base> allValues;
|
||||
try {
|
||||
allValues = myFhirPathEngine.evaluate((Base) theResource, nextPath);
|
||||
} catch (FHIRException e) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
|
||||
throw new InternalErrorException(msg, e);
|
||||
}
|
||||
if (allValues.isEmpty() == false) {
|
||||
values.addAll(allValues);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
Object nextObject = values.get(i);
|
||||
if (nextObject instanceof Extension) {
|
||||
Extension nextExtension = (Extension) nextObject;
|
||||
nextObject = nextExtension.getValue();
|
||||
values.set(i, nextObject);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setValidationSupportForTesting(org.hl7.fhir.r4.hapi.ctx.IValidationSupport theValidationSupport) {
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
private class SearchParamExtractorR4HostServices implements FHIRPathEngine.IEvaluationContext {
|
||||
private static class SearchParamExtractorR4HostServices implements FHIRPathEngine.IEvaluationContext {
|
||||
|
||||
private Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
|
@ -802,6 +159,8 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
|||
ResourceType resourceType = ResourceType.fromCode(url.getResourceType());
|
||||
if (resourceType != null) {
|
||||
retVal = new Resource() {
|
||||
private static final long serialVersionUID = -5303169871827706447L;
|
||||
|
||||
@Override
|
||||
public Resource copy() {
|
||||
return this;
|
||||
|
@ -835,11 +194,4 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
|||
}
|
||||
}
|
||||
|
||||
private static <T extends Enum<?>> String extractSystem(Enumeration<T> theBoundCode) {
|
||||
if (theBoundCode.getValue() != null) {
|
||||
return theBoundCode.getEnumFactory().toSystem(theBoundCode.getValue());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,47 +20,26 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.PathEngineException;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r5.model.*;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestSecurityComponent;
|
||||
import org.hl7.fhir.r5.model.Location.LocationPositionComponent;
|
||||
import org.hl7.fhir.r5.model.Patient.PatientCommunicationComponent;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.measure.unit.NonSI;
|
||||
import javax.measure.unit.Unit;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorR5.class);
|
||||
|
||||
@Autowired
|
||||
private IValidationSupport myValidationSupport;
|
||||
private FHIRPathEngine myFhirPathEngine;
|
||||
|
@ -81,730 +60,103 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
|
|||
myFhirPathEngine.setHostServices(new SearchParamExtractorR5HostServices());
|
||||
}
|
||||
|
||||
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, IBase theQuantity) {
|
||||
BaseRuntimeElementCompositeDefinition<?> quantityDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Quantity");
|
||||
BaseRuntimeChildDefinition quantityValueChild = quantityDefinition.getChildByName("value");
|
||||
BaseRuntimeChildDefinition quantitySystemChild = quantityDefinition.getChildByName("system");
|
||||
BaseRuntimeChildDefinition quantityCodeChild = quantityDefinition.getChildByName("code");
|
||||
|
||||
Optional<IPrimitiveType<BigDecimal>> valueField = quantityValueChild.getAccessor().getFirstValueOrNull(theQuantity);
|
||||
if (valueField.isPresent() && valueField.get().getValue() != null) {
|
||||
BigDecimal nextValueValue = valueField.get().getValue();
|
||||
String system = quantitySystemChild.getAccessor().<IPrimitiveType<String>>getFirstValueOrNull(theQuantity).map(t-> t.getValue()).orElse(null);
|
||||
String code = quantityCodeChild.getAccessor().<IPrimitiveType<String>>getFirstValueOrNull(theQuantity).map(t-> t.getValue()).orElse(null);
|
||||
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, system, code);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void addMoney(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, IBase theMoney) {
|
||||
BaseRuntimeElementCompositeDefinition<?> moneyDefinition = (BaseRuntimeElementCompositeDefinition<?>) getContext().getElementDefinition("Money");
|
||||
BaseRuntimeChildDefinition moneyValueChild = moneyDefinition.getChildByName("value");
|
||||
BaseRuntimeChildDefinition moneyCurrencyChild = moneyDefinition.getChildByName("currency");
|
||||
|
||||
Optional<IPrimitiveType<BigDecimal>> valueField = moneyValueChild.getAccessor().getFirstValueOrNull(theMoney);
|
||||
if (valueField.isPresent() && valueField.get().getValue() != null) {
|
||||
BigDecimal nextValueValue = valueField.get().getValue();
|
||||
|
||||
String nextValueString = "urn:iso:std:iso:4217";
|
||||
String nextValueCode = moneyCurrencyChild.getAccessor().<IPrimitiveType<String>>getFirstValueOrNull(theMoney).map(t-> t.getValue()).orElse(null);
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
|
||||
if (isBlank(searchTerm)) {
|
||||
return;
|
||||
}
|
||||
if (searchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), resourceName, StringNormalizer.normalizeString(searchTerm), searchTerm);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
private void addStringParam(ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> retVal, RuntimeSearchParam nextSpDef, String value) {
|
||||
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), nextSpDef.getName(), StringNormalizer.normalizeString(value), value);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
||||
ArrayList<PathAndRef> retVal = new ArrayList<>();
|
||||
protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String nextPath) {
|
||||
return () -> myFhirPathEngine.evaluate((Base) theResource, nextPath);
|
||||
}
|
||||
|
||||
String[] nextPathsSplit = SPLIT_R4.split(theNextSpDef.getPath());
|
||||
for (String path : nextPathsSplit) {
|
||||
path = path.trim();
|
||||
if (isNotBlank(path)) {
|
||||
|
||||
for (Object next : extractValues(path, theResource)) {
|
||||
retVal.add(new PathAndRef(path, next));
|
||||
}
|
||||
}
|
||||
private static class SearchParamExtractorR5HostServices implements FHIRPathEngine.IEvaluationContext {
|
||||
|
||||
private Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
@Override
|
||||
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
@Override
|
||||
public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||
// TODO: implement
|
||||
return Collections.emptySet();
|
||||
}
|
||||
@Override
|
||||
public boolean log(String argument, List<Base> focus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<>();
|
||||
@Override
|
||||
public FunctionDetails resolveFunction(String functionName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.DATE) {
|
||||
continue;
|
||||
}
|
||||
@Override
|
||||
public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
@Override
|
||||
public List<Base> executeFunction(Object appContext, String functionName, List<List<Base>> parameters) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
@Override
|
||||
public Base resolveReference(Object theAppContext, String theUrl) throws FHIRException {
|
||||
|
||||
/*
|
||||
* When we're doing resolution within the SearchParamExtractor, if we want
|
||||
* to do a resolve() it's just to check the type, so there is no point
|
||||
* going through the heavyweight test. We can just return a stub and
|
||||
* that's good enough since we're just doing something like
|
||||
* Encounter.patient.where(resolve() is Patient)
|
||||
*/
|
||||
IdType url = new IdType(theUrl);
|
||||
Base retVal = null;
|
||||
if (isNotBlank(url.getResourceType())) {
|
||||
|
||||
retVal = myResourceTypeToStub.get(url.getResourceType());
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamDate nextEntity;
|
||||
if (nextObject instanceof BaseDateTimeType) {
|
||||
BaseDateTimeType nextValue = (BaseDateTimeType) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue(), nextValue.getValueAsString());
|
||||
} else if (nextObject instanceof Period) {
|
||||
Period nextValue = (Period) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd(), nextValue.getStartElement().getValueAsString());
|
||||
} else if (nextObject instanceof Timing) {
|
||||
Timing nextValue = (Timing) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
TreeSet<Date> dates = new TreeSet<>();
|
||||
String firstValue = null;
|
||||
for (DateTimeType nextEvent : nextValue.getEvent()) {
|
||||
if (nextEvent.getValue() != null) {
|
||||
dates.add(nextEvent.getValue());
|
||||
if (firstValue == null) {
|
||||
firstValue = nextEvent.getValueAsString();
|
||||
}
|
||||
ResourceType resourceType = ResourceType.fromCode(url.getResourceType());
|
||||
if (resourceType != null) {
|
||||
retVal = new Resource() {
|
||||
private static final long serialVersionUID = 2368522971330181178L;
|
||||
|
||||
@Override
|
||||
public Resource copy() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
if (nextValue.getRepeat().hasBounds()) {
|
||||
if (nextValue.getRepeat().getBoundsPeriod().getStart() != null) {
|
||||
dates.add(nextValue.getRepeat().getBoundsPeriod().getStart());
|
||||
|
||||
@Override
|
||||
public ResourceType getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
if (nextValue.getRepeat().getBoundsPeriod().getEnd() != null) {
|
||||
dates.add(nextValue.getRepeat().getBoundsPeriod().getEnd());
|
||||
|
||||
@Override
|
||||
public String fhirType() {
|
||||
return url.getResourceType();
|
||||
}
|
||||
}
|
||||
if (dates.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), dates.first(), dates.last(), firstValue);
|
||||
} else if (nextObject instanceof StringType) {
|
||||
// CarePlan.activitydate can be a string
|
||||
continue;
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
if (nextEntity != null) {
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
};
|
||||
myResourceTypeToStub.put(url.getResourceType(), retVal);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.NUMBER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
|
||||
if (nextObject instanceof Duration) {
|
||||
Duration nextValue = (Duration) nextObject;
|
||||
if (nextValue.getValueElement().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SearchParamConstants.UCUM_NS.equals(nextValue.getSystem())) {
|
||||
if (isNotBlank(nextValue.getCode())) {
|
||||
|
||||
Unit<? extends javax.measure.quantity.Quantity> unit = Unit.valueOf(nextValue.getCode());
|
||||
javax.measure.converter.UnitConverter dayConverter = unit.getConverterTo(NonSI.DAY);
|
||||
double dayValue = dayConverter.convert(nextValue.getValue().doubleValue());
|
||||
Duration newValue = new Duration();
|
||||
newValue.setSystem(SearchParamConstants.UCUM_NS);
|
||||
newValue.setCode(NonSI.DAY.toString());
|
||||
newValue.setValue(dayValue);
|
||||
nextValue = newValue;
|
||||
|
||||
/*
|
||||
* @SuppressWarnings("unchecked") PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>>)
|
||||
* UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if (unit.isCompatible(UCUM.DAY)) {
|
||||
*
|
||||
* @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit = (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY);
|
||||
* double dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); Duration newValue = new Duration(); newValue.setSystem(UCUM_NS);
|
||||
* newValue.setCode(UCUM.DAY.getSymbol()); newValue.setValue(dayValue); nextValue=newValue; }
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof Quantity) {
|
||||
Quantity nextValue = (Quantity) nextObject;
|
||||
if (nextValue.getValueElement().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof IntegerType) {
|
||||
IntegerType nextValue = (IntegerType) nextObject;
|
||||
if (nextValue.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, new BigDecimal(nextValue.getValue()));
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof DecimalType) {
|
||||
DecimalType nextValue = (DecimalType) nextObject;
|
||||
if (nextValue.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
|
||||
return false;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamQuantity> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.QUANTITY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
|
||||
for (IBase nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || nextObject.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
String typeName = getContext().getElementDefinition(nextObject.getClass()).getName();
|
||||
|
||||
if (typeName.equals("Quantity")) {
|
||||
addQuantity(theEntity, retVal, resourceName, nextObject);
|
||||
} else if (typeName.equals("Money")) {
|
||||
addMoney(theEntity, retVal, resourceName, nextObject);
|
||||
} else if (nextObject instanceof Range) {
|
||||
Range nextValue = (Range) nextObject;
|
||||
addQuantity(theEntity, retVal, resourceName, nextValue.getLow());
|
||||
addQuantity(theEntity, retVal, resourceName, nextValue.getHigh());
|
||||
} else if (!getIgnoredForSearchDatatypes().contains(nextObject.getClass())) {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public ValueSet resolveValueSet(Object theO, String theS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamString> retVal = new HashSet<>();
|
||||
|
||||
String resourceName = getContext().getResourceDefinition(theResource).getName();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.STRING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
String nextSpName = nextSpDef.getName();
|
||||
|
||||
if (isBlank(nextPath)) {
|
||||
|
||||
// // TODO: implement phonetic, and any others that have no path
|
||||
//
|
||||
// // TODO: do we still need this check?
|
||||
// if ("Questionnaire".equals(nextSpName) && nextSpDef.getName().equals("title")) {
|
||||
// Questionnaire q = (Questionnaire) theResource;
|
||||
// String title = "";// q.getGroup().getTitle();
|
||||
// addSearchTerm(theEntity, retVal, nextSpName, title);
|
||||
// }
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextObject instanceof IPrimitiveType<?>) {
|
||||
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) nextObject;
|
||||
String searchTerm = nextValue.getValueAsString();
|
||||
addSearchTerm(theEntity, retVal, nextSpName, searchTerm);
|
||||
} else {
|
||||
if (nextObject instanceof HumanName) {
|
||||
ArrayList<StringType> allNames = new ArrayList<>();
|
||||
HumanName nextHumanName = (HumanName) nextObject;
|
||||
if (isNotBlank(nextHumanName.getFamily())) {
|
||||
allNames.add(nextHumanName.getFamilyElement());
|
||||
}
|
||||
allNames.addAll(nextHumanName.getGiven());
|
||||
for (StringType nextName : allNames) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof Address) {
|
||||
ArrayList<StringType> allNames = new ArrayList<>();
|
||||
Address nextAddress = (Address) nextObject;
|
||||
allNames.addAll(nextAddress.getLine());
|
||||
allNames.add(nextAddress.getCityElement());
|
||||
allNames.add(nextAddress.getStateElement());
|
||||
allNames.add(nextAddress.getCountryElement());
|
||||
allNames.add(nextAddress.getPostalCodeElement());
|
||||
for (StringType nextName : allNames) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof ContactPoint) {
|
||||
ContactPoint nextContact = (ContactPoint) nextObject;
|
||||
if (nextContact.getValueElement().isEmpty() == false) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, nextContact.getValue());
|
||||
}
|
||||
} else if (nextObject instanceof Quantity) {
|
||||
BigDecimal value = ((Quantity) nextObject).getValue();
|
||||
if (value != null) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
|
||||
}
|
||||
} else if (nextObject instanceof Range) {
|
||||
Quantity low = ((Range) nextObject).getLow();
|
||||
if (low != null) {
|
||||
BigDecimal value = low.getValue();
|
||||
if (value != null) {
|
||||
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + nextSpName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
|
||||
*/
|
||||
@Override
|
||||
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<>();
|
||||
|
||||
String useSystem = null;
|
||||
if (theResource instanceof CodeSystem) {
|
||||
CodeSystem cs = (CodeSystem) theResource;
|
||||
useSystem = cs.getUrl();
|
||||
}
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.TOKEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceType = theEntity.getResourceType();
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<String> systems = new ArrayList<>();
|
||||
List<String> codes = new ArrayList<>();
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Patient:language
|
||||
if (nextObject instanceof PatientCommunicationComponent) {
|
||||
PatientCommunicationComponent nextValue = (PatientCommunicationComponent) nextObject;
|
||||
nextObject = nextValue.getLanguage();
|
||||
}
|
||||
|
||||
if (nextObject instanceof Identifier) {
|
||||
Identifier nextValue = (Identifier) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String system = StringUtils.defaultIfBlank(nextValue.getSystemElement().getValueAsString(), null);
|
||||
String value = nextValue.getValueElement().getValue();
|
||||
if (isNotBlank(value)) {
|
||||
systems.add(system);
|
||||
codes.add(value);
|
||||
}
|
||||
|
||||
if (isNotBlank(nextValue.getType().getText())) {
|
||||
addStringParam(theEntity, retVal, nextSpDef, nextValue.getType().getText());
|
||||
}
|
||||
|
||||
} else if (nextObject instanceof ContactPoint) {
|
||||
ContactPoint nextValue = (ContactPoint) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
systems.add(nextValue.getSystemElement().getValueAsString());
|
||||
codes.add(nextValue.getValueElement().getValue());
|
||||
} else if (nextObject instanceof IBaseEnumeration<?>) {
|
||||
IBaseEnumeration<?> obj = (IBaseEnumeration<?>) nextObject;
|
||||
String system = extractSystem(obj);
|
||||
String code = obj.getValueAsString();
|
||||
if (isNotBlank(code)) {
|
||||
systems.add(system);
|
||||
codes.add(code);
|
||||
}
|
||||
} else if (nextObject instanceof IPrimitiveType<?>) {
|
||||
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if ("CodeSystem.concept.code".equals(nextPath)) {
|
||||
systems.add(useSystem);
|
||||
} else {
|
||||
systems.add(null);
|
||||
}
|
||||
codes.add(nextValue.getValueAsString());
|
||||
} else if (nextObject instanceof Coding) {
|
||||
Coding nextValue = (Coding) nextObject;
|
||||
extractTokensFromCoding(systems, codes, theEntity, retVal, nextSpDef, nextValue);
|
||||
} else if (nextObject instanceof CodeableConcept) {
|
||||
CodeableConcept nextCC = (CodeableConcept) nextObject;
|
||||
if (!nextCC.getTextElement().isEmpty()) {
|
||||
addStringParam(theEntity, retVal, nextSpDef, nextCC.getTextElement().getValue());
|
||||
}
|
||||
|
||||
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
|
||||
} else if (nextObject instanceof CapabilityStatementRestSecurityComponent) {
|
||||
// Conformance.security search param points to something kind of useless right now - This should probably
|
||||
// be fixed.
|
||||
CapabilityStatementRestSecurityComponent sec = (CapabilityStatementRestSecurityComponent) nextObject;
|
||||
for (CodeableConcept nextCC : sec.getService()) {
|
||||
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
|
||||
}
|
||||
} else if (nextObject instanceof LocationPositionComponent) {
|
||||
ourLog.warn("Position search not currently supported, not indexing location");
|
||||
continue;
|
||||
} else if (nextObject instanceof StructureDefinition.StructureDefinitionContextComponent) {
|
||||
ourLog.warn("StructureDefinition context indexing not currently supported"); // TODO: implement this
|
||||
continue;
|
||||
} else if (resourceType.equals("Consent") && nextPath.equals("Consent.source")) {
|
||||
// Consent#source-identifier has a path that isn't typed - This is a one-off to deal with that
|
||||
continue;
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " with path " + nextPath + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
assert systems.size() == codes.size() : "Systems contains " + systems + ", codes contains: " + codes;
|
||||
|
||||
Set<Pair<String, String>> haveValues = new HashSet<>();
|
||||
for (int i = 0; i < systems.size(); i++) {
|
||||
String system = systems.get(i);
|
||||
String code = codes.get(i);
|
||||
if (isBlank(system) && isBlank(code)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
system = system.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
code = code.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
|
||||
}
|
||||
|
||||
Pair<String, String> nextPair = Pair.of(system, code);
|
||||
if (haveValues.contains(nextPair)) {
|
||||
continue;
|
||||
}
|
||||
haveValues.add(nextPair);
|
||||
|
||||
ResourceIndexedSearchParamToken nextEntity;
|
||||
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), system, code);
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<ResourceIndexedSearchParamUri> retVal = new HashSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.URI) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourceName = nextSpDef.getName();
|
||||
|
||||
if (nextObject instanceof UriType) {
|
||||
UriType nextValue = (UriType) nextObject;
|
||||
if (isBlank(nextValue.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ourLog.trace("Adding param: {}, {}", resourceName, nextValue.getValue());
|
||||
|
||||
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue());
|
||||
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConcept theCodeableConcept, ResourceTable theEntity,
|
||||
Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) {
|
||||
for (Coding nextCoding : theCodeableConcept.getCoding()) {
|
||||
extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding);
|
||||
}
|
||||
}
|
||||
|
||||
private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate,
|
||||
RuntimeSearchParam theParameterDef, Coding nextCoding) {
|
||||
if (nextCoding != null && !nextCoding.isEmpty()) {
|
||||
|
||||
String nextSystem = nextCoding.getSystemElement().getValueAsString();
|
||||
String nextCode = nextCoding.getCodeElement().getValue();
|
||||
if (isNotBlank(nextSystem) || isNotBlank(nextCode)) {
|
||||
theSystems.add(nextSystem);
|
||||
theCodes.add(nextCode);
|
||||
}
|
||||
|
||||
if (!nextCoding.getDisplayElement().isEmpty()) {
|
||||
addStringParam(theEntity, theListToPopulate, theParameterDef, nextCoding.getDisplayElement().getValue());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<List<? extends IBase>> getPathValueExtractor(IBaseResource theResource, String nextPath) {
|
||||
return () -> {
|
||||
try {
|
||||
IWorkerContext worker = new HapiWorkerContext(getContext(), myValidationSupport);
|
||||
FHIRPathEngine fp = new FHIRPathEngine(worker);
|
||||
fp.setHostServices(new SearchParamExtractorR5HostServices());
|
||||
return fp.evaluate((Base) theResource, nextPath);
|
||||
} catch (FHIRException e) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
|
||||
throw new InternalErrorException(msg, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class SearchParamExtractorR5HostServices implements FHIRPathEngine.IEvaluationContext {
|
||||
|
||||
private Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
@Override
|
||||
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean log(String argument, List<Base> focus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionDetails resolveFunction(String functionName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Base> executeFunction(Object appContext, String functionName, List<List<Base>> parameters) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Base resolveReference(Object theAppContext, String theUrl) throws FHIRException {
|
||||
|
||||
/*
|
||||
* When we're doing resolution within the SearchParamExtractor, if we want
|
||||
* to do a resolve() it's just to check the type, so there is no point
|
||||
* going through the heavyweight test. We can just return a stub and
|
||||
* that's good enough since we're just doing something like
|
||||
* Encounter.patient.where(resolve() is Patient)
|
||||
*/
|
||||
IdType url = new IdType(theUrl);
|
||||
Base retVal = null;
|
||||
if (isNotBlank(url.getResourceType())) {
|
||||
|
||||
retVal = myResourceTypeToStub.get(url.getResourceType());
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
ResourceType resourceType = ResourceType.fromCode(url.getResourceType());
|
||||
if (resourceType != null) {
|
||||
retVal = new Resource() {
|
||||
@Override
|
||||
public Resource copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceType getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fhirType() {
|
||||
return url.getResourceType();
|
||||
}
|
||||
|
||||
};
|
||||
myResourceTypeToStub.put(url.getResourceType(), retVal);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet resolveValueSet(Object theO, String theS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static <T extends Enum<?>> String extractSystem(IBaseEnumeration<T> theBoundCode) {
|
||||
if (theBoundCode.getValue() != null) {
|
||||
return theBoundCode.getEnumFactory().toSystem(theBoundCode.getValue());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -256,7 +256,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<Object> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
|
@ -378,7 +378,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<Object> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
|
@ -480,7 +480,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<Object> values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -691,7 +691,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<Object> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
|
@ -792,7 +792,7 @@ public class FhirTerserR4Test {
|
|||
public void testGetValuesWithTheCreate() {
|
||||
Patient p = new Patient();
|
||||
|
||||
List<Object> values = ourCtx.newTerser().getValues(p, "Patient.active", true);
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
|
@ -841,7 +841,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<Object> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
|
@ -957,7 +957,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<Object> values = ourCtx.newTerser().getValues(p, "Patient.active", true);
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
||||
public class FhirTerserTest {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirTerserTest.class);
|
||||
|
||||
@Test
|
||||
public void testGetAllPopulatedChildElementsOfType() {
|
||||
|
||||
|
@ -53,13 +53,13 @@ public class FhirTerserTest {
|
|||
|
||||
// As string
|
||||
{
|
||||
List<Object> values = t.getValues(obs, "Observation.valueString");
|
||||
List<IBase> values = t.getValues(obs, "Observation.valueString");
|
||||
assertEquals(0, values.size());
|
||||
}
|
||||
|
||||
// As quantity
|
||||
{
|
||||
List<Object> values = t.getValues(obs, "Observation.valueQuantity");
|
||||
List<IBase> values = t.getValues(obs, "Observation.valueQuantity");
|
||||
assertEquals(1, values.size());
|
||||
Quantity actual = (Quantity) values.get(0);
|
||||
assertEquals("123", actual.getValueElement().getValueAsString());
|
||||
|
|
Loading…
Reference in New Issue