Add date ordinals to subscription matching checker

This commit is contained in:
Gary Graham 2020-02-26 17:05:23 -05:00
parent 2dc94de6bb
commit f86a4c9fa1
14 changed files with 91 additions and 61 deletions

View File

@ -100,10 +100,6 @@ public class DaoConfig {
*/
private boolean myAllowInlineMatchUrlReferences = true;
private boolean myAllowMultipleDelete;
/**
* Update setter javadoc if default changes.
*/
private boolean myUseOrdinalDatesForDayPrecisionSearches = true;
/**
* update setter javadoc if default changes
*/
@ -1912,43 +1908,7 @@ public class DaoConfig {
setPreExpandValueSetsDefaultCount(Math.min(getPreExpandValueSetsDefaultCount(), getPreExpandValueSetsMaxCount()));
}
/**
* <p>
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}.
*
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
* ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
* and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
* </p>
* Default is {@literal true} beginning in HAPI FHIR 4.3.
* </p>
*
* @since 4.3
*/
public void setUseOrdinalDatesForDayPrecisionSearches(boolean theUseOrdinalDates) {
myUseOrdinalDatesForDayPrecisionSearches = theUseOrdinalDates;
}
/**
* <p>
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}.
*
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
* integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
* and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
* </p>
* Default is {@literal true} beginning in HAPI FHIR 4.3.
* </p>
*
* @since 4.3
*/
public boolean getUseOrdinalDatesForDayPrecisionSearches() {
return myUseOrdinalDatesForDayPrecisionSearches;
}
public enum StoreMetaSourceInformationEnum {
NONE(false, false),

View File

@ -50,6 +50,7 @@ abstract class BasePredicateBuilder {
@Autowired
DaoConfig myDaoConfig;
boolean myDontUseHashesForSearch;
final IDao myCallingDao;
final CriteriaBuilder myBuilder;

View File

@ -171,7 +171,7 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
* If all present search parameters are of DAY precision, and {@link DaoConfig#getUseOrdinalDatesForDayPrecisionSearches()} is true,
* then we attempt to use the ordinal field for date comparisons instead of the date field.
*/
boolean isOrdinalComparison = isNullOrDayPrecision(lowerBound) && isNullOrDayPrecision(upperBound) && myDaoConfig.getUseOrdinalDatesForDayPrecisionSearches();
boolean isOrdinalComparison = isNullOrDayPrecision(lowerBound) && isNullOrDayPrecision(upperBound) && myDaoConfig.getModelConfig().getUseOrdinalDatesForDayPrecisionSearches();
Predicate lt = null;
Predicate gt = null;

View File

@ -884,10 +884,10 @@ public class InMemorySubscriptionMatcherR4Test {
public void testDateSearchParametersShouldBeTimezoneIndependent() {
List<Observation> nlist = new ArrayList<>();
nlist.add(createObservationWithEffective("NO1", "2011-01-02T23:00:00-11:30"));
nlist.add(createObservationWithEffective("NO2", "2011-01-03T00:00:00+01:00"));
List<Observation> ylist = new ArrayList<>();
nlist.add(createObservationWithEffective("YES00", "2011-01-02T23:00:00-11:30"));
ylist.add(createObservationWithEffective("YES01", "2011-01-02T00:00:00-11:30"));
ylist.add(createObservationWithEffective("YES02", "2011-01-02T00:00:00-10:00"));
ylist.add(createObservationWithEffective("YES03", "2011-01-02T00:00:00-09:00"));

View File

@ -133,7 +133,7 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
public abstract IQueryParameterType toQueryParameterType();
public boolean matches(IQueryParameterType theParam) {
public boolean matches(IQueryParameterType theParam, boolean theUseOrdinalDatesForDayComparison) {
throw new UnsupportedOperationException("No parameter matcher for " + theParam);
}

View File

@ -60,6 +60,10 @@ public class ModelConfig {
private String myEmailFromAddress = "noreply@unknown.com";
private boolean mySubscriptionMatchingEnabled = true;
private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH;
/**
* Update setter javadoc if default changes.
*/
private boolean myUseOrdinalDatesForDayPrecisionSearches = true;
/**
* Constructor
@ -257,7 +261,6 @@ public class ModelConfig {
myTreatReferencesAsLogical = new HashSet<>();
}
myTreatReferencesAsLogical.add(theTreatReferencesAsLogical);
}
/**
@ -340,13 +343,12 @@ public class ModelConfig {
return mySubscriptionMatchingEnabled;
}
/**
* If set to <code>true</code> (default is true) the server will match incoming resources against active subscriptions
* and send them to the subscription channel. If set to <code>false</code> no matching or sending occurs.
* @since 3.7.0
*/
public void setSubscriptionMatchingEnabled(boolean theSubscriptionMatchingEnabled) {
mySubscriptionMatchingEnabled = theSubscriptionMatchingEnabled;
}
@ -388,6 +390,43 @@ public class ModelConfig {
myWebsocketContextPath = theWebsocketContextPath;
}
/**
* <p>
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}.
*
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
* ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
* and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
* </p>
* Default is {@literal true} beginning in HAPI FHIR 4.3.
* </p>
*
* @since 4.3
*/
public void setUseOrdinalDatesForDayPrecisionSearches(boolean theUseOrdinalDates) {
myUseOrdinalDatesForDayPrecisionSearches = theUseOrdinalDates;
}
/**
* <p>
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}.
*
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
* integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
* and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
* </p>
* Default is {@literal true} beginning in HAPI FHIR 4.3.
* </p>
*
* @since 4.3
*/
public boolean getUseOrdinalDatesForDayPrecisionSearches() {
return myUseOrdinalDatesForDayPrecisionSearches;
}
private static void validateTreatBaseUrlsAsLocal(String theUrl) {
Validate.notBlank(theUrl, "Base URL must not be null or empty");

View File

@ -35,7 +35,6 @@ import org.hibernate.search.annotations.Field;
import org.hl7.fhir.r4.model.DateTimeType;
import javax.persistence.*;
import java.text.SimpleDateFormat;
import java.util.Date;
@Embeddable
@ -238,21 +237,33 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
}
@Override
public boolean matches(IQueryParameterType theParam) {
public boolean matches(IQueryParameterType theParam, boolean theUseOrdinalDatesForDayComparison) {
if (!(theParam instanceof DateParam)) {
return false;
}
DateParam dateParam = (DateParam) theParam;
DateRangeParam range = new DateRangeParam(dateParam);
boolean result;
if (theUseOrdinalDatesForDayComparison) {
result = matchesOrdinalDateBounds(range);
} else {
result = matchesDateBounds(range);
}
return result;
}
private boolean matchesDateBounds(DateRangeParam range) {
Date lowerBound = range.getLowerBoundAsInstant();
Date upperBound = range.getUpperBoundAsInstant();
if (lowerBound == null && upperBound == null) {
// should never happen
return false;
}
boolean result = true;
if (lowerBound != null) {
result &= (myValueLow.after(lowerBound) || myValueLow.equals(lowerBound));
@ -265,8 +276,27 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
return result;
}
private boolean matchesOrdinalDateBounds(DateRangeParam range) {
boolean result = true;
Integer lowerBoundAsDateInteger = range.getLowerBoundAsDateInteger();
Integer upperBoundAsDateInteger = range.getUpperBoundAsDateInteger();
if (upperBoundAsDateInteger == null && lowerBoundAsDateInteger == null) {
return false;
}
if (lowerBoundAsDateInteger != null) {
result &= (myValueLowDateOrdinal.equals(lowerBoundAsDateInteger) || myValueLowDateOrdinal > lowerBoundAsDateInteger);
result &= (myValueHighDateOrdinal.equals(lowerBoundAsDateInteger) || myValueHighDateOrdinal > lowerBoundAsDateInteger);
}
if (upperBoundAsDateInteger != null) {
result &= (myValueHighDateOrdinal.equals(upperBoundAsDateInteger) || myValueHighDateOrdinal < upperBoundAsDateInteger);
result &= (myValueLowDateOrdinal.equals(upperBoundAsDateInteger) || myValueLowDateOrdinal < upperBoundAsDateInteger);
}
return result;
}
public static Long calculateOrdinalValue(Date theDate) {
return (long) DateUtils.convertDatetoDayInteger(theDate);
};
}
}

View File

@ -158,7 +158,7 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
}
@Override
public boolean matches(IQueryParameterType theParam) {
public boolean matches(IQueryParameterType theParam, boolean theUseOrdinalDatesForDayComparison) {
if (!(theParam instanceof NumberParam)) {
return false;
}

View File

@ -237,7 +237,7 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
}
@Override
public boolean matches(IQueryParameterType theParam) {
public boolean matches(IQueryParameterType theParam, boolean theUseOrdinalDatesForDayComparison) {
if (!(theParam instanceof QuantityParam)) {
return false;
}

View File

@ -314,7 +314,7 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
}
@Override
public boolean matches(IQueryParameterType theParam) {
public boolean matches(IQueryParameterType theParam, boolean theUseOrdinalDatesForDayComparison) {
if (!(theParam instanceof StringParam)) {
return false;
}

View File

@ -239,7 +239,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
}
@Override
public boolean matches(IQueryParameterType theParam) {
public boolean matches(IQueryParameterType theParam, boolean theUseOrdinalDatesForDayComparison) {
if (!(theParam instanceof TokenParam)) {
return false;
}

View File

@ -188,7 +188,7 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
}
@Override
public boolean matches(IQueryParameterType theParam) {
public boolean matches(IQueryParameterType theParam, boolean theUseOrdinalDatesForDayComparison) {
if (!(theParam instanceof UriParam)) {
return false;
}

View File

@ -217,7 +217,7 @@ public final class ResourceIndexedSearchParams {
return myPopulatedResourceLinkParameters;
}
public boolean matchParam(String theResourceName, String theParamName, RuntimeSearchParam theParamDef, IQueryParameterType theParam) {
public boolean matchParam(String theResourceName, String theParamName, RuntimeSearchParam theParamDef, IQueryParameterType theParam, boolean theUseOrdinalDatesForDayComparison) {
if (theParamDef == null) {
return false;
}
@ -254,7 +254,7 @@ public final class ResourceIndexedSearchParams {
}
Predicate<BaseResourceIndexedSearchParam> namedParamPredicate = param ->
param.getParamName().equalsIgnoreCase(theParamName) &&
param.matches(theParam);
param.matches(theParam, theUseOrdinalDatesForDayComparison);
return resourceParams.stream().anyMatch(namedParamPredicate);
}

View File

@ -202,7 +202,7 @@ public class InMemoryResourceMatcher {
if (theSearchParams == null) {
return InMemoryMatchResult.successfulMatch();
} else {
return InMemoryMatchResult.fromBoolean(theAndOrParams.stream().anyMatch(nextAnd -> matchParams(theResourceName, theParamName, theParamDef, nextAnd, theSearchParams)));
return InMemoryMatchResult.fromBoolean(theAndOrParams.stream().anyMatch(nextAnd -> matchParams(theResourceName, theParamName, theParamDef, nextAnd, theSearchParams, myModelConfig.getUseOrdinalDatesForDayPrecisionSearches())));
}
case COMPOSITE:
case HAS:
@ -219,11 +219,11 @@ public class InMemoryResourceMatcher {
}
}
private boolean matchParams(String theResourceName, String theParamName, RuntimeSearchParam paramDef, List<? extends IQueryParameterType> theNextAnd, ResourceIndexedSearchParams theSearchParams) {
private boolean matchParams(String theResourceName, String theParamName, RuntimeSearchParam paramDef, List<? extends IQueryParameterType> theNextAnd, ResourceIndexedSearchParams theSearchParams, boolean theUseOrdinalDatesForDayComparison) {
if (paramDef.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
stripBaseUrlsFromReferenceParams(theNextAnd);
}
return theNextAnd.stream().anyMatch(token -> theSearchParams.matchParam(theResourceName, theParamName, paramDef, token));
return theNextAnd.stream().anyMatch(token -> theSearchParams.matchParam(theResourceName, theParamName, paramDef, token, theUseOrdinalDatesForDayComparison));
}
private void stripBaseUrlsFromReferenceParams(List<? extends IQueryParameterType> theNextAnd) {