Improve performance of _lastUpdated queries in JPA server
This commit is contained in:
parent
6ddf91d9e2
commit
00ced6a652
|
@ -71,7 +71,6 @@ import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
|||
import ca.uhn.fhir.model.api.annotation.Block;
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.api.annotation.Compartment;
|
||||
import ca.uhn.fhir.model.api.annotation.Compartments;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.api.annotation.Extension;
|
||||
|
@ -708,23 +707,24 @@ class ModelScanner {
|
|||
for (Field nextField : theClass.getFields()) {
|
||||
SearchParamDefinition searchParam = pullAnnotation(nextField, SearchParamDefinition.class);
|
||||
if (searchParam != null) {
|
||||
RestSearchParameterTypeEnum paramType = RestSearchParameterTypeEnum.valueOf(searchParam.type().toUpperCase());
|
||||
RestSearchParameterTypeEnum paramType = RestSearchParameterTypeEnum.forCode(searchParam.type().toLowerCase());
|
||||
if (paramType == null) {
|
||||
throw new ConfigurationException("Search param " + searchParam.name() + " has an invalid type: " + searchParam.type());
|
||||
}
|
||||
Set<String> providesMembershipInCompartments = null;
|
||||
providesMembershipInCompartments = new HashSet<String>();
|
||||
for (Compartment next : searchParam.providesMembershipIn()) {
|
||||
if (paramType != RestSearchParameterTypeEnum.REFERENCE) {
|
||||
throw new ConfigurationException("Search param " + searchParam.name() + " provides compartment membershit but is not of type 'reference'");
|
||||
}
|
||||
providesMembershipInCompartments.add(next.name());
|
||||
}
|
||||
|
||||
if (paramType == RestSearchParameterTypeEnum.COMPOSITE) {
|
||||
compositeFields.put(nextField, searchParam);
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<String> providesMembershipInCompartments = null;
|
||||
Compartments compartmentsAnnotation = pullAnnotation(nextField, Compartments.class);
|
||||
if (compartmentsAnnotation != null) {
|
||||
providesMembershipInCompartments = new HashSet<String>();
|
||||
for (Compartment next : compartmentsAnnotation.providesMembershipIn()) {
|
||||
providesMembershipInCompartments.add(next.name());
|
||||
}
|
||||
}
|
||||
|
||||
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType, providesMembershipInCompartments);
|
||||
theResourceDef.addSearchParam(param);
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.context;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -37,6 +38,7 @@ import ca.uhn.fhir.util.UrlUtil;
|
|||
public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefinition<IBaseResource> {
|
||||
|
||||
private RuntimeResourceDefinition myBaseDefinition;
|
||||
private Map<String, List<RuntimeSearchParam>> myCompartmentNameToSearchParams;
|
||||
private FhirContext myContext;
|
||||
private String myId;
|
||||
private Map<String, RuntimeSearchParam> myNameToSearchParam = new LinkedHashMap<String, RuntimeSearchParam>();
|
||||
|
@ -47,7 +49,7 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
|
||||
public RuntimeResourceDefinition(FhirContext theContext, String theResourceName, Class<? extends IBaseResource> theClass, ResourceDef theResourceAnnotation, boolean theStandardType) {
|
||||
super(theResourceName, theClass, theStandardType);
|
||||
myContext= theContext;
|
||||
myContext = theContext;
|
||||
myResourceProfile = theResourceAnnotation.profile();
|
||||
myId = theResourceAnnotation.id();
|
||||
|
||||
|
@ -134,6 +136,17 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
return mySearchParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will not return null
|
||||
*/
|
||||
public List<RuntimeSearchParam> getSearchParamsForCompartmentName(String theCompartmentName) {
|
||||
List<RuntimeSearchParam> retVal = myCompartmentNameToSearchParams.get(theCompartmentName);
|
||||
if (retVal == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public FhirVersionEnum getStructureVersion() {
|
||||
return myStructureVersion;
|
||||
}
|
||||
|
@ -161,14 +174,27 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
});
|
||||
mySearchParams = Collections.unmodifiableList(searchParams);
|
||||
|
||||
Map<String, List<RuntimeSearchParam>> compartmentNameToSearchParams = new HashMap<String, List<RuntimeSearchParam>>();
|
||||
for (RuntimeSearchParam next : searchParams) {
|
||||
if (next.getProvidesMembershipInCompartments() != null) {
|
||||
for (String nextCompartment : next.getProvidesMembershipInCompartments()) {
|
||||
if (!compartmentNameToSearchParams.containsKey(nextCompartment)) {
|
||||
compartmentNameToSearchParams.put(nextCompartment, new ArrayList<RuntimeSearchParam>());
|
||||
}
|
||||
compartmentNameToSearchParams.get(nextCompartment).add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
myCompartmentNameToSearchParams = Collections.unmodifiableMap(compartmentNameToSearchParams);
|
||||
|
||||
Class<?> target = getImplementingClass();
|
||||
myBaseDefinition = this;
|
||||
do {
|
||||
target = target.getSuperclass();
|
||||
if (IResource.class.isAssignableFrom(target) && target.getAnnotation(ResourceDef.class)!=null) {
|
||||
if (IResource.class.isAssignableFrom(target) && target.getAnnotation(ResourceDef.class) != null) {
|
||||
myBaseDefinition = (RuntimeResourceDefinition) theClassToElementDefinitions.get(target);
|
||||
}
|
||||
} while (target.equals(Object.class)==false);
|
||||
} while (target.equals(Object.class) == false);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
|
|
@ -24,16 +24,16 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value= {})
|
||||
public @interface Compartment {
|
||||
|
||||
/**
|
||||
* This may only be populated on a reference field. On such a field, places the containing
|
||||
/**
|
||||
* This may only be populated on a reference search paramater field. On such a field, places the containing
|
||||
* resource in a compartment with the name(s) specified by the given strings, where the compartment
|
||||
* belongs to the target resource. For example, this field could be populated with <code>Patient</code> on
|
||||
* the <code>Observation.subject</code> field.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value= {})
|
||||
public @interface Compartment {
|
||||
|
||||
String name();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
package ca.uhn.fhir.model.api.annotation;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value= {ElementType.FIELD})
|
||||
public @interface Compartments {
|
||||
|
||||
Compartment[] providesMembershipIn();
|
||||
|
||||
}
|
|
@ -66,4 +66,10 @@ public @interface SearchParamDefinition {
|
|||
*/
|
||||
Class<? extends IBaseResource>[] target() default {};
|
||||
|
||||
/**
|
||||
* Indicates that this field indicates that resources linked to by this parameter
|
||||
* (must be a reference parameter) place the resource in the given compartment.
|
||||
*/
|
||||
Compartment[] providesMembershipIn() default {};
|
||||
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ public enum RestSearchParameterTypeEnum {
|
|||
/**
|
||||
* Returns the enumerated value associated with this code
|
||||
*/
|
||||
public RestSearchParameterTypeEnum forCode(String theCode) {
|
||||
public static RestSearchParameterTypeEnum forCode(String theCode) {
|
||||
RestSearchParameterTypeEnum retVal = CODE_TO_ENUM.get(theCode);
|
||||
return retVal;
|
||||
}
|
||||
|
@ -141,6 +141,8 @@ public enum RestSearchParameterTypeEnum {
|
|||
* Converts codes to their respective enumerated values
|
||||
*/
|
||||
public static final IValueSetEnumBinder<RestSearchParameterTypeEnum> VALUESET_BINDER = new IValueSetEnumBinder<RestSearchParameterTypeEnum>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public String toCodeString(RestSearchParameterTypeEnum theEnum) {
|
||||
return theEnum.getCode();
|
||||
|
|
|
@ -46,6 +46,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeChildDirectResource;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
||||
|
@ -63,6 +64,16 @@ public class FhirTerser {
|
|||
myContext = theContext;
|
||||
}
|
||||
|
||||
private List<String> addNameToList(List<String> theCurrentList, BaseRuntimeChildDefinition theChildDefinition) {
|
||||
if (theChildDefinition == null)
|
||||
return null;
|
||||
if (theCurrentList == null || theCurrentList.isEmpty())
|
||||
return new ArrayList<String>(Arrays.asList(theChildDefinition.getElementName()));
|
||||
List<String> newList = new ArrayList<String>(theCurrentList);
|
||||
newList.add(theChildDefinition.getElementName());
|
||||
return newList;
|
||||
}
|
||||
|
||||
private void addUndeclaredExtensions(IBase theElement, BaseRuntimeElementDefinition<?> theDefinition, BaseRuntimeChildDefinition theChildDefinition, IModelVisitor theCallback) {
|
||||
if (theElement instanceof ISupportsUndeclaredExtensions) {
|
||||
ISupportsUndeclaredExtensions containingElement = (ISupportsUndeclaredExtensions) theElement;
|
||||
|
@ -88,6 +99,51 @@ public class FhirTerser {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones all values from a source object into the equivalent fields in a target object
|
||||
* @param theSource The source object (must not be null)
|
||||
* @param theTarget The target object to copy values into (must not be null)
|
||||
* @param theIgnoreMissingFields The ignore fields in the target which do not exist (if false, an exception will be thrown if the target is unable to accept a value from the source)
|
||||
*/
|
||||
public void cloneInto(IBase theSource, IBase theTarget, boolean theIgnoreMissingFields) {
|
||||
Validate.notNull(theSource, "theSource must not be null");
|
||||
Validate.notNull(theTarget, "theTarget must not be null");
|
||||
|
||||
if (theSource instanceof IPrimitiveType<?>) {
|
||||
if (theTarget instanceof IPrimitiveType<?>) {
|
||||
((IPrimitiveType<?>)theTarget).setValueAsString(((IPrimitiveType<?>)theSource).getValueAsString());
|
||||
return;
|
||||
} else {
|
||||
if (theIgnoreMissingFields) {
|
||||
return;
|
||||
} else {
|
||||
throw new DataFormatException("Can not copy value from primitive of type " + theSource.getClass().getName() + " into type " + theTarget.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> sourceDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theSource.getClass());
|
||||
BaseRuntimeElementCompositeDefinition<?> targetDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theTarget.getClass());
|
||||
|
||||
for (BaseRuntimeChildDefinition nextChild : sourceDef.getChildren()) {
|
||||
for (IBase nextValue : nextChild.getAccessor().getValues(theSource)) {
|
||||
BaseRuntimeChildDefinition targetChild = targetDef.getChildByName(nextChild.getElementName());
|
||||
if (targetChild == null) {
|
||||
if (theIgnoreMissingFields) {
|
||||
continue;
|
||||
} else {
|
||||
throw new DataFormatException("Type " + theTarget.getClass().getName() + " does not have a child with name " + nextChild.getElementName());
|
||||
}
|
||||
}
|
||||
|
||||
IBase target = targetChild.getChildByName(nextChild.getElementName()).newInstance();
|
||||
targetChild.getMutator().addValue(theTarget, target);
|
||||
cloneInto(nextValue, target, theIgnoreMissingFields);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing all child elements (including the resource itself) which are <b>non-empty</b> and are either of the exact type specified, or are a subclass of that type.
|
||||
* <p>
|
||||
|
@ -186,6 +242,33 @@ public class FhirTerser {
|
|||
|
||||
}
|
||||
|
||||
public Object getSingleValueOrNull(IBase theTarget, String thePath) {
|
||||
Class<Object> wantedType = Object.class;
|
||||
|
||||
return getSingleValueOrNull(theTarget, thePath, wantedType);
|
||||
}
|
||||
|
||||
public <T> T getSingleValueOrNull(IBase theTarget, String thePath, Class<T> theWantedType) {
|
||||
Validate.notNull(theTarget, "theTarget must not be null");
|
||||
Validate.notBlank(thePath, "thePath must not be empty");
|
||||
|
||||
BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theTarget.getClass());
|
||||
if (!(def instanceof BaseRuntimeElementCompositeDefinition)) {
|
||||
throw new IllegalArgumentException("Target is not a composite type: " + theTarget.getClass().getName());
|
||||
}
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> currentDef = (BaseRuntimeElementCompositeDefinition<?>) def;
|
||||
Object currentObj = theTarget;
|
||||
|
||||
List<String> parts = Arrays.asList(thePath.split("\\."));
|
||||
List<T> retVal = getValues(currentDef, currentObj, parts, theWantedType);
|
||||
if (retVal.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return retVal.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, Object theCurrentObj, List<String> theSubList, Class<T> theWantedClass) {
|
||||
String name = theSubList.get(0);
|
||||
|
@ -252,105 +335,38 @@ public class FhirTerser {
|
|||
return getValues(currentDef, currentObj, subList, theWantedClass);
|
||||
}
|
||||
|
||||
private List<String> addNameToList(List<String> theCurrentList, BaseRuntimeChildDefinition theChildDefinition) {
|
||||
if (theChildDefinition == null)
|
||||
return null;
|
||||
if (theCurrentList == null || theCurrentList.isEmpty())
|
||||
return new ArrayList<String>(Arrays.asList(theChildDefinition.getElementName()));
|
||||
List<String> newList = new ArrayList<String>(theCurrentList);
|
||||
newList.add(theChildDefinition.getElementName());
|
||||
return newList;
|
||||
}
|
||||
/**
|
||||
* Returns <code>true</code> if <code>theSource</code> is in the compartment named <code>theCompartmentName</code>
|
||||
* belonging to resource <code>theTarget</code>
|
||||
*
|
||||
* @param theCompartmentName The name of the compartment
|
||||
* @param theSource The potential member of the compartment
|
||||
* @param theTarget The owner of the compartment
|
||||
* @return <code>true</code> if <code>theSource</code> is in the compartment
|
||||
*/
|
||||
public boolean isSourceInCompartmentForTarget(String theCompartmentName, IBaseResource theSource, IBaseResource theTarget) {
|
||||
Validate.notBlank(theCompartmentName, "theCompartmentName must not be null or blank");
|
||||
Validate.notNull(theSource, "theSource must not be null");
|
||||
Validate.notNull(theTarget, "theTarget must not be null");
|
||||
Validate.notBlank(theTarget.getIdElement().getIdPart(), "theTarget must have a populated ID (theTarget.getIdElement().getIdPart() does not return a value)");
|
||||
|
||||
private void visit(IdentityHashMap<Object, Object> theStack, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition,
|
||||
BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor theCallback) {
|
||||
List<String> pathToElement = addNameToList(thePathToElement, theChildDefinition);
|
||||
RuntimeResourceDefinition targetDef = myContext.getResourceDefinition(theTarget);
|
||||
String wantRef = targetDef.getName() + '/' + theTarget.getIdElement().getIdPart();
|
||||
|
||||
if (theStack.put(theElement, theElement) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
theCallback.acceptElement(theElement, pathToElement, theChildDefinition, theDefinition);
|
||||
addUndeclaredExtensions(theElement, theDefinition, theChildDefinition, theCallback);
|
||||
|
||||
BaseRuntimeElementDefinition<?> def = theDefinition;
|
||||
if (def.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) {
|
||||
def = myContext.getElementDefinition(theElement.getClass());
|
||||
}
|
||||
|
||||
switch (def.getChildType()) {
|
||||
case ID_DATATYPE:
|
||||
case PRIMITIVE_XHTML_HL7ORG:
|
||||
case PRIMITIVE_XHTML:
|
||||
case PRIMITIVE_DATATYPE:
|
||||
// These are primitive types
|
||||
break;
|
||||
case RESOURCE_REF:
|
||||
IBaseReference resRefDt = (IBaseReference) theElement;
|
||||
if (resRefDt.getReferenceElement().getValue() == null && resRefDt.getResource() != null) {
|
||||
IBaseResource theResource = resRefDt.getResource();
|
||||
if (theResource.getIdElement() == null || theResource.getIdElement().isEmpty() || theResource.getIdElement().isLocal()) {
|
||||
def = myContext.getResourceDefinition(theResource);
|
||||
visit(theStack, theResource, pathToElement, null, def, theCallback);
|
||||
RuntimeResourceDefinition sourceDef = myContext.getResourceDefinition(theSource);
|
||||
List<RuntimeSearchParam> params = sourceDef.getSearchParamsForCompartmentName(theCompartmentName);
|
||||
for (RuntimeSearchParam nextParam : params) {
|
||||
for (String nextPath : nextParam.getPathsSplit()) {
|
||||
for (IBaseReference nextValue : getValues(theSource, nextPath, IBaseReference.class)) {
|
||||
String nextRef = nextValue.getReferenceElement().toUnqualifiedVersionless().getValue();
|
||||
if (wantRef.equals(nextRef)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RESOURCE:
|
||||
case RESOURCE_BLOCK:
|
||||
case COMPOSITE_DATATYPE: {
|
||||
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def;
|
||||
for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
|
||||
List<?> values = nextChild.getAccessor().getValues(theElement);
|
||||
if (values != null) {
|
||||
for (Object nextValueObject : values) {
|
||||
IBase nextValue;
|
||||
try {
|
||||
nextValue = (IBase) nextValueObject;
|
||||
} catch (ClassCastException e) {
|
||||
String s = "Found instance of " + nextValueObject.getClass() + " - Did you set a field value to the incorrect type? Expected " + IBase.class.getName();
|
||||
throw new ClassCastException(s);
|
||||
}
|
||||
if (nextValue == null) {
|
||||
continue;
|
||||
}
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
BaseRuntimeElementDefinition<?> childElementDef;
|
||||
childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
|
||||
|
||||
if (childElementDef == null) {
|
||||
childElementDef = myContext.getElementDefinition(nextValue.getClass());
|
||||
}
|
||||
|
||||
if (nextChild instanceof RuntimeChildDirectResource) {
|
||||
// Don't descend into embedded resources
|
||||
theCallback.acceptElement(nextValue, null, nextChild, childElementDef);
|
||||
} else {
|
||||
visit(theStack, nextValue, pathToElement, nextChild, childElementDef, theCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONTAINED_RESOURCES: {
|
||||
BaseContainedDt value = (BaseContainedDt) theElement;
|
||||
for (IResource next : value.getContainedResources()) {
|
||||
def = myContext.getResourceDefinition(next);
|
||||
visit(theStack, next, pathToElement, null, def, theCallback);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONTAINED_RESOURCE_LIST:
|
||||
case EXTENSION_DECLARED:
|
||||
case UNDECL_EXT: {
|
||||
throw new IllegalStateException("state should not happen: " + def.getChildType());
|
||||
}
|
||||
}
|
||||
|
||||
theStack.remove(theElement);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void visit(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor2 theCallback, List<IBase> theContainingElementPath,
|
||||
|
@ -516,75 +532,94 @@ public class FhirTerser {
|
|||
visit(theResource, null, def, theVisitor, new ArrayList<IBase>(), new ArrayList<BaseRuntimeChildDefinition>(), new ArrayList<BaseRuntimeElementDefinition<?>>());
|
||||
}
|
||||
|
||||
public Object getSingleValueOrNull(IBase theTarget, String thePath) {
|
||||
Class<Object> wantedType = Object.class;
|
||||
private void visit(IdentityHashMap<Object, Object> theStack, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition,
|
||||
BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor theCallback) {
|
||||
List<String> pathToElement = addNameToList(thePathToElement, theChildDefinition);
|
||||
|
||||
return getSingleValueOrNull(theTarget, thePath, wantedType);
|
||||
}
|
||||
|
||||
public <T> T getSingleValueOrNull(IBase theTarget, String thePath, Class<T> theWantedType) {
|
||||
Validate.notNull(theTarget, "theTarget must not be null");
|
||||
Validate.notBlank(thePath, "thePath must not be empty");
|
||||
|
||||
BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theTarget.getClass());
|
||||
if (!(def instanceof BaseRuntimeElementCompositeDefinition)) {
|
||||
throw new IllegalArgumentException("Target is not a composite type: " + theTarget.getClass().getName());
|
||||
}
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> currentDef = (BaseRuntimeElementCompositeDefinition<?>) def;
|
||||
Object currentObj = theTarget;
|
||||
|
||||
List<String> parts = Arrays.asList(thePath.split("\\."));
|
||||
List<T> retVal = getValues(currentDef, currentObj, parts, theWantedType);
|
||||
if (retVal.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return retVal.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones all values from a source object into the equivalent fields in a target object
|
||||
* @param theSource The source object (must not be null)
|
||||
* @param theTarget The target object to copy values into (must not be null)
|
||||
* @param theIgnoreMissingFields The ignore fields in the target which do not exist (if false, an exception will be thrown if the target is unable to accept a value from the source)
|
||||
*/
|
||||
public void cloneInto(IBase theSource, IBase theTarget, boolean theIgnoreMissingFields) {
|
||||
Validate.notNull(theSource, "theSource must not be null");
|
||||
Validate.notNull(theTarget, "theTarget must not be null");
|
||||
|
||||
if (theSource instanceof IPrimitiveType<?>) {
|
||||
if (theTarget instanceof IPrimitiveType<?>) {
|
||||
((IPrimitiveType<?>)theTarget).setValueAsString(((IPrimitiveType<?>)theSource).getValueAsString());
|
||||
if (theStack.put(theElement, theElement) != null) {
|
||||
return;
|
||||
} else {
|
||||
if (theIgnoreMissingFields) {
|
||||
return;
|
||||
} else {
|
||||
throw new DataFormatException("Can not copy value from primitive of type " + theSource.getClass().getName() + " into type " + theTarget.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> sourceDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theSource.getClass());
|
||||
BaseRuntimeElementCompositeDefinition<?> targetDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theTarget.getClass());
|
||||
theCallback.acceptElement(theElement, pathToElement, theChildDefinition, theDefinition);
|
||||
addUndeclaredExtensions(theElement, theDefinition, theChildDefinition, theCallback);
|
||||
|
||||
for (BaseRuntimeChildDefinition nextChild : sourceDef.getChildren()) {
|
||||
for (IBase nextValue : nextChild.getAccessor().getValues(theSource)) {
|
||||
BaseRuntimeChildDefinition targetChild = targetDef.getChildByName(nextChild.getElementName());
|
||||
if (targetChild == null) {
|
||||
if (theIgnoreMissingFields) {
|
||||
BaseRuntimeElementDefinition<?> def = theDefinition;
|
||||
if (def.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) {
|
||||
def = myContext.getElementDefinition(theElement.getClass());
|
||||
}
|
||||
|
||||
switch (def.getChildType()) {
|
||||
case ID_DATATYPE:
|
||||
case PRIMITIVE_XHTML_HL7ORG:
|
||||
case PRIMITIVE_XHTML:
|
||||
case PRIMITIVE_DATATYPE:
|
||||
// These are primitive types
|
||||
break;
|
||||
case RESOURCE_REF:
|
||||
IBaseReference resRefDt = (IBaseReference) theElement;
|
||||
if (resRefDt.getReferenceElement().getValue() == null && resRefDt.getResource() != null) {
|
||||
IBaseResource theResource = resRefDt.getResource();
|
||||
if (theResource.getIdElement() == null || theResource.getIdElement().isEmpty() || theResource.getIdElement().isLocal()) {
|
||||
def = myContext.getResourceDefinition(theResource);
|
||||
visit(theStack, theResource, pathToElement, null, def, theCallback);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RESOURCE:
|
||||
case RESOURCE_BLOCK:
|
||||
case COMPOSITE_DATATYPE: {
|
||||
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def;
|
||||
for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
|
||||
List<?> values = nextChild.getAccessor().getValues(theElement);
|
||||
if (values != null) {
|
||||
for (Object nextValueObject : values) {
|
||||
IBase nextValue;
|
||||
try {
|
||||
nextValue = (IBase) nextValueObject;
|
||||
} catch (ClassCastException e) {
|
||||
String s = "Found instance of " + nextValueObject.getClass() + " - Did you set a field value to the incorrect type? Expected " + IBase.class.getName();
|
||||
throw new ClassCastException(s);
|
||||
}
|
||||
if (nextValue == null) {
|
||||
continue;
|
||||
}
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
BaseRuntimeElementDefinition<?> childElementDef;
|
||||
childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
|
||||
|
||||
if (childElementDef == null) {
|
||||
childElementDef = myContext.getElementDefinition(nextValue.getClass());
|
||||
}
|
||||
|
||||
if (nextChild instanceof RuntimeChildDirectResource) {
|
||||
// Don't descend into embedded resources
|
||||
theCallback.acceptElement(nextValue, null, nextChild, childElementDef);
|
||||
} else {
|
||||
throw new DataFormatException("Type " + theTarget.getClass().getName() + " does not have a child with name " + nextChild.getElementName());
|
||||
visit(theStack, nextValue, pathToElement, nextChild, childElementDef, theCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONTAINED_RESOURCES: {
|
||||
BaseContainedDt value = (BaseContainedDt) theElement;
|
||||
for (IResource next : value.getContainedResources()) {
|
||||
def = myContext.getResourceDefinition(next);
|
||||
visit(theStack, next, pathToElement, null, def, theCallback);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONTAINED_RESOURCE_LIST:
|
||||
case EXTENSION_DECLARED:
|
||||
case UNDECL_EXT: {
|
||||
throw new IllegalStateException("state should not happen: " + def.getChildType());
|
||||
}
|
||||
}
|
||||
|
||||
IBase target = targetChild.getChildByName(nextChild.getElementName()).newInstance();
|
||||
targetChild.getMutator().addValue(theTarget, target);
|
||||
cloneInto(nextValue, target, theIgnoreMissingFields);
|
||||
}
|
||||
}
|
||||
theStack.remove(theElement);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ public class SearchBuilder {
|
|||
private ISearchResultDao mySearchResultDao;
|
||||
|
||||
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, IFulltextSearchSvc theSearchDao,
|
||||
ISearchResultDao theSearchResultDao, BaseHapiFhirDao theDao, IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao) {
|
||||
ISearchResultDao theSearchResultDao, BaseHapiFhirDao<?> theDao, IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao) {
|
||||
myContext = theFhirContext;
|
||||
myEntityManager = theEntityManager;
|
||||
myPlatformTransactionManager = thePlatformTransactionManager;
|
||||
|
@ -179,7 +179,7 @@ public class SearchBuilder {
|
|||
IQueryParameterType rightValue = cp.getRightValue();
|
||||
predicates.add(createCompositeParamPart(builder, from, right, rightValue));
|
||||
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
|
@ -215,7 +215,8 @@ public class SearchBuilder {
|
|||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
|
||||
predicates.add(masterCodePredicate);
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
@ -237,8 +238,8 @@ public class SearchBuilder {
|
|||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(from.get("myId").in(thePids));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
predicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, from));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
createPredicateLastUpdatedForResourceTable(builder, from, predicates);
|
||||
|
||||
cq.where(toArray(predicates));
|
||||
|
||||
|
@ -274,7 +275,8 @@ public class SearchBuilder {
|
|||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(from.get("myLanguage").as(String.class).in(values));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
createPredicateLastUpdatedForResourceTable(builder, from, predicates);
|
||||
|
||||
predicates.add(builder.isNull(from.get("myDeleted")));
|
||||
|
||||
|
@ -366,7 +368,8 @@ public class SearchBuilder {
|
|||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
predicates.add(builder.or(toArray(codePredicates)));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
|
@ -391,7 +394,7 @@ public class SearchBuilder {
|
|||
predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.isNull(from.get("myDeleted")));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
|
@ -416,7 +419,7 @@ public class SearchBuilder {
|
|||
subQ.where(path);
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
|
||||
|
@ -504,7 +507,8 @@ public class SearchBuilder {
|
|||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
predicates.add(builder.or(toArray(codePredicates)));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
|
@ -650,7 +654,8 @@ public class SearchBuilder {
|
|||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(createResourceLinkPathPredicate(theParamName, builder, from));
|
||||
predicates.add(builder.or(toArray(codePredicates)));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("mySourceResourcePid").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("mySourceResourcePid").as(Long.class));
|
||||
createPredicateLastUpdatedForResourceLink(builder, from, predicates);
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
|
@ -684,7 +689,9 @@ public class SearchBuilder {
|
|||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
predicates.add(builder.or(toArray(codePredicates)));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
|
@ -771,7 +778,7 @@ public class SearchBuilder {
|
|||
andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin));
|
||||
}
|
||||
|
||||
doCreateIdPredicate(builder, cq, andPredicates, from.get("myResourceId").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, andPredicates, from.get("myResourceId").as(Long.class));
|
||||
Predicate masterCodePredicate = builder.and(toArray(andPredicates));
|
||||
|
||||
cq.where(masterCodePredicate);
|
||||
|
@ -821,7 +828,7 @@ public class SearchBuilder {
|
|||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
predicates.add(builder.or(toArray(codePredicates)));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
|
@ -906,7 +913,7 @@ public class SearchBuilder {
|
|||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
predicates.add(builder.or(toArray(codePredicates)));
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
|
@ -996,6 +1003,28 @@ public class SearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private void createPredicateLastUpdatedForIndexedSearchParam(CriteriaBuilder builder, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> predicates) {
|
||||
DateRangeParam lastUpdated = myParams.getLastUpdatedAndRemove();
|
||||
if (lastUpdated != null) {
|
||||
From<BaseResourceIndexedSearchParam, ResourceTable> defJoin = from.join("myResource", JoinType.INNER);
|
||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(lastUpdated, builder, defJoin);
|
||||
predicates.addAll(lastUpdatedPredicates);
|
||||
}
|
||||
}
|
||||
|
||||
private void createPredicateLastUpdatedForResourceLink(CriteriaBuilder builder, Root<ResourceLink> from, List<Predicate> predicates) {
|
||||
DateRangeParam lastUpdated = myParams.getLastUpdatedAndRemove();
|
||||
if (lastUpdated != null) {
|
||||
From<BaseResourceIndexedSearchParam, ResourceTable> defJoin = from.join("mySourceResource", JoinType.INNER);
|
||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(lastUpdated, builder, defJoin);
|
||||
predicates.addAll(lastUpdatedPredicates);
|
||||
}
|
||||
}
|
||||
|
||||
private void createPredicateLastUpdatedForResourceTable(CriteriaBuilder builder, Root<ResourceTable> from, List<Predicate> predicates) {
|
||||
predicates.addAll(createLastUpdatedPredicates(myParams.getLastUpdatedAndRemove(), builder, from));
|
||||
}
|
||||
|
||||
private Predicate createPredicateNumeric(CriteriaBuilder builder, IQueryParameterType params, ParamPrefixEnum cmpValue, BigDecimal valueValue, final Expression<BigDecimal> path,
|
||||
String invalidMessageName, String theValueString) {
|
||||
Predicate num;
|
||||
|
@ -1039,6 +1068,26 @@ public class SearchBuilder {
|
|||
return num;
|
||||
}
|
||||
|
||||
private void createPredicateResourceId(CriteriaBuilder builder, CriteriaQuery<?> cq, List<Predicate> thePredicates, Expression<Long> theExpression) {
|
||||
if (myParams.isPersistResults()) {
|
||||
if (mySearchEntity.getTotalCount() > -1) {
|
||||
Subquery<Long> subQ = cq.subquery(Long.class);
|
||||
Root<SearchResult> subQfrom = subQ.from(SearchResult.class);
|
||||
subQ.select(subQfrom.get("myResourcePid").as(Long.class));
|
||||
Predicate subQname = builder.equal(subQfrom.get("mySearch"), mySearchEntity);
|
||||
subQ.where(subQname);
|
||||
|
||||
thePredicates.add(theExpression.in(subQ));
|
||||
}
|
||||
} else {
|
||||
if (myPids != null) {
|
||||
thePredicates.add(theExpression.in(myPids));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
|
||||
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theFrom) {
|
||||
String rawSearchTerm;
|
||||
|
@ -1242,24 +1291,6 @@ public class SearchBuilder {
|
|||
createSort(theBuilder, theFrom, theSort.getChain(), theOrders, thePredicates);
|
||||
}
|
||||
|
||||
private void doCreateIdPredicate(CriteriaBuilder builder, CriteriaQuery<?> cq, List<Predicate> thePredicates, Expression<Long> theExpression) {
|
||||
if (myParams.isPersistResults()) {
|
||||
if (mySearchEntity.getTotalCount() > -1) {
|
||||
Subquery<Long> subQ = cq.subquery(Long.class);
|
||||
Root<SearchResult> subQfrom = subQ.from(SearchResult.class);
|
||||
subQ.select(subQfrom.get("myResourcePid").as(Long.class));
|
||||
Predicate subQname = builder.equal(subQfrom.get("mySearch"), mySearchEntity);
|
||||
subQ.where(subQname);
|
||||
|
||||
thePredicates.add(theExpression.in(subQ));
|
||||
}
|
||||
} else {
|
||||
if (myPids != null) {
|
||||
thePredicates.add(theExpression.in(myPids));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Long> doGetPids() {
|
||||
if (myParams.isPersistResults()) {
|
||||
HashSet<Long> retVal = new HashSet<Long>();
|
||||
|
@ -1355,7 +1386,7 @@ public class SearchBuilder {
|
|||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(theLastUpdated, builder, from);
|
||||
doCreateIdPredicate(builder, cq, lastUpdatedPredicates, from.get("myId").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, lastUpdatedPredicates, from.get("myId").as(Long.class));
|
||||
|
||||
cq.where(SearchBuilder.toArray(lastUpdatedPredicates));
|
||||
TypedQuery<Long> query = myEntityManager.createQuery(cq);
|
||||
|
@ -1382,7 +1413,7 @@ public class SearchBuilder {
|
|||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
|
||||
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
|
||||
createSort(builder, from, theParams.getSort(), orders, predicates);
|
||||
|
||||
|
@ -1426,9 +1457,6 @@ public class SearchBuilder {
|
|||
doInitializeSearch();
|
||||
|
||||
DateRangeParam lu = theParams.getLastUpdated();
|
||||
if (lu != null && lu.isEmpty()) {
|
||||
lu = null;
|
||||
}
|
||||
|
||||
// Collection<Long> loadPids;
|
||||
if (theParams.getEverythingMode() != null) {
|
||||
|
|
|
@ -115,10 +115,28 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return myIncludes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if there is no last updated value
|
||||
*/
|
||||
public DateRangeParam getLastUpdated() {
|
||||
if (myLastUpdated != null) {
|
||||
if (myLastUpdated.isEmpty()) {
|
||||
myLastUpdated = null;
|
||||
}
|
||||
}
|
||||
return myLastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if there is no last updated value, and removes the lastupdated
|
||||
* value from this map
|
||||
*/
|
||||
public DateRangeParam getLastUpdatedAndRemove() {
|
||||
DateRangeParam retVal = getLastUpdated();
|
||||
myLastUpdated = null;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public RequestDetails getRequestDetails() {
|
||||
return myRequestDetails;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,6 +4,9 @@ import static org.junit.Assert.*;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.model.api.annotation.Compartment;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
|
||||
import ca.uhn.fhir.model.dstu.resource.CarePlan;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
@ -27,9 +30,18 @@ public class ModelScannerTest {
|
|||
|
||||
RuntimeResourceDefinition def = ctx.getResourceDefinition(MyPatient.class);
|
||||
RuntimeResourceDefinition baseDef = def.getBaseDefinition();
|
||||
assertEquals(Patient.class,baseDef.getImplementingClass());
|
||||
assertEquals(Patient.class, baseDef.getImplementingClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceWithNoDef() {
|
||||
try {
|
||||
FhirContext.forDstu1().getResourceDefinition(NoResourceDef.class);
|
||||
fail();
|
||||
} catch (ConfigurationException e) {
|
||||
assertEquals("Resource class[ca.uhn.fhir.context.ModelScannerTest$NoResourceDef] does not contain any valid HAPI-FHIR annotations", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanExtensionTypes() throws DataFormatException {
|
||||
|
@ -64,6 +76,50 @@ public class ModelScannerTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchParamWithCompartmentForNonReferenceParam() {
|
||||
try {
|
||||
FhirContext.forDstu1().getResourceDefinition(CompartmentForNonReferenceParam.class);
|
||||
fail();
|
||||
} catch (ConfigurationException e) {
|
||||
assertEquals("Search param foo provides compartment membershit but is not of type 'reference'", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchParamWithInvalidType() {
|
||||
try {
|
||||
FhirContext.forDstu1().getResourceDefinition(InvalidParamType.class);
|
||||
fail();
|
||||
} catch (ConfigurationException e) {
|
||||
assertEquals("Search param foo has an invalid type: bar", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ResourceDef(name = "Patient")
|
||||
public static class CompartmentForNonReferenceParam extends Patient {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SearchParamDefinition(name = "foo", path = "Patient.telecom", type = "string", providesMembershipIn = { @Compartment(name = "Patient"), @Compartment(name = "Device") })
|
||||
public static final String SP_TELECOM = "foo";
|
||||
|
||||
}
|
||||
|
||||
@ResourceDef(name = "Patient")
|
||||
public static class InvalidParamType extends Patient {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SearchParamDefinition(name = "foo", path = "Patient.telecom", type = "bar")
|
||||
public static final String SP_TELECOM = "foo";
|
||||
|
||||
}
|
||||
|
||||
class NoResourceDef extends Patient {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SearchParamDefinition(name = "foo", path = "Patient.telecom", type = "bar")
|
||||
public static final String SP_TELECOM = "foo";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>hapi-fhir-structures-dstu2.1</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,31 @@
|
|||
package ca.uhn.fhir.model.dstu2;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
|
||||
public class CompartmentDstu2Test {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
||||
|
||||
@Test
|
||||
public void testMembership() {
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.setId("PID1");
|
||||
|
||||
Patient p2 = new Patient();
|
||||
p2.setId("PID2");
|
||||
|
||||
Observation o = new Observation();
|
||||
o.getSubject().setReference("Patient/PID1");
|
||||
|
||||
assertTrue(ourCtx.newTerser().isSourceInCompartmentForTarget("Patient", o, p1));
|
||||
assertFalse(ourCtx.newTerser().isSourceInCompartmentForTarget("Patient", o, p2));
|
||||
}
|
||||
|
||||
}
|
|
@ -55,14 +55,11 @@ public class ${className} extends ca.uhn.fhir.model.${version}.resource.BaseReso
|
|||
* Path: <b>$esc.html(${param.path})</b><br>
|
||||
* </p>
|
||||
*/
|
||||
@SearchParamDefinition(name="${param.name}", path="${param.path}", description="${param.description}", type="${param.type}" #{if}($param.compositeOf.empty == false) , compositeOf={ #{foreach}($compositeOf in $param.compositeOf) "${compositeOf}"#{if}($foreach.hasNext), #{end}#{end} } #{end} )
|
||||
#if ($param.compartments.size() > 0)
|
||||
@Compartments(providesMembershipIn={
|
||||
#foreach ($compartment in $param.compartments)
|
||||
@Compartment(name="$compartment") #{if}($foreach.hasNext), #{end}
|
||||
#end
|
||||
})
|
||||
#end
|
||||
@SearchParamDefinition(name="${param.name}", path="${param.path}", description="${param.description}", type="${param.type}" #{if}($param.compositeOf.empty == false) , compositeOf={ #{foreach}($compositeOf in $param.compositeOf) "${compositeOf}"#{if}($foreach.hasNext), #{end}#{end} } #{end}
|
||||
#{if} ($param.compartments.size() > 0), providesMembershipIn={
|
||||
#{foreach} ($compartment in $param.compartments) @Compartment(name="$compartment") #{if}($foreach.hasNext), #{end}#{end}
|
||||
}
|
||||
#{end} )
|
||||
public static final String $param.constantName = "${param.name}";
|
||||
|
||||
/**
|
||||
|
|
|
@ -235,6 +235,12 @@
|
|||
($foo) where the response from the server was a raw resource instead
|
||||
of a Parameters resource. Thanks to Andrew Michael Martin for reporting!
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA server applies _lastUpdated filter inline with other searches wherever possible
|
||||
instead of applying this filter as a second query against the results of the
|
||||
first query. This should improve performance when searching against large
|
||||
datasets.
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.4" date="2016-02-04">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue