Merge branch 'master' of github.com:jamesagnew/hapi-fhir into hl7org_structs

This commit is contained in:
James Agnew 2015-05-02 14:38:14 -07:00
commit ab7b1c3a4f
37 changed files with 963 additions and 319 deletions

View File

@ -59,4 +59,16 @@ public interface IQueryParameterType {
*/ */
public String getQueryParameterQualifier(); public String getQueryParameterQualifier();
/**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale
* instead of a normal value
*/
Boolean getMissing();
/**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale
* instead of a normal value
*/
void setMissing(Boolean theMissing);
} }

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.param.TokenParam;
public abstract class BaseCodingDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType { public abstract class BaseCodingDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType {
@ -172,4 +173,28 @@ public abstract class BaseCodingDt extends BaseIdentifiableElement implements IC
public abstract BaseCodingDt setSystem(String theUri); public abstract BaseCodingDt setSystem(String theUri);
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link TokenParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
return null;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link TokenParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
} }

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.param.StringParam;
public abstract class BaseIdentifierDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType { public abstract class BaseIdentifierDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType {
@ -112,4 +113,29 @@ public abstract class BaseIdentifierDt extends BaseIdentifiableElement implement
} }
} }
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
return null;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
} }

View File

@ -33,6 +33,8 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.StringParam;
public abstract class BaseQuantityDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType { public abstract class BaseQuantityDt extends BaseIdentifiableElement implements ICompositeDatatype, IQueryParameterType {
@ -208,4 +210,30 @@ public abstract class BaseQuantityDt extends BaseIdentifiableElement implements
* </p> * </p>
*/ */
public abstract DecimalDt getValueElement(); public abstract DecimalDt getValueElement();
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link QuantityParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
return null;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link QuantityParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
} }

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter; import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.rest.param.StringParam;
@DatatypeDef(name = "string") @DatatypeDef(name = "string")
public class StringDt extends BasePrimitive<String> implements IQueryParameterType { public class StringDt extends BasePrimitive<String> implements IQueryParameterType {
@ -122,4 +123,28 @@ public class StringDt extends BasePrimitive<String> implements IQueryParameterTy
return theValue; return theValue;
} }
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
return null;
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
} }

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.rest.server.Constants;
abstract class BaseClientParam implements IParam {
@Override
public ICriterion<?> isMissing(boolean theMissing) {
return new MissingCriterion(theMissing ? Constants.PARAMQUALIFIER_MISSING_TRUE : Constants.PARAMQUALIFIER_MISSING_FALSE);
}
private class MissingCriterion implements ICriterion<IParam>, ICriterionInternal
{
private String myParameterValue;
public MissingCriterion(String theParameterValue) {
myParameterValue = theParameterValue;
}
@Override
public String getParameterValue() {
return myParameterValue;
}
@Override
public String getParameterName() {
return BaseClientParam.this.getParamName() + Constants.PARAMQUALIFIER_MISSING;
}
}
}

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.rest.gclient;
/** /**
* Composite parameter type for use in fluent client interfaces * Composite parameter type for use in fluent client interfaces
*/ */
public class CompositeClientParam<A extends IParam, B extends IParam> implements IParam { public class CompositeClientParam<A extends IParam, B extends IParam> extends BaseClientParam implements IParam {
private String myName; private String myName;

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.model.primitive.DateTimeDt;
/** /**
* Date parameter type for use in fluent client interfaces * Date parameter type for use in fluent client interfaces
*/ */
public class DateClientParam implements IParam { public class DateClientParam extends BaseClientParam implements IParam {
private String myParamName; private String myParamName;

View File

@ -22,6 +22,16 @@ package ca.uhn.fhir.rest.gclient;
public interface IParam { public interface IParam {
/**
* Returns the name of this parameter
*/
String getParamName(); String getParamName();
/**
* Sets the <code>:missing</code> qualifier for this parameter. Set this to <code>true</code>
* to indicate that the server should return resources with this value <p>populated</p>. Set this to
* <code>false</code> to indicate that the server should return resources with this value <b>missing</b>.
*/
ICriterion<?> isMissing(boolean theMissing);
} }

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.rest.gclient;
/** /**
* Token parameter type for use in fluent client interfaces * Token parameter type for use in fluent client interfaces
*/ */
public class NumberClientParam implements IParam { public class NumberClientParam extends BaseClientParam implements IParam {
private String myParamName; private String myParamName;

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.rest.gclient.NumberClientParam.IMatches;
/** /**
* Token parameter type for use in fluent client interfaces * Token parameter type for use in fluent client interfaces
*/ */
public class QuantityClientParam implements IParam { public class QuantityClientParam extends BaseClientParam implements IParam {
private String myParamName; private String myParamName;

View File

@ -23,7 +23,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
*/ */
public class ReferenceClientParam implements IParam { public class ReferenceClientParam extends BaseClientParam implements IParam {
private String myName; private String myName;

View File

@ -31,7 +31,7 @@ import ca.uhn.fhir.rest.server.Constants;
* @author james * @author james
* *
*/ */
public class StringClientParam implements IParam { public class StringClientParam extends BaseClientParam implements IParam {
private final String myParamName; private final String myParamName;

View File

@ -31,7 +31,7 @@ import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
/** /**
* Token parameter type for use in fluent client interfaces * Token parameter type for use in fluent client interfaces
*/ */
public class TokenClientParam implements IParam { public class TokenClientParam extends BaseClientParam implements IParam {
private String myParamName; private String myParamName;

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
/** /**
* *
*/ */
public class UriClientParam implements IParam { public class UriClientParam extends BaseClientParam implements IParam {
//TODO: handle :above and :below //TODO: handle :above and :below

View File

@ -26,54 +26,76 @@ import ca.uhn.fhir.rest.server.Constants;
/** /**
* Base class for RESTful operation parameter types * Base class for RESTful operation parameter types
*/ */
public class BaseParam implements IQueryParameterType { abstract class BaseParam implements IQueryParameterType {
private Boolean myMissing;
private Boolean myMissing;
/** /**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale * If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale instead of a normal value
* instead of a normal value
*/ */
@Override
public Boolean getMissing() { public Boolean getMissing() {
return myMissing; return myMissing;
} }
@Override @Override
public String getQueryParameterQualifier() { public final String getQueryParameterQualifier() {
if (myMissing != null) { if (myMissing != null) {
return Constants.PARAMQUALIFIER_MISSING; return Constants.PARAMQUALIFIER_MISSING;
} }
return null; return doGetQueryParameterQualifier();
} }
abstract String doGetQueryParameterQualifier();
abstract String doGetValueAsQueryToken();
@Override @Override
public String getValueAsQueryToken() { public final String getValueAsQueryToken() {
if (myMissing != null) { if (myMissing != null) {
return myMissing ? "true" : "false"; return myMissing ? Constants.PARAMQUALIFIER_MISSING_TRUE : Constants.PARAMQUALIFIER_MISSING_FALSE;
} }
return null; return doGetValueAsQueryToken();
} }
/** /**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale * If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale instead of a normal value
* instead of a normal value
*/ */
@Override
public void setMissing(Boolean theMissing) { public void setMissing(Boolean theMissing) {
myMissing = theMissing; myMissing = theMissing;
} }
@Override @Override
public void setValueAsQueryToken(String theQualifier, String theValue) { public final void setValueAsQueryToken(String theQualifier, String theValue) {
if (Constants.PARAMQUALIFIER_MISSING.equals(theQualifier)) { if (Constants.PARAMQUALIFIER_MISSING.equals(theQualifier)) {
myMissing = "true".equals(theValue); myMissing = "true".equals(theValue);
doSetValueAsQueryToken(null, null);
} else { } else {
myMissing = null; myMissing = null;
doSetValueAsQueryToken(theQualifier, theValue);
} }
} }
abstract void doSetValueAsQueryToken(String theQualifier, String theValue);
static class ComposableBaseParam extends BaseParam{
@Override
String doGetQueryParameterQualifier() {
return null;
}
@Override
String doGetValueAsQueryToken() {
return null;
}
@Override
void doSetValueAsQueryToken(String theQualifier, String theValue) {
// nothing
}
}
} }

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class CompositeParam<A extends IQueryParameterType, B extends IQueryParameterType> implements IQueryParameterType { public class CompositeParam<A extends IQueryParameterType, B extends IQueryParameterType> extends BaseParam implements IQueryParameterType {
private A myLeftType; private A myLeftType;
private B myRightType; private B myRightType;
@ -59,27 +59,13 @@ public class CompositeParam<A extends IQueryParameterType, B extends IQueryParam
} }
} }
/**
* @return Returns the left value for this parameter (the first of two parameters in this composite)
*/
public A getLeftValue() {
return myLeftType;
}
@Override @Override
public String getQueryParameterQualifier() { String doGetQueryParameterQualifier() {
return null; return null;
} }
/**
* @return Returns the right value for this parameter (the second of two parameters in this composite)
*/
public B getRightValue() {
return myRightType;
}
@Override @Override
public String getValueAsQueryToken() { String doGetValueAsQueryToken() {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
if (myLeftType != null) { if (myLeftType != null) {
b.append(myLeftType.getValueAsQueryToken()); b.append(myLeftType.getValueAsQueryToken());
@ -92,7 +78,7 @@ public class CompositeParam<A extends IQueryParameterType, B extends IQueryParam
} }
@Override @Override
public void setValueAsQueryToken(String theQualifier, String theValue) { void doSetValueAsQueryToken(String theQualifier, String theValue) {
if (isBlank(theValue)) { if (isBlank(theValue)) {
myLeftType.setValueAsQueryToken(theQualifier, ""); myLeftType.setValueAsQueryToken(theQualifier, "");
myRightType.setValueAsQueryToken(theQualifier, ""); myRightType.setValueAsQueryToken(theQualifier, "");
@ -108,4 +94,18 @@ public class CompositeParam<A extends IQueryParameterType, B extends IQueryParam
} }
} }
/**
* @return Returns the left value for this parameter (the first of two parameters in this composite)
*/
public A getLeftValue() {
return myLeftType;
}
/**
* @return Returns the right value for this parameter (the second of two parameters in this composite)
*/
public B getRightValue() {
return myRightType;
}
} }

View File

@ -36,7 +36,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateParam extends DateTimeDt implements IQueryParameterType, IQueryParameterOr<DateParam> { public class DateParam extends DateTimeDt implements IQueryParameterType, IQueryParameterOr<DateParam> {
private QuantityCompararatorEnum myComparator; private QuantityCompararatorEnum myComparator;
private BaseParam myBase=new BaseParam(); private BaseParam myBase=new BaseParam.ComposableBaseParam();
/** /**
* Constructor * Constructor
@ -191,4 +191,14 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
return new DateTimeDt(getValueAsString()); return new DateTimeDt(getValueAsString());
} }
@Override
public Boolean getMissing() {
return myBase.getMissing();
}
@Override
public void setMissing(Boolean theMissing) {
myBase.setMissing(theMissing);
}
} }

View File

@ -365,6 +365,16 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype
return getDisplay(); return getDisplay();
} }
@Override
public Boolean getMissing() {
throw new UnsupportedOperationException();
}
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException();
}

View File

@ -414,6 +414,16 @@ class InternalQuantityDt
return this; return this;
} }
@Override
public Boolean getMissing() {
throw new UnsupportedOperationException();
}
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException();
}

View File

@ -45,32 +45,24 @@ public class NumberParam extends BaseParam implements IQueryParameterType {
} }
@Override @Override
public String toString() { String doGetQueryParameterQualifier() {
return null;
}
@Override
String doGetValueAsQueryToken() {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append(getClass().getSimpleName());
b.append("[");
if (myQuantity.getComparatorElement().isEmpty() == false) { if (myQuantity.getComparatorElement().isEmpty() == false) {
b.append(myQuantity.getComparatorElement().getValue()); b.append(myQuantity.getComparatorElement().getValue());
} }
if (myQuantity.getValueElement().isEmpty() == false) { if (myQuantity.getValueElement().isEmpty() == false) {
b.append(myQuantity.getValueElement().toString()); b.append(myQuantity.getValueElement().toString());
} }
b.append("]");
return b.toString(); return b.toString();
} }
public QuantityCompararatorEnum getComparator() {
return myQuantity.getComparatorElement().getValueAsEnum();
}
public BigDecimal getValue() {
return myQuantity.getValueElement().getValue();
}
@Override @Override
public void setValueAsQueryToken(String theQualifier, String theValue) { void doSetValueAsQueryToken(String theQualifier, String theValue) {
super.setValueAsQueryToken(theQualifier, theValue);
if (getMissing() != null && isBlank(theValue)) { if (getMissing() != null && isBlank(theValue)) {
return; return;
} }
@ -91,26 +83,29 @@ public class NumberParam extends BaseParam implements IQueryParameterType {
myQuantity.setValue(new BigDecimal(theValue)); myQuantity.setValue(new BigDecimal(theValue));
} }
} }
public QuantityCompararatorEnum getComparator() {
return myQuantity.getComparatorElement().getValueAsEnum();
}
public BigDecimal getValue() {
return myQuantity.getValueElement().getValue();
}
@Override @Override
public String getValueAsQueryToken() { public String toString() {
if (getMissing() != null) {
return super.getQueryParameterQualifier();
}
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append(getClass().getSimpleName());
b.append("[");
if (myQuantity.getComparatorElement().isEmpty() == false) { if (myQuantity.getComparatorElement().isEmpty() == false) {
b.append(myQuantity.getComparatorElement().getValue()); b.append(myQuantity.getComparatorElement().getValue());
} }
if (myQuantity.getValueElement().isEmpty() == false) { if (myQuantity.getValueElement().isEmpty() == false) {
b.append(myQuantity.getValueElement().toString()); b.append(myQuantity.getValueElement().toString());
} }
b.append("]");
return b.toString(); return b.toString();
} }
@Override
public String getQueryParameterQualifier() {
return super.getQueryParameterQualifier();
}
} }

View File

@ -142,33 +142,13 @@ public class QuantityParam extends BaseParam implements IQueryParameterType {
myApproximate = false; myApproximate = false;
} }
public QuantityCompararatorEnum getComparator() { @Override
return myQuantity.getComparatorElement().getValueAsEnum(); String doGetQueryParameterQualifier() {
return null;
} }
@Override @Override
public String getQueryParameterQualifier() { String doGetValueAsQueryToken() {
return super.getQueryParameterQualifier();
}
public UriDt getSystem() {
return myQuantity.getSystemElement();
}
public String getUnits() {
return myQuantity.getUnitsElement().getValue();
}
public DecimalDt getValue() {
return myQuantity.getValueElement();
}
@Override
public String getValueAsQueryToken() {
if (super.getMissing() != null) {
return super.getValueAsQueryToken();
}
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
if (myApproximate) { if (myApproximate) {
b.append('~'); b.append('~');
@ -191,6 +171,62 @@ public class QuantityParam extends BaseParam implements IQueryParameterType {
return b.toString(); return b.toString();
} }
@Override
void doSetValueAsQueryToken(String theQualifier, String theValue) {
clear();
if (theValue == null) {
return;
}
List<String> parts = ParameterUtil.splitParameterString(theValue, '|', true);
if (parts.size() > 0 && StringUtils.isNotBlank(parts.get(0))) {
if (parts.get(0).startsWith("~")) {
myQuantity.setComparator((QuantityCompararatorEnum) null);
myApproximate = true;
myQuantity.setValue(new BigDecimal(parts.get(0).substring(1)));
} else if (parts.get(0).startsWith("<=")) {
myQuantity.setComparator(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(2)));
} else if (parts.get(0).startsWith("<")) {
myQuantity.setComparator(QuantityCompararatorEnum.LESSTHAN);
String valStr = parts.get(0).substring(1);
myQuantity.setValue(new BigDecimal(valStr));
} else if (parts.get(0).startsWith(">=")) {
myQuantity.setComparator(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(2)));
} else if (parts.get(0).startsWith(">")) {
myQuantity.setComparator(QuantityCompararatorEnum.GREATERTHAN);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(1)));
} else {
myQuantity.setValue(new BigDecimal(parts.get(0)));
}
}
if (parts.size() > 1 && StringUtils.isNotBlank(parts.get(1))) {
myQuantity.setSystem(parts.get(1));
}
if (parts.size() > 2 && StringUtils.isNotBlank(parts.get(2))) {
myQuantity.setUnits(parts.get(2));
}
}
public QuantityCompararatorEnum getComparator() {
return myQuantity.getComparatorElement().getValueAsEnum();
}
public UriDt getSystem() {
return myQuantity.getSystemElement();
}
public String getUnits() {
return myQuantity.getUnitsElement().getValue();
}
public DecimalDt getValue() {
return myQuantity.getValueElement();
}
public boolean isApproximate() { public boolean isApproximate() {
return myApproximate; return myApproximate;
} }
@ -253,51 +289,6 @@ public class QuantityParam extends BaseParam implements IQueryParameterType {
return this; return this;
} }
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
clear();
super.setValueAsQueryToken(theQualifier, theValue);
if (getMissing() != null) {
return;
}
if (theValue == null) {
return;
}
List<String> parts = ParameterUtil.splitParameterString(theValue, '|', true);
if (parts.size() > 0 && StringUtils.isNotBlank(parts.get(0))) {
if (parts.get(0).startsWith("~")) {
myQuantity.setComparator((QuantityCompararatorEnum) null);
myApproximate = true;
myQuantity.setValue(new BigDecimal(parts.get(0).substring(1)));
} else if (parts.get(0).startsWith("<=")) {
myQuantity.setComparator(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(2)));
} else if (parts.get(0).startsWith("<")) {
myQuantity.setComparator(QuantityCompararatorEnum.LESSTHAN);
String valStr = parts.get(0).substring(1);
myQuantity.setValue(new BigDecimal(valStr));
} else if (parts.get(0).startsWith(">=")) {
myQuantity.setComparator(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(2)));
} else if (parts.get(0).startsWith(">")) {
myQuantity.setComparator(QuantityCompararatorEnum.GREATERTHAN);
myQuantity.setValue(new BigDecimal(parts.get(0).substring(1)));
} else {
myQuantity.setValue(new BigDecimal(parts.get(0)));
}
}
if (parts.size() > 1 && StringUtils.isNotBlank(parts.get(1))) {
myQuantity.setSystem(parts.get(1));
}
if (parts.size() > 2 && StringUtils.isNotBlank(parts.get(2))) {
myQuantity.setUnits(parts.get(2));
}
}
@Override @Override
public String toString() { public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
public class ReferenceParam extends IdDt implements IQueryParameterType { public class ReferenceParam extends IdDt implements IQueryParameterType {
private String myChain; private String myChain;
private BaseParam myBase=new BaseParam(); private BaseParam myBase=new BaseParam.ComposableBaseParam();
public ReferenceParam() { public ReferenceParam() {
} }
@ -206,4 +206,14 @@ public class ReferenceParam extends IdDt implements IQueryParameterType {
return retVal; return retVal;
} }
@Override
public Boolean getMissing() {
return myBase.getMissing();
}
@Override
public void setMissing(Boolean theMissing) {
myBase.setMissing(theMissing);
}
} }

View File

@ -48,23 +48,35 @@ public class StringParam extends BaseParam implements IQueryParameterType {
} }
@Override @Override
public String getQueryParameterQualifier() { String doGetQueryParameterQualifier() {
if (getMissing() != null) { if (isExact()) {
return super.getQueryParameterQualifier();
}else if (isExact()) {
return Constants.PARAMQUALIFIER_STRING_EXACT; return Constants.PARAMQUALIFIER_STRING_EXACT;
} else { } else {
return null; return null;
} }
} }
@Override
String doGetValueAsQueryToken() {
return ParameterUtil.escape(myValue);
}
@Override
void doSetValueAsQueryToken(String theQualifier, String theValue) {
if (Constants.PARAMQUALIFIER_STRING_EXACT.equals(theQualifier)) {
setExact(true);
} else {
setExact(false);
}
myValue = ParameterUtil.unescape(theValue);
}
public String getValue() { public String getValue() {
return myValue; return myValue;
} }
@Override public StringDt getValueAsStringDt() {
public String getValueAsQueryToken() { return new StringDt(myValue);
return ParameterUtil.escape(myValue);
} }
public String getValueNotNull() { public String getValueNotNull() {
@ -87,16 +99,6 @@ public class StringParam extends BaseParam implements IQueryParameterType {
myValue = theValue; myValue = theValue;
} }
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
if (Constants.PARAMQUALIFIER_STRING_EXACT.equals(theQualifier)) {
setExact(true);
} else {
setExact(false);
}
myValue = ParameterUtil.unescape(theValue);
}
@Override @Override
public String toString() { public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -107,8 +109,4 @@ public class StringParam extends BaseParam implements IQueryParameterType {
return builder.toString(); return builder.toString();
} }
public StringDt getValueAsStringDt() {
return new StringDt(myValue);
}
} }

View File

@ -41,21 +41,6 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
public TokenParam() { public TokenParam() {
} }
public TokenParam(String theSystem, String theValue) {
setSystem(theSystem);
setValue(theValue);
}
public TokenParam(String theSystem, String theValue, boolean theText) {
if (theText && isNotBlank(theSystem)) {
throw new IllegalArgumentException(
"theSystem can not be non-blank if theText is true (:text searches do not include a system). In other words, set the first parameter to null for a text search");
}
setSystem(theSystem);
setValue(theValue);
setText(theText);
}
/** /**
* Constructor which copies the {@link InternalCodingDt#getSystemElement() system} and {@link InternalCodingDt#getCodeElement() code} from a {@link InternalCodingDt} instance and adds it as a parameter * Constructor which copies the {@link InternalCodingDt#getSystemElement() system} and {@link InternalCodingDt#getCodeElement() code} from a {@link InternalCodingDt} instance and adds it as a parameter
* *
@ -76,21 +61,56 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
this(toSystemValue(theIdentifierDt.getSystemElement()), theIdentifierDt.getValueElement().getValue()); this(toSystemValue(theIdentifierDt.getSystemElement()), theIdentifierDt.getValueElement().getValue());
} }
private static String toSystemValue(UriDt theSystem) { public TokenParam(String theSystem, String theValue) {
return theSystem.getValueAsString(); setSystem(theSystem);
setValue(theValue);
}
public TokenParam(String theSystem, String theValue, boolean theText) {
if (theText && isNotBlank(theSystem)) {
throw new IllegalArgumentException(
"theSystem can not be non-blank if theText is true (:text searches do not include a system). In other words, set the first parameter to null for a text search");
}
setSystem(theSystem);
setValue(theValue);
setText(theText);
} }
@Override @Override
public String getQueryParameterQualifier() { String doGetQueryParameterQualifier() {
if (getMissing() != null) { if (isText()) {
return super.getQueryParameterQualifier();
} else if (isText()) {
return Constants.PARAMQUALIFIER_TOKEN_TEXT; return Constants.PARAMQUALIFIER_TOKEN_TEXT;
} else { } else {
return null; return null;
} }
} }
/**
* {@inheritDoc}
*/
@Override
String doGetValueAsQueryToken() {
if (getSystem() != null) {
return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|' + ParameterUtil.escape(getValue());
} else {
return ParameterUtil.escape(getValue());
}
}
/**
* {@inheritDoc}
*/
@Override
void doSetValueAsQueryToken(String theQualifier, String theParameter) {
int barIndex = ParameterUtil.nonEscapedIndexOf(theParameter, '|');
if (barIndex != -1) {
setSystem(theParameter.substring(0, barIndex));
setValue(ParameterUtil.unescape(theParameter.substring(barIndex + 1)));
} else {
setValue(ParameterUtil.unescape(theParameter));
}
}
public String getSystem() { public String getSystem() {
return mySystem; return mySystem;
} }
@ -99,18 +119,8 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
return myValue; return myValue;
} }
/** public InternalCodingDt getValueAsCoding() {
* {@inheritDoc} return new InternalCodingDt(mySystem, myValue);
*/
@Override
public String getValueAsQueryToken() {
if (getMissing() != null) {
return super.getValueAsQueryToken();
} else if (getSystem() != null) {
return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|' + ParameterUtil.escape(getValue());
} else {
return ParameterUtil.escape(getValue());
}
} }
public String getValueNotNull() { public String getValueNotNull() {
@ -137,25 +147,6 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
myValue = theValue; myValue = theValue;
} }
/**
* {@inheritDoc}
*/
@Override
public void setValueAsQueryToken(String theQualifier, String theParameter) {
super.setValueAsQueryToken(theQualifier, theParameter);
if (getMissing() != null) {
return;
}
int barIndex = ParameterUtil.nonEscapedIndexOf(theParameter, '|');
if (barIndex != -1) {
setSystem(theParameter.substring(0, barIndex));
setValue(ParameterUtil.unescape(theParameter.substring(barIndex + 1)));
} else {
setValue(ParameterUtil.unescape(theParameter));
}
}
@Override @Override
public String toString() { public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -170,8 +161,8 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
return builder.toString(); return builder.toString();
} }
public InternalCodingDt getValueAsCoding() { private static String toSystemValue(UriDt theSystem) {
return new InternalCodingDt(mySystem, myValue); return theSystem.getValueAsString();
} }
} }

View File

@ -42,21 +42,30 @@ public class UriParam extends BaseParam implements IQueryParameterType {
} }
@Override @Override
public String getQueryParameterQualifier() { String doGetQueryParameterQualifier() {
if (getMissing() != null) { return null;
return super.getQueryParameterQualifier(); }
} else {
return null; @Override
} String doGetValueAsQueryToken() {
return ParameterUtil.escape(myValue);
}
@Override
void doSetValueAsQueryToken(String theQualifier, String theValue) {
myValue = ParameterUtil.unescape(theValue);
} }
public String getValue() { public String getValue() {
return myValue; return myValue;
} }
@Override public StringDt getValueAsStringDt() {
public String getValueAsQueryToken() { return new StringDt(myValue);
return ParameterUtil.escape(myValue); }
public UriDt getValueAsUriDt() {
return new UriDt(myValue);
} }
public String getValueNotNull() { public String getValueNotNull() {
@ -71,11 +80,6 @@ public class UriParam extends BaseParam implements IQueryParameterType {
myValue = theValue; myValue = theValue;
} }
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
myValue = ParameterUtil.unescape(theValue);
}
@Override @Override
public String toString() { public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -83,12 +87,4 @@ public class UriParam extends BaseParam implements IQueryParameterType {
return builder.toString(); return builder.toString();
} }
public StringDt getValueAsStringDt() {
return new StringDt(myValue);
}
public UriDt getValueAsUriDt() {
return new UriDt(myValue);
}
} }

View File

@ -124,6 +124,8 @@ public class Constants {
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501; public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
public static final String URL_TOKEN_HISTORY = "_history"; public static final String URL_TOKEN_HISTORY = "_history";
public static final String URL_TOKEN_METADATA = "metadata"; public static final String URL_TOKEN_METADATA = "metadata";
public static final String PARAMQUALIFIER_MISSING_TRUE = "true";
public static final String PARAMQUALIFIER_MISSING_FALSE = "false";
static { static {
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>(); Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -42,4 +42,5 @@ ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.transactionInvalidUrl=Unable to perform {0
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.failedToCreateWithClientAssignedNumericId=Can not create resource with ID[{0}], no resource with this ID exists and clients may only assign IDs which begin with a non-numeric character on this server ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.failedToCreateWithClientAssignedNumericId=Can not create resource with ID[{0}], no resource with this ID exists and clients may only assign IDs which begin with a non-numeric character on this server
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.failedToCreateWithClientAssignedId=Can not create resource with ID[{0}], ID must not be supplied on a create (POST) operation ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.failedToCreateWithClientAssignedId=Can not create resource with ID[{0}], ID must not be supplied on a create (POST) operation
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.multipleParamsWithSameNameOneIsMissingTrue=This server does not know how to handle multiple "{0}" parameters where one has a value of :missing=true
ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.unableToDeleteNotFound=Unable to find resource matching URL "{0}". Deletion failed. ca.uhn.fhir.jpa.dao.BaseFhirResourceDao.unableToDeleteNotFound=Unable to find resource matching URL "{0}". Deletion failed.

View File

@ -425,16 +425,14 @@ public abstract class BaseFhirDao implements IDao {
// Get list of IDs // Get list of IDs
searchHistoryCurrentVersion(theResourceName, theId, theSince, end.getValue(), limit, tuples); searchHistoryCurrentVersion(theResourceName, theId, theSince, end.getValue(), limit, tuples);
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated());
ourLog.info("Retrieved {} history IDs from current versions in {} ms", tuples.size(), timer.getMillisAndRestart()); ourLog.info("Retrieved {} history IDs from current versions in {} ms", tuples.size(), timer.getMillisAndRestart());
searchHistoryHistory(theResourceName, theId, theSince, end.getValue(), limit, tuples); searchHistoryHistory(theResourceName, theId, theSince, end.getValue(), limit, tuples);
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated());
ourLog.info("Retrieved {} history IDs from previous versions in {} ms", tuples.size(), timer.getMillisAndRestart()); ourLog.info("Retrieved {} history IDs from previous versions in {} ms", tuples.size(), timer.getMillisAndRestart());
// Sort merged list // Sort merged list
Collections.sort(tuples, Collections.reverseOrder()); Collections.sort(tuples, Collections.reverseOrder());
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated()); assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated()) : tuples.toString();
return new IBundleProvider() { return new IBundleProvider() {

View File

@ -51,6 +51,7 @@ import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path; import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -72,6 +73,7 @@ import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.entity.BaseTag; import ca.uhn.fhir.jpa.entity.BaseTag;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
@ -141,6 +143,8 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
private String mySecondaryPrimaryKeyParamName; private String mySecondaryPrimaryKeyParamName;
private Set<Long> addPredicateComposite(RuntimeSearchParam theParamDef, Set<Long> thePids, List<? extends IQueryParameterType> theNextAnd) { private Set<Long> addPredicateComposite(RuntimeSearchParam theParamDef, Set<Long> thePids, List<? extends IQueryParameterType> theNextAnd) {
// TODO: fail if missing is set for a composite query
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> from = cq.from(ResourceTable.class); Root<ResourceTable> from = cq.from(ResourceTable.class);
@ -178,6 +182,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return thePids; return thePids;
} }
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsDate", theParamName, ResourceIndexedSearchParamDate.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamDate> from = cq.from(ResourceIndexedSearchParamDate.class); Root<ResourceIndexedSearchParamDate> from = cq.from(ResourceIndexedSearchParamDate.class);
@ -185,6 +193,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
List<Predicate> codePredicates = new ArrayList<Predicate>(); List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) { for (IQueryParameterType nextOr : theList) {
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
IQueryParameterType params = nextOr; IQueryParameterType params = nextOr;
Predicate p = createPredicateDate(builder, from, params); Predicate p = createPredicateDate(builder, from, params);
codePredicates.add(p); codePredicates.add(p);
@ -205,50 +217,6 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
private Predicate addPredicateDateFromRange(CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> theFrom, DateRangeParam theRange) {
Date lowerBound = theRange.getLowerBoundAsInstant();
Date upperBound = theRange.getUpperBoundAsInstant();
Predicate lb = null;
if (lowerBound != null) {
Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueLow"), lowerBound);
Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueHigh"), lowerBound);
lb = theBuilder.or(gt, lt);
// Predicate gin = builder.isNull(from.get("myValueLow"));
// Predicate lbo = builder.or(gt, gin);
// Predicate lin = builder.isNull(from.get("myValueHigh"));
// Predicate hbo = builder.or(lt, lin);
// lb = builder.and(lbo, hbo);
}
Predicate ub = null;
if (upperBound != null) {
Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueLow"), upperBound);
Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueHigh"), upperBound);
ub = theBuilder.or(gt, lt);
// Predicate gin = builder.isNull(from.get("myValueLow"));
// Predicate lbo = builder.or(gt, gin);
// Predicate lin = builder.isNull(from.get("myValueHigh"));
// Predicate ubo = builder.or(lt, lin);
// ub = builder.and(ubo, lbo);
}
if (lb != null && ub != null) {
return (theBuilder.and(lb, ub));
} else if (lb != null) {
return (lb);
} else {
return (ub);
}
}
// private Set<Long> addPredicateComposite(String theParamName, Set<Long> thePids, List<? extends
// IQueryParameterType> theList) {
// }
private Set<Long> addPredicateId(Set<Long> theExistingPids, Set<Long> thePids) { private Set<Long> addPredicateId(Set<Long> theExistingPids, Set<Long> thePids) {
if (thePids == null || thePids.isEmpty()) { if (thePids == null || thePids.isEmpty()) {
return Collections.emptySet(); return Collections.emptySet();
@ -273,6 +241,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return found; return found;
} }
// private Set<Long> addPredicateComposite(String theParamName, Set<Long> thePids, List<? extends
// IQueryParameterType> theList) {
// }
private Set<Long> addPredicateLanguage(Set<Long> thePids, List<List<? extends IQueryParameterType>> theList) { private Set<Long> addPredicateLanguage(Set<Long> thePids, List<List<? extends IQueryParameterType>> theList) {
if (theList == null || theList.isEmpty()) { if (theList == null || theList.isEmpty()) {
return thePids; return thePids;
@ -323,6 +295,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return thePids; return thePids;
} }
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsNumber", theParamName, ResourceIndexedSearchParamNumber.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamNumber> from = cq.from(ResourceIndexedSearchParamNumber.class); Root<ResourceIndexedSearchParamNumber> from = cq.from(ResourceIndexedSearchParamNumber.class);
@ -332,6 +308,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
for (IQueryParameterType nextOr : theList) { for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr; IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
if (params instanceof NumberParam) { if (params instanceof NumberParam) {
NumberParam param = (NumberParam) params; NumberParam param = (NumberParam) params;
@ -385,10 +365,68 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
private Set<Long> addPredicateParamMissing(Set<Long> thePids, String joinName, String theParamName, Class<? extends BaseResourceIndexedSearchParam> theParamTable) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class));
Subquery<Long> subQ = cq.subquery(Long.class);
Root<? extends BaseResourceIndexedSearchParam> subQfrom = subQ.from(theParamTable);
subQ.select(subQfrom.get("myResourcePid").as(Long.class));
subQ.where(builder.equal(subQfrom.get("myParamName"), theParamName));
Predicate joinPredicate = builder.not(builder.in(from.get("myId")).value(subQ));
if (thePids.size() > 0) {
Predicate inPids = (from.get("myId").in(thePids));
cq.where(builder.and(inPids, joinPredicate));
} else {
cq.where(joinPredicate);
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
List<Long> resultList = q.getResultList();
HashSet<Long> retVal = new HashSet<Long>(resultList);
return retVal;
}
private Set<Long> addPredicateParamMissingResourceLink(Set<Long> thePids, String joinName, String theParamName) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class));
Subquery<Long> subQ = cq.subquery(Long.class);
Root<ResourceLink> subQfrom = subQ.from(ResourceLink.class);
subQ.select(subQfrom.get("mySourceResourcePid").as(Long.class));
// subQ.where(builder.equal(subQfrom.get("myParamName"), theParamName));
subQ.where(createResourceLinkPathPredicate(theParamName, builder, subQfrom));
Predicate joinPredicate = builder.not(builder.in(from.get("myId")).value(subQ));
if (thePids.size() > 0) {
Predicate inPids = (from.get("myId").in(thePids));
cq.where(builder.and(inPids, joinPredicate));
} else {
cq.where(joinPredicate);
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
List<Long> resultList = q.getResultList();
HashSet<Long> retVal = new HashSet<Long>(resultList);
return retVal;
}
private Set<Long> addPredicateQuantity(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { private Set<Long> addPredicateQuantity(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
if (theList == null || theList.isEmpty()) { if (theList == null || theList.isEmpty()) {
return thePids; return thePids;
} }
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsQuantity", theParamName, ResourceIndexedSearchParamQuantity.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
@ -399,6 +437,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
for (IQueryParameterType nextOr : theList) { for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr; IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
String systemValue; String systemValue;
String unitsValue; String unitsValue;
QuantityCompararatorEnum cmpValue; QuantityCompararatorEnum cmpValue;
@ -500,6 +542,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return pidsToRetain; return pidsToRetain;
} }
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissingResourceLink(thePids, "myResourceLinks", theParamName);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceLink> from = cq.from(ResourceLink.class); Root<ResourceLink> from = cq.from(ResourceLink.class);
@ -510,6 +556,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
for (IQueryParameterType nextOr : theList) { for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr; IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresentForResourceLink(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
if (params instanceof ReferenceParam) { if (params instanceof ReferenceParam) {
ReferenceParam ref = (ReferenceParam) params; ReferenceParam ref = (ReferenceParam) params;
@ -574,10 +624,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
RuntimeSearchParam param = getContext().getResourceDefinition(getResourceType()).getSearchParam(theParamName); Predicate type = createResourceLinkPathPredicate(theParamName, builder, from);
String path = param.getPath();
Predicate type = builder.equal(from.get("mySourcePath"), path);
if (pidsToRetain.size() > 0) { if (pidsToRetain.size() > 0) {
Predicate inPids = (from.get("mySourceResourcePid").in(pidsToRetain)); Predicate inPids = (from.get("mySourceResourcePid").in(pidsToRetain));
cq.where(builder.and(type, masterCodePredicate, inPids)); cq.where(builder.and(type, masterCodePredicate, inPids));
@ -589,11 +636,23 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
private Predicate createResourceLinkPathPredicate(String theParamName, CriteriaBuilder builder, Root<? extends ResourceLink> from) {
RuntimeSearchParam param = getContext().getResourceDefinition(getResourceType()).getSearchParam(theParamName);
String path = param.getPath();
Predicate type = builder.equal(from.get("mySourcePath"), path);
return type;
}
private Set<Long> addPredicateString(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { private Set<Long> addPredicateString(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
if (theList == null || theList.isEmpty()) { if (theList == null || theList.isEmpty()) {
return thePids; return thePids;
} }
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsString", theParamName, ResourceIndexedSearchParamString.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamString> from = cq.from(ResourceIndexedSearchParamString.class); Root<ResourceIndexedSearchParamString> from = cq.from(ResourceIndexedSearchParamString.class);
@ -602,7 +661,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
List<Predicate> codePredicates = new ArrayList<Predicate>(); List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) { for (IQueryParameterType nextOr : theList) {
IQueryParameterType theParameter = nextOr; IQueryParameterType theParameter = nextOr;
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
Predicate singleCode = createPredicateString(theParameter, theParamName, builder, from); Predicate singleCode = createPredicateString(theParameter, theParamName, builder, from);
codePredicates.add(singleCode); codePredicates.add(singleCode);
} }
@ -622,11 +684,43 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "multipleParamsWithSameNameOneIsMissingTrue", theParamName));
}
Predicate singleCode = from.get("myId").isNotNull();
Predicate name = theBuilder.equal(from.get("myParamName"), theParamName);
codePredicates.add(theBuilder.and(name, singleCode));
missingFalse = true;
}
return missingFalse;
}
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "multipleParamsWithSameNameOneIsMissingTrue", theParamName));
}
Predicate singleCode = from.get("mySourceResource").isNotNull();
Predicate name = createResourceLinkPathPredicate(theParamName, theBuilder, from);
codePredicates.add(theBuilder.and(name, singleCode));
missingFalse = true;
}
return missingFalse;
}
private Set<Long> addPredicateToken(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { private Set<Long> addPredicateToken(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
if (theList == null || theList.isEmpty()) { if (theList == null || theList.isEmpty()) {
return thePids; return thePids;
} }
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsToken", theParamName, ResourceIndexedSearchParamToken.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamToken> from = cq.from(ResourceIndexedSearchParamToken.class); Root<ResourceIndexedSearchParamToken> from = cq.from(ResourceIndexedSearchParamToken.class);
@ -634,6 +728,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
List<Predicate> codePredicates = new ArrayList<Predicate>(); List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) { for (IQueryParameterType nextOr : theList) {
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
if (nextOr instanceof TokenParam) { if (nextOr instanceof TokenParam) {
TokenParam id = (TokenParam) nextOr; TokenParam id = (TokenParam) nextOr;
if (id.isText()) { if (id.isText()) {
@ -759,20 +857,60 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
DateParam date = (DateParam) theParam; DateParam date = (DateParam) theParam;
if (!date.isEmpty()) { if (!date.isEmpty()) {
DateRangeParam range = new DateRangeParam(date); DateRangeParam range = new DateRangeParam(date);
p = addPredicateDateFromRange(theBuilder, theFrom, range); p = createPredicateDateFromRange(theBuilder, theFrom, range);
} else { } else {
// TODO: handle missing date param? // TODO: handle missing date param?
p = null; p = null;
} }
} else if (theParam instanceof DateRangeParam) { } else if (theParam instanceof DateRangeParam) {
DateRangeParam range = (DateRangeParam) theParam; DateRangeParam range = (DateRangeParam) theParam;
p = addPredicateDateFromRange(theBuilder, theFrom, range); p = createPredicateDateFromRange(theBuilder, theFrom, range);
} else { } else {
throw new IllegalArgumentException("Invalid token type: " + theParam.getClass()); throw new IllegalArgumentException("Invalid token type: " + theParam.getClass());
} }
return p; return p;
} }
private Predicate createPredicateDateFromRange(CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> theFrom, DateRangeParam theRange) {
Date lowerBound = theRange.getLowerBoundAsInstant();
Date upperBound = theRange.getUpperBoundAsInstant();
Predicate lb = null;
if (lowerBound != null) {
Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueLow"), lowerBound);
Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueHigh"), lowerBound);
lb = theBuilder.or(gt, lt);
// Predicate gin = builder.isNull(from.get("myValueLow"));
// Predicate lbo = builder.or(gt, gin);
// Predicate lin = builder.isNull(from.get("myValueHigh"));
// Predicate hbo = builder.or(lt, lin);
// lb = builder.and(lbo, hbo);
}
Predicate ub = null;
if (upperBound != null) {
Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueLow"), upperBound);
Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueHigh"), upperBound);
ub = theBuilder.or(gt, lt);
// Predicate gin = builder.isNull(from.get("myValueLow"));
// Predicate lbo = builder.or(gt, gin);
// Predicate lin = builder.isNull(from.get("myValueHigh"));
// Predicate ubo = builder.or(lt, lin);
// ub = builder.and(ubo, lbo);
}
if (lb != null && ub != null) {
return (theBuilder.and(lb, ub));
} else if (lb != null) {
return (lb);
} else {
return (ub);
}
}
private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder, private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theFrom) { From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theFrom) {
String rawSearchTerm; String rawSearchTerm;
@ -1166,7 +1304,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
} }
HashSet<Long> pidsToInclude = new HashSet<Long>(); HashSet<Long> pidsToInclude = new HashSet<Long>();
for (Include nextInclude : theRevIncludes) { for (Include nextInclude : theRevIncludes) {
boolean matchAll = "*".equals(nextInclude.getValue()); boolean matchAll = "*".equals(nextInclude.getValue());
if (matchAll) { if (matchAll) {
@ -1208,7 +1346,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
} }
} }
} }
theMatches.addAll(pidsToInclude); theMatches.addAll(pidsToInclude);
} }
@ -1532,9 +1670,8 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
loadResourcesByPid(pidsSubList, retVal, BundleEntrySearchModeEnum.MATCH); loadResourcesByPid(pidsSubList, retVal, BundleEntrySearchModeEnum.MATCH);
/* /*
* Load _include resources - Note that _revincludes are handled differently * Load _include resources - Note that _revincludes are handled differently than _include ones, as they are counted towards the total count and paged, so they are loaded
* than _include ones, as they are counted towards the total count and paged, * outside the bundle provider
* so they are loaded outside the bundle provider
*/ */
if (theParams.getIncludes() != null && theParams.getIncludes().isEmpty() == false) { if (theParams.getIncludes() != null && theParams.getIncludes().isEmpty() == false) {
Set<IIdType> previouslyLoadedPids = new HashSet<IIdType>(); Set<IIdType> previouslyLoadedPids = new HashSet<IIdType>();

View File

@ -22,6 +22,9 @@ package ca.uhn.fhir.jpa.dao;
import java.util.Date; import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
class HistoryTuple implements Comparable<HistoryTuple> { class HistoryTuple implements Comparable<HistoryTuple> {
private Long myId; private Long myId;
@ -64,4 +67,13 @@ class HistoryTuple implements Comparable<HistoryTuple> {
myUpdated = theUpdated; myUpdated = theUpdated;
} }
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("id", myId);
b.append("history", myIsHistory);
b.append("updated", myUpdated);
return b.build();
}
} }

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
@ -868,16 +869,16 @@ public class FhirResourceDaoDstu2Test {
public void testReverseIncludes() { public void testReverseIncludes() {
String methodName = "testReverseIncludes"; String methodName = "testReverseIncludes";
Organization org = new Organization(); Organization org = new Organization();
org.setName("X"+methodName+"X"); org.setName("X" + methodName + "X");
IdDt orgId = ourOrganizationDao.create(org).getId(); IdDt orgId = ourOrganizationDao.create(org).getId();
Patient pat = new Patient(); Patient pat = new Patient();
pat.addName().addFamily("X"+methodName+"X"); pat.addName().addFamily("X" + methodName + "X");
pat.getManagingOrganization().setReference(orgId.toUnqualifiedVersionless()); pat.getManagingOrganization().setReference(orgId.toUnqualifiedVersionless());
ourPatientDao.create(pat); ourPatientDao.create(pat);
SearchParameterMap map = new SearchParameterMap(); SearchParameterMap map = new SearchParameterMap();
map.add(Organization.SP_NAME, new StringParam("X"+methodName+"X")); map.add(Organization.SP_NAME, new StringParam("X" + methodName + "X"));
map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION)); map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION));
IBundleProvider resultsP = ourOrganizationDao.search(map); IBundleProvider resultsP = ourOrganizationDao.search(map);
assertEquals(2, resultsP.size()); assertEquals(2, resultsP.size());
@ -886,11 +887,11 @@ public class FhirResourceDaoDstu2Test {
assertEquals(Organization.class, results.get(0).getClass()); assertEquals(Organization.class, results.get(0).getClass());
assertEquals(Patient.class, results.get(1).getClass()); assertEquals(Patient.class, results.get(1).getClass());
} }
@Test @Test
public void testResourceInstanceMetaOperation() { public void testResourceInstanceMetaOperation() {
deleteEverything(); deleteEverything();
String methodName = "testResourceInstanceMetaOperation"; String methodName = "testResourceInstanceMetaOperation";
IdDt id1, id2; IdDt id1, id2;
{ {
@ -1523,6 +1524,195 @@ public class FhirResourceDaoDstu2Test {
} }
@Test
public void testSearchWithMissingString() {
IdDt orgId = ourOrganizationDao.create(new Organization()).getId();
IdDt notMissing;
IdDt missing;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
missing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("002");
patient.addName().addFamily("Tester_testSearchStringParam").addGiven("John");
patient.setBirthDate(new DateDt("2011-01-01"));
patient.getManagingOrganization().setReference(orgId);
notMissing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
// String Param
{
HashMap<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
StringParam param = new StringParam();
param.setMissing(false);
params.put(Patient.SP_FAMILY, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
StringParam param = new StringParam();
param.setMissing(true);
params.put(Patient.SP_FAMILY, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithMissingQuantity() {
IdDt notMissing;
IdDt missing;
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("001");
missing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("002");
obs.setValue(new QuantityDt(123));
notMissing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
}
// Quantity Param
{
HashMap<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
QuantityParam param = new QuantityParam();
param.setMissing(false);
params.put(Observation.SP_VALUE_QUANTITY, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
QuantityParam param = new QuantityParam();
param.setMissing(true);
params.put(Observation.SP_VALUE_QUANTITY, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithToken() {
IdDt notMissing;
IdDt missing;
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("001");
missing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("002");
obs.getCode().addCoding().setSystem("urn:system").setCode("002");
notMissing = ourObservationDao.create(obs).getId().toUnqualifiedVersionless();
}
// Token Param
{
HashMap<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
TokenParam param = new TokenParam();
param.setMissing(false);
params.put(Observation.SP_CODE, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
TokenParam param = new TokenParam();
param.setMissing(true);
params.put(Observation.SP_CODE, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithMissingDate() {
IdDt orgId = ourOrganizationDao.create(new Organization()).getId();
IdDt notMissing;
IdDt missing;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
missing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("002");
patient.addName().addFamily("Tester_testSearchStringParam").addGiven("John");
patient.setBirthDate(new DateDt("2011-01-01"));
patient.getManagingOrganization().setReference(orgId);
notMissing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
// Date Param
{
HashMap<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
DateParam param = new DateParam();
param.setMissing(false);
params.put(Patient.SP_BIRTHDATE, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
DateParam param = new DateParam();
param.setMissing(true);
params.put(Patient.SP_BIRTHDATE, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithMissingReference() {
IdDt orgId = ourOrganizationDao.create(new Organization()).getId();
IdDt notMissing;
IdDt missing;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
missing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("002");
patient.addName().addFamily("Tester_testSearchStringParam").addGiven("John");
patient.setBirthDate(new DateDt("2011-01-01"));
patient.getManagingOrganization().setReference(orgId);
notMissing = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
// Reference Param
{
HashMap<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
ReferenceParam param = new ReferenceParam();
param.setMissing(false);
params.put(Patient.SP_ORGANIZATION, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
ReferenceParam param = new ReferenceParam();
param.setMissing(true);
params.put(Patient.SP_ORGANIZATION, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test @Test
public void testSearchStringParamWithNonNormalized() { public void testSearchStringParamWithNonNormalized() {
{ {
@ -2234,36 +2424,36 @@ public class FhirResourceDaoDstu2Test {
@Test @Test
public void testUpdateMaintainsSearchParams() throws InterruptedException { public void testUpdateMaintainsSearchParams() throws InterruptedException {
Patient p1 = new Patient(); Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsAAA"); p1.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsDstu2AAA");
p1.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsAAA"); p1.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsDstu2AAA");
IdDt p1id = ourPatientDao.create(p1).getId(); IdDt p1id = ourPatientDao.create(p1).getId();
Patient p2 = new Patient(); Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsBBB"); p2.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsDstu2BBB");
p2.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsBBB"); p2.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsDstu2BBB");
ourPatientDao.create(p2).getId(); ourPatientDao.create(p2).getId();
Set<Long> ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA")); Set<Long> ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsDstu2AAA"));
assertEquals(1, ids.size()); assertEquals(1, ids.size());
assertThat(ids, contains(p1id.getIdPartAsLong())); assertThat(ids, contains(p1id.getIdPartAsLong()));
// Update the name // Update the name
p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsBBB"); p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsDstu2BBB");
MethodOutcome update2 = ourPatientDao.update(p1); MethodOutcome update2 = ourPatientDao.update(p1);
IdDt p1id2 = update2.getId(); IdDt p1id2 = update2.getId();
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA")); ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsDstu2AAA"));
assertEquals(0, ids.size()); assertEquals(0, ids.size());
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsBBB")); ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsDstu2BBB"));
assertEquals(2, ids.size()); assertEquals(2, ids.size());
// Make sure vreads work // Make sure vreads work
p1 = ourPatientDao.read(p1id); p1 = ourPatientDao.read(p1id);
assertEquals("testUpdateMaintainsSearchParamsAAA", p1.getNameFirstRep().getGivenAsSingleString()); assertEquals("testUpdateMaintainsSearchParamsDstu2AAA", p1.getNameFirstRep().getGivenAsSingleString());
p1 = ourPatientDao.read(p1id2); p1 = ourPatientDao.read(p1id2);
assertEquals("testUpdateMaintainsSearchParamsBBB", p1.getNameFirstRep().getGivenAsSingleString()); assertEquals("testUpdateMaintainsSearchParamsDstu2BBB", p1.getNameFirstRep().getGivenAsSingleString());
} }
@ -2344,14 +2534,12 @@ public class FhirResourceDaoDstu2Test {
FhirSystemDaoDstu2Test.doDeleteEverything(ourSystemDao); FhirSystemDaoDstu2Test.doDeleteEverything(ourSystemDao);
} }
@Test @Test
public void testSearchWithNoResults() { public void testSearchWithNoResults() {
Device dev = new Device(); Device dev = new Device();
dev.addIdentifier().setSystem("Foo"); dev.addIdentifier().setSystem("Foo");
ourDeviceDao.create(dev); ourDeviceDao.create(dev);
IBundleProvider value = ourDeviceDao.search(new SearchParameterMap()); IBundleProvider value = ourDeviceDao.search(new SearchParameterMap());
ourLog.info("Initial size: " + value.size()); ourLog.info("Initial size: " + value.size());
for (IBaseResource next : value.getResources(0, value.size())) { for (IBaseResource next : value.getResources(0, value.size())) {

View File

@ -37,6 +37,7 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider; import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu.resource.Device; import ca.uhn.fhir.model.dstu.resource.Device;
@ -282,7 +283,7 @@ public class ResourceProviderDstu2Test {
response.close(); response.close();
} }
} }
@Test @Test
public void testSearchWithInclude() throws Exception { public void testSearchWithInclude() throws Exception {
Organization org = new Organization(); Organization org = new Organization();
@ -313,6 +314,59 @@ public class ResourceProviderDstu2Test {
assertEquals(BundleEntrySearchModeEnum.INCLUDE, found.getEntries().get(1).getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE)); assertEquals(BundleEntrySearchModeEnum.INCLUDE, found.getEntries().get(1).getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE));
} }
@Test
public void testSearchWithMissing() throws Exception {
String methodName = "testSearchWithMissing";
Organization org = new Organization();
org.addIdentifier().setSystem("urn:system:rpdstu2").setValue(methodName + "01");
org.setName(methodName + "name");
IdDt orgNotMissing = ourClient.create().resource(org).prettyPrint().encodedXml().execute().getId().toUnqualifiedVersionless();
org = new Organization();
org.addIdentifier().setSystem("urn:system:rpdstu2").setValue(methodName + "01");
IdDt orgMissing = ourClient.create().resource(org).prettyPrint().encodedXml().execute().getId().toUnqualifiedVersionless();
{
//@formatter:off
Bundle found = ourClient
.search()
.forResource(Organization.class)
.where(Organization.NAME.isMissing(false))
.prettyPrint()
.execute();
//@formatter:on
List<IdDt> list = toIdListUnqualifiedVersionless(found);
ourLog.info(methodName + ": " + list.toString());
assertThat(list, containsInRelativeOrder(orgNotMissing));
assertThat(list, not(containsInRelativeOrder(orgMissing)));
}
{
//@formatter:off
Bundle found = ourClient
.search()
.forResource(Organization.class)
.where(Organization.NAME.isMissing(true))
.prettyPrint()
.execute();
//@formatter:on
List<IdDt> list = toIdListUnqualifiedVersionless(found);
ourLog.info(methodName + ": " + list.toString());
assertThat(list, not(containsInRelativeOrder(orgNotMissing)));
assertThat(list, containsInRelativeOrder(orgMissing));
}
}
private List<IdDt> toIdListUnqualifiedVersionless(Bundle found) {
List<IdDt> list = new ArrayList<IdDt>();
for (BundleEntry next : found.getEntries()) {
list.add(next.getResource().getId().toUnqualifiedVersionless());
}
return list;
}
@Test @Test
public void testEverythingOperation() throws Exception { public void testEverythingOperation() throws Exception {
String methodName = "testEverythingOperation"; String methodName = "testEverythingOperation";
@ -688,7 +742,8 @@ public class ResourceProviderDstu2Test {
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01"); p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
IdDt p1Id = ourClient.create().resource(p1).execute().getId(); IdDt p1Id = ourClient.create().resource(p1).execute().getId();
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint().execute(); Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint()
.execute();
assertEquals(1, actual.size()); assertEquals(1, actual.size());
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart());
@ -786,7 +841,8 @@ public class ResourceProviderDstu2Test {
assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2/_history")); assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2/_history"));
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2")).encodedJson().prettyPrint().execute(); Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2"))
.encodedJson().prettyPrint().execute();
assertEquals(1, actual.size()); assertEquals(1, actual.size());
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart());

View File

@ -147,7 +147,30 @@ public class GenericClientTest {
} }
@Test
public void testMissing() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return (new ReaderInputStream(new StringReader(getPatientFeedWithOneResult()), Charset.forName("UTF-8")));
}});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
client.search().forResource("Patient").where(Patient.NAME.isMissing(true)).execute();
assertEquals("http://example.com/fhir/Patient?name%3Amissing=true", capt.getValue().getRequestLine().getUri());
client.search().forResource("Patient").where(Patient.NAME.isMissing(false)).execute();
assertEquals("http://example.com/fhir/Patient?name%3Amissing=false", capt.getValue().getRequestLine().getUri());
}
@Test @Test
public void testCreateWithStringAutoDetectsEncoding() throws Exception { public void testCreateWithStringAutoDetectsEncoding() throws Exception {

View File

@ -33,6 +33,7 @@ import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.param.StringParam;
/** /**
* HAPI/FHIR <b>Identifier</b> Datatype * HAPI/FHIR <b>Identifier</b> Datatype
@ -402,5 +403,29 @@ public class IdentifierDt
return null; return null;
} }
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public Boolean getMissing() {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
/**
* <b>Not supported!</b>
*
* @deprecated get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you
* need this functionality
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}
} }

View File

@ -138,3 +138,5 @@ local.properties
.texlipse .texlipse
/target/ /target/
/target/
/target/

View File

@ -191,7 +191,15 @@
</action> </action>
<action type="add" issue="164"> <action type="add" issue="164">
Improve error message when a user tries to perform a create/update with an invalid Improve error message when a user tries to perform a create/update with an invalid
or missing Content-Type header. Thanks to wanghaisheng for reporting! or missing Content-Type header. Thanks to wanghaisheng for reporting! (This was
actually a three part bug, so the following two fixes also reference this
bug number)
</action>
<action type="add" issue="164">
Add support for :missing qualifier in generic/fluent client.
</action>
<action type="add" issue="164">
Add support for :missing qualifier in JPA server.
</action> </action>
<action type="add"> <action type="add">
Add a new configuration method on the parsers, Add a new configuration method on the parsers,