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