Hacky first-pass of adding ordinal date field to ResourceIndexedSearchParamDate and using it for search
This commit is contained in:
parent
18d859281e
commit
7f5b3394e0
|
@ -262,6 +262,22 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
validateAndSet(myLowerBound, new DateParam(ParamPrefixEnum.LESSTHAN, theUpperBound));
|
||||
return this;
|
||||
}
|
||||
public Integer getLowerBoundAsDateOrdinal() {
|
||||
if (myLowerBound == null || myLowerBound.getValue() == null) {
|
||||
return null;
|
||||
}
|
||||
Calendar cal = DateUtils.toCalendar(myLowerBound.getValue());
|
||||
String s = new StringBuilder().append(cal.get(Calendar.YEAR)).append(cal.get(Calendar.MONTH)).append(cal.get(Calendar.DAY_OF_MONTH)).toString();
|
||||
return Integer.parseInt(s);
|
||||
}
|
||||
public Integer getUpperBoundAsDateOrdinal() {
|
||||
if (myUpperBound == null || myUpperBound.getValue() == null) {
|
||||
return null;
|
||||
}
|
||||
Calendar cal = DateUtils.toCalendar(myUpperBound.getValue());
|
||||
String s = new StringBuilder().append(cal.get(Calendar.YEAR)).append(cal.get(Calendar.MONTH)).append(cal.get(Calendar.DAY_OF_MONTH)).toString();
|
||||
return Integer.parseInt(s);
|
||||
}
|
||||
|
||||
public Date getLowerBoundAsInstant() {
|
||||
if (myLowerBound == null || myLowerBound.getValue() == null) {
|
||||
|
@ -334,6 +350,12 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
|
||||
Date retVal = myUpperBound.getValue();
|
||||
|
||||
|
||||
//if (myUpperBound.getPrecision().ordinal() == TemporalPrecisionEnum.DAY.ordinal()) {
|
||||
// DateUtils.setHours(retVal, 23);
|
||||
// DateUtils.setMinutes(retVal, 59);
|
||||
// DateUtils.setSeconds(retVal, 59);
|
||||
// }
|
||||
if (myUpperBound.getPrecision().ordinal() <= TemporalPrecisionEnum.DAY.ordinal()) {
|
||||
Calendar cal = DateUtils.toCalendar(retVal);
|
||||
cal.setTimeZone(TimeZone.getTimeZone("GMT+11:30"));
|
||||
|
|
|
@ -126,7 +126,6 @@ abstract class BasePredicateBuilder {
|
|||
}
|
||||
|
||||
void addPredicateParamMissing(String theResourceName, String theParamName, boolean theMissing, Join<ResourceTable, ? extends BaseResourceIndexedSearchParam> theJoin) {
|
||||
|
||||
myQueryRoot.addPredicate(myBuilder.equal(theJoin.get("myResourceType"), theResourceName));
|
||||
myQueryRoot.addPredicate(myBuilder.equal(theJoin.get("myParamName"), theParamName));
|
||||
myQueryRoot.addPredicate(myBuilder.equal(theJoin.get("myMissing"), theMissing));
|
||||
|
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
|||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
|
@ -127,67 +128,102 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, p);
|
||||
}
|
||||
|
||||
private boolean isNullOrDayPrecision(DateParam theDateParam) {
|
||||
return theDateParam == null || theDateParam.getPrecision().ordinal() == TemporalPrecisionEnum.DAY.ordinal();
|
||||
}
|
||||
private Predicate createPredicateDateFromRange(CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamDate> theFrom,
|
||||
DateRangeParam theRange,
|
||||
SearchFilterParser.CompareOperation operation) {
|
||||
Date lowerBound = theRange.getLowerBoundAsInstant();
|
||||
Date upperBound = theRange.getUpperBoundAsInstant();
|
||||
Date lowerBoundInstant = theRange.getLowerBoundAsInstant();
|
||||
Date upperBoundInstant = theRange.getUpperBoundAsInstant();
|
||||
|
||||
DateParam lowerBound = theRange.getLowerBound();
|
||||
DateParam upperBound = theRange.getUpperBound();
|
||||
boolean isOrdinalComparison = isNullOrDayPrecision(lowerBound) && isNullOrDayPrecision(upperBound);
|
||||
Predicate lt = null;
|
||||
Predicate gt = null;
|
||||
Predicate lb = null;
|
||||
Predicate ub = null;
|
||||
|
||||
if (operation == SearchFilterParser.CompareOperation.lt) {
|
||||
if (lowerBound == null) {
|
||||
if (lowerBoundInstant == null) {
|
||||
throw new InvalidRequestException("lowerBound value not correctly specified for compare operation");
|
||||
}
|
||||
lb = theBuilder.lessThan(theFrom.get("myValueLow"), lowerBound);
|
||||
//im like 80% sure this should be ub and not lb, as it is an UPPER bound.
|
||||
if (isOrdinalComparison) {
|
||||
lb = theBuilder.lessThan(theFrom.get("myValueLowDateOrdinal"), theRange.getLowerBoundAsDateOrdinal());
|
||||
} else {
|
||||
lb = theBuilder.lessThan(theFrom.get("myValueLow"), lowerBoundInstant);
|
||||
}
|
||||
} else if (operation == SearchFilterParser.CompareOperation.le) {
|
||||
if (upperBound == null) {
|
||||
if (upperBoundInstant == null) {
|
||||
throw new InvalidRequestException("upperBound value not correctly specified for compare operation");
|
||||
}
|
||||
lb = theBuilder.lessThanOrEqualTo(theFrom.get("myValueHigh"), upperBound);
|
||||
//im like 80% sure this should be ub and not lb, as it is an UPPER bound.
|
||||
if (isOrdinalComparison) {
|
||||
lb = theBuilder.lessThanOrEqualTo(theFrom.get("myValueHighDateOrdinal"), theRange.getUpperBoundAsDateOrdinal());
|
||||
} else {
|
||||
lb = theBuilder.lessThanOrEqualTo(theFrom.get("myValueHigh"), upperBoundInstant);
|
||||
}
|
||||
} else if (operation == SearchFilterParser.CompareOperation.gt) {
|
||||
if (upperBound == null) {
|
||||
if (upperBoundInstant == null) {
|
||||
throw new InvalidRequestException("upperBound value not correctly specified for compare operation");
|
||||
}
|
||||
lb = theBuilder.greaterThan(theFrom.get("myValueHigh"), upperBound);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ge) {
|
||||
if (lowerBound == null) {
|
||||
if (isOrdinalComparison) {
|
||||
lb = theBuilder.greaterThan(theFrom.get("myValueHighDateOrdinal"), theRange.getUpperBoundAsDateOrdinal());
|
||||
} else {
|
||||
lb = theBuilder.greaterThan(theFrom.get("myValueHigh"), upperBoundInstant);
|
||||
}
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ge) {
|
||||
if (lowerBoundInstant == null) {
|
||||
throw new InvalidRequestException("lowerBound value not correctly specified for compare operation");
|
||||
}
|
||||
lb = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueLow"), lowerBound);
|
||||
if (isOrdinalComparison) {
|
||||
lb = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueLowDateOrdinal"), theRange.getLowerBoundAsDateOrdinal());
|
||||
} else {
|
||||
lb = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueLow"), lowerBoundInstant);
|
||||
}
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ne) {
|
||||
if ((lowerBound == null) ||
|
||||
(upperBound == null)) {
|
||||
if ((lowerBoundInstant == null) ||
|
||||
(upperBoundInstant == null)) {
|
||||
throw new InvalidRequestException("lowerBound and/or upperBound value not correctly specified for compare operation");
|
||||
}
|
||||
/*Predicate*/
|
||||
lt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueLow"), lowerBound);
|
||||
/*Predicate*/
|
||||
gt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueHigh"), upperBound);
|
||||
if (isOrdinalComparison){
|
||||
lt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueLowDateOrdinal"), theRange.getLowerBoundAsDateOrdinal());
|
||||
gt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueHighDateOrdinal"), theRange.getUpperBoundAsDateOrdinal());
|
||||
} else {
|
||||
lt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueLow"), lowerBoundInstant);
|
||||
gt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueHigh"), upperBoundInstant);
|
||||
}
|
||||
lb = theBuilder.or(lt,
|
||||
gt);
|
||||
} else if ((operation == SearchFilterParser.CompareOperation.eq) ||
|
||||
(operation == null)) {
|
||||
if (lowerBound != null) {
|
||||
/*Predicate*/
|
||||
gt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueLow"), lowerBound);
|
||||
/*Predicate*/
|
||||
lt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueHigh"), lowerBound);
|
||||
if (theRange.getLowerBound().getPrefix() == ParamPrefixEnum.STARTS_AFTER || theRange.getLowerBound().getPrefix() == ParamPrefixEnum.EQUAL) {
|
||||
} else if ((operation == SearchFilterParser.CompareOperation.eq) || (operation == null)) {
|
||||
if (lowerBoundInstant != null) {
|
||||
if (isOrdinalComparison) {
|
||||
gt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueLowDateOrdinal"), theRange.getLowerBoundAsDateOrdinal());
|
||||
lt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueHighDateOrdinal"), theRange.getLowerBoundAsDateOrdinal());
|
||||
//also try a strict equality here.
|
||||
}
|
||||
else {
|
||||
gt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueLow"), lowerBoundInstant);
|
||||
lt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueHigh"), lowerBoundInstant);
|
||||
}
|
||||
if (lowerBound.getPrefix() == ParamPrefixEnum.STARTS_AFTER || lowerBound.getPrefix() == ParamPrefixEnum.EQUAL) {
|
||||
lb = gt;
|
||||
} else {
|
||||
lb = theBuilder.or(gt, lt);
|
||||
}
|
||||
}
|
||||
|
||||
if (upperBound != null) {
|
||||
/*Predicate*/
|
||||
gt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueLow"), upperBound);
|
||||
/*Predicate*/
|
||||
lt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueHigh"), upperBound);
|
||||
if (upperBoundInstant != null) {
|
||||
if (isOrdinalComparison) {
|
||||
gt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueLowDateOrdinal"), theRange.getUpperBoundAsDateOrdinal());
|
||||
lt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueHighDateOrdinal"), theRange.getUpperBoundAsDateOrdinal());
|
||||
} else {
|
||||
gt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueLow"), upperBoundInstant);
|
||||
lt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueHigh"), upperBoundInstant);
|
||||
}
|
||||
if (theRange.getUpperBound().getPrefix() == ParamPrefixEnum.ENDS_BEFORE || theRange.getUpperBound().getPrefix() == ParamPrefixEnum.EQUAL) {
|
||||
ub = lt;
|
||||
} else {
|
||||
|
@ -198,8 +234,10 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
throw new InvalidRequestException(String.format("Unsupported operator specified, operator=%s",
|
||||
operation.name()));
|
||||
}
|
||||
|
||||
ourLog.trace("Date range is {} - {}", lowerBound, upperBound);
|
||||
if (isOrdinalComparison) {
|
||||
ourLog.trace("Ordinal date range is {} - {} ", theRange.getLowerBoundAsDateOrdinal(), theRange.getUpperBoundAsDateOrdinal());
|
||||
}
|
||||
ourLog.trace("Date range is {} - {}", lowerBoundInstant, upperBoundInstant);
|
||||
|
||||
if (lb != null && ub != null) {
|
||||
return (theBuilder.and(lb, ub));
|
||||
|
|
|
@ -4094,7 +4094,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
obs.setId(theId);
|
||||
obs.setEffective(new DateTimeType(theEffective));
|
||||
myObservationDao.update(obs);
|
||||
|
||||
ourLog.info("Obs {} has time {}", theId, obs.getEffectiveDateTimeType().getValue().toString());
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
|
||||
<logger name="ca.uhn.fhir.jpa.dao" additivity="false" level="info">
|
||||
<logger name="ca.uhn.fhir.jpa.dao" additivity="false" level="trace">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
|
||||
|
|
|
@ -29,10 +29,12 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
|
|||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hibernate.search.annotations.Field;
|
||||
import org.hl7.fhir.r4.model.DateTimeType;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
@Embeddable
|
||||
|
@ -54,6 +56,12 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Field
|
||||
public Date myValueLow;
|
||||
|
||||
@Column(name="SP_VALUE_LOW_DATE_ORDINAL")
|
||||
public int myValueLowDateOrdinal;
|
||||
@Column(name="SP_VALUE_HIGH_DATE_ORDINAL")
|
||||
public int myValueHighDateOrdinal;
|
||||
|
||||
@Transient
|
||||
private transient String myOriginalValue;
|
||||
@Id
|
||||
|
@ -82,9 +90,28 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
setParamName(theParamName);
|
||||
setValueLow(theLow);
|
||||
setValueHigh(theHigh);
|
||||
computeValueHighDateOrdinal(theHigh);
|
||||
computeValueLowDateOrdinal(theLow);
|
||||
myOriginalValue = theOriginalValue;
|
||||
}
|
||||
|
||||
private void computeValueHighDateOrdinal(Date theHigh) {
|
||||
this.myValueHighDateOrdinal = generateOrdinalDateInteger(theHigh);
|
||||
}
|
||||
private int generateOrdinalDateInteger(Date theDate) {
|
||||
Calendar calendar = DateUtils.toCalendar(theDate);
|
||||
String ordinalDateString = new StringBuilder()
|
||||
.append(calendar.get(Calendar.YEAR))
|
||||
.append(calendar.get(Calendar.MONTH))
|
||||
.append(calendar.get(Calendar.DAY_OF_MONTH))
|
||||
.toString();
|
||||
return Integer.parseInt(ordinalDateString);
|
||||
}
|
||||
|
||||
private void computeValueLowDateOrdinal(Date theLow) {
|
||||
this.myValueLowDateOrdinal = generateOrdinalDateInteger(theLow);
|
||||
}
|
||||
|
||||
@Override
|
||||
@PrePersist
|
||||
public void calculateHashes() {
|
||||
|
|
Loading…
Reference in New Issue