From 3ec772b527f89dc40ff7d632cdb906e72a0d28c8 Mon Sep 17 00:00:00 2001 From: Gary Graham Date: Tue, 25 Feb 2020 14:06:36 -0500 Subject: [PATCH] rework ResourceIndexedSearchParamDate constructor to support incoming string values --- .../main/java/ca/uhn/fhir/util/DateUtils.java | 7 +- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 2 +- .../taskdef/CalculateOrdinalDateTask.java | 264 ++++++++++++++++++ .../tasks/HapiFhirJpaMigrationTasks.java | 10 +- .../ResourceIndexedSearchParamDate.java | 16 +- .../ResourceIndexedSearchParamDateTest.java | 32 +-- .../extractor/BaseSearchParamExtractor.java | 13 +- .../InMemoryResourceMatcherR5Test.java | 2 +- 8 files changed, 315 insertions(+), 31 deletions(-) create mode 100644 hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateOrdinalDateTask.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/DateUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/DateUtils.java index 70b742452f2..c9697b2cf26 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/DateUtils.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/DateUtils.java @@ -65,6 +65,8 @@ public final class DateUtils { @SuppressWarnings("WeakerAccess") public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy"; + private static final String PATTERN_INTEGER_DATE = "yyyyMMdd"; + private static final String[] DEFAULT_PATTERNS = new String[]{ PATTERN_RFC1123, PATTERN_RFC1036, @@ -173,8 +175,11 @@ public final class DateUtils { public static int convertDatetoDayInteger(final Date theDateValue) { notNull(theDateValue, "Date value"); Calendar cal = org.apache.commons.lang3.time.DateUtils.toCalendar(theDateValue); + SimpleDateFormat format = new SimpleDateFormat(PATTERN_INTEGER_DATE); + String theDateString = format.format(theDateValue); + String s = String.valueOf(cal.get(Calendar.YEAR)) + cal.get(Calendar.MONTH) + cal.get(Calendar.DAY_OF_MONTH); - return Integer.parseInt(s); + return Integer.parseInt(theDateString); } /** diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 66687daf9dc..5e7e20702da 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -3960,7 +3960,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertEquals(1, results.getResources(0, 10).size()); // We expect a new one because we don't cache the search URL for very long search URLs assertEquals(2, mySearchEntityDao.count()); - } @Test @@ -4094,6 +4093,7 @@ 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()); } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateOrdinalDateTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateOrdinalDateTask.java new file mode 100644 index 00000000000..d77e23a9cb9 --- /dev/null +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateOrdinalDateTask.java @@ -0,0 +1,264 @@ +package ca.uhn.fhir.jpa.migrate.taskdef; + +/*- + * #%L + * HAPI FHIR JPA Server - Migration + * %% + * Copyright (C) 2014 - 2020 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.migrate.JdbcUtils; +import ca.uhn.fhir.util.StopWatch; +import ca.uhn.fhir.util.VersionEnum; +import com.google.common.collect.ForwardingMap; +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.ColumnMapRowMapper; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowCallbackHandler; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.Function; + +public class CalculateOrdinalDateTask extends BaseTableColumnTask { + + private static final Logger ourLog = LoggerFactory.getLogger(CalculateOrdinalDateTask.class); + private int myBatchSize = 10000; + private Map, Long>> myCalculators = new HashMap<>(); + private ThreadPoolExecutor myExecutor; + + public void setBatchSize(int theBatchSize) { + myBatchSize = theBatchSize; + } + + /** + * Constructor + */ + public CalculateOrdinalDateTask(VersionEnum theRelease, String theVersion) { + super(theRelease.toString(), theVersion); + setDescription("Calculate resource search parameter index hashes"); + } + + @Override + public synchronized void doExecute() throws SQLException { + if (isDryRun()) { + return; + } + + Set tableNames = JdbcUtils.getTableNames(getConnectionProperties()); + initializeExecutor(); + try { + + while(true) { + MyRowCallbackHandler rch = new MyRowCallbackHandler(); + getTxTemplate().execute(t -> { + JdbcTemplate jdbcTemplate = newJdbcTemplate(); + jdbcTemplate.setMaxRows(100000); + String sql = "SELECT * FROM " + getTableName() + " WHERE " + getColumnName() + " IS NULL"; + logInfo(ourLog, "Finding up to {} rows in {} that requires hashes", myBatchSize, getTableName()); + + jdbcTemplate.query(sql, rch); + rch.done(); + + return null; + }); + + rch.submitNext(); + List> futures = rch.getFutures(); + if (futures.isEmpty()) { + break; + } + + logInfo(ourLog, "Waiting for {} tasks to complete", futures.size()); + for (Future next : futures) { + try { + next.get(); + } catch (Exception e) { + throw new SQLException(e); + } + } + + } + + } finally { + destroyExecutor(); + } + } + + private void destroyExecutor() { + myExecutor.shutdownNow(); + } + + private void initializeExecutor() { + int maximumPoolSize = Runtime.getRuntime().availableProcessors(); + + LinkedBlockingQueue executorQueue = new LinkedBlockingQueue<>(maximumPoolSize); + BasicThreadFactory threadFactory = new BasicThreadFactory.Builder() + .namingPattern("worker-" + "-%d") + .daemon(false) + .priority(Thread.NORM_PRIORITY) + .build(); + RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() { + @Override + public void rejectedExecution(Runnable theRunnable, ThreadPoolExecutor theExecutor) { + logInfo(ourLog, "Note: Executor queue is full ({} elements), waiting for a slot to become available!", executorQueue.size()); + StopWatch sw = new StopWatch(); + try { + executorQueue.put(theRunnable); + } catch (InterruptedException theE) { + throw new RejectedExecutionException("Task " + theRunnable.toString() + + " rejected from " + theE.toString()); + } + logInfo(ourLog, "Slot become available after {}ms", sw.getMillis()); + } + }; + myExecutor = new ThreadPoolExecutor( + 1, + maximumPoolSize, + 0L, + TimeUnit.MILLISECONDS, + executorQueue, + threadFactory, + rejectedExecutionHandler); + } + + private Future updateRows(List> theRows) { + Runnable task = () -> { + StopWatch sw = new StopWatch(); + getTxTemplate().execute(t -> { + + // Loop through rows + assert theRows != null; + for (Map nextRow : theRows) { + + Map newValues = new HashMap<>(); + MandatoryKeyMap nextRowMandatoryKeyMap = new MandatoryKeyMap<>(nextRow); + + // Apply calculators + for (Map.Entry, Long>> nextCalculatorEntry : myCalculators.entrySet()) { + String nextColumn = nextCalculatorEntry.getKey(); + Function, Long> nextCalculator = nextCalculatorEntry.getValue(); + Long value = nextCalculator.apply(nextRowMandatoryKeyMap); + newValues.put(nextColumn, value); + } + + // Generate update SQL + StringBuilder sqlBuilder = new StringBuilder(); + List arguments = new ArrayList<>(); + sqlBuilder.append("UPDATE "); + sqlBuilder.append(getTableName()); + sqlBuilder.append(" SET "); + for (Map.Entry nextNewValueEntry : newValues.entrySet()) { + if (arguments.size() > 0) { + sqlBuilder.append(", "); + } + sqlBuilder.append(nextNewValueEntry.getKey()).append(" = ?"); + arguments.add(nextNewValueEntry.getValue()); + } + sqlBuilder.append(" WHERE SP_ID = ?"); + arguments.add((Number) nextRow.get("SP_ID")); + + // Apply update SQL + newJdbcTemplate().update(sqlBuilder.toString(), arguments.toArray()); + + } + + return theRows.size(); + }); + logInfo(ourLog, "Updated {} rows on {} in {}", theRows.size(), getTableName(), sw.toString()); + }; + return myExecutor.submit(task); + } + + public CalculateOrdinalDateTask addCalculator(String theColumnName, Function, Long> theConsumer) { + Validate.isTrue(myCalculators.containsKey(theColumnName) == false); + myCalculators.put(theColumnName, theConsumer); + return this; + } + + private class MyRowCallbackHandler implements RowCallbackHandler { + + private List> myRows = new ArrayList<>(); + private List> myFutures = new ArrayList<>(); + + @Override + public void processRow(ResultSet rs) throws SQLException { + Map row = new ColumnMapRowMapper().mapRow(rs, 0); + myRows.add(row); + + if (myRows.size() >= myBatchSize) { + submitNext(); + } + } + + private void submitNext() { + if (myRows.size() > 0) { + myFutures.add(updateRows(myRows)); + myRows = new ArrayList<>(); + } + } + + public List> getFutures() { + return myFutures; + } + + public void done() { + if (myRows.size() > 0) { + submitNext(); + } + } + } + + + public static class MandatoryKeyMap extends ForwardingMap { + + private final Map myWrap; + + public MandatoryKeyMap(Map theWrap) { + myWrap = theWrap; + } + + @Override + public V get(Object theKey) { + if (!containsKey(theKey)) { + throw new IllegalArgumentException("No key: " + theKey); + } + return super.get(theKey); + } + + public String getString(String theKey) { + return (String) get(theKey); + } + + @Override + protected Map delegate() { + return myWrap; + } + + public String getResourceType() { + return getString("RES_TYPE"); + } + + public String getParamName() { + return getString("SP_NAME"); + } + } +} diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 05b94668006..59f5720002b 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -71,8 +71,14 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { // // Add support for integer comparisons during day-granularity date search. - version.onTable("HFJ_SPIDX_DATE").addColumn("20200225.1", "SP_VALUE_LOW_DATE_ORDINAL").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.INT); - version.onTable("HFJ_SPIDX_DATE").addColumn("20200225.1", "SP_VALUE_HIGH_DATE_ORDINAL").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.INT); + Builder.BuilderWithTableName spidxDate = version.onTable("HFJ_SPIDX_DATE"); + spidxDate.addColumn("20200225.1", "SP_VALUE_LOW_DATE_ORDINAL").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.INT); + spidxDate.addColumn("20200225.2", "SP_VALUE_HIGH_DATE_ORDINAL").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.INT); + spidxDate.addTask(new CalculateHashesTask(VersionEnum.V4_3_0, "20200225.3") + .setColumnName("HASH_IDENTITY") + .addCalculator("SP_VALUE_LOW_DATE_ORDINAL", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME"))) + .addCalculator("SP_VALUE_HIGH_DATE_ORDINAL", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME"))) + ); // } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java index eb5e6097c6b..d4a19d9f022 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java @@ -88,24 +88,26 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar /** * Constructor */ - public ResourceIndexedSearchParamDate(String theResourceType, String theParamName, Date theLow, Date theHigh, String theOriginalValue) { + public ResourceIndexedSearchParamDate(String theResourceType, String theParamName, Date theLow, String theLowString, Date theHigh, String theHighString, String theOriginalValue) { setResourceType(theResourceType); setParamName(theParamName); setValueLow(theLow); setValueHigh(theHigh); - computeValueHighDateOrdinal(theHigh); - computeValueLowDateOrdinal(theLow); + computeValueHighDateOrdinal(theHighString); + computeValueLowDateOrdinal(theLowString); myOriginalValue = theOriginalValue; } - private void computeValueHighDateOrdinal(Date theHigh) { + private void computeValueHighDateOrdinal(String theHigh) { this.myValueHighDateOrdinal = generateOrdinalDateInteger(theHigh); } - private int generateOrdinalDateInteger(Date theDate) { - return ca.uhn.fhir.util.DateUtils.convertDatetoDayInteger(theDate); + private int generateOrdinalDateInteger(String theDateString){ + String t = theDateString.substring(0, theDateString.indexOf("T")); + t = t.replace("-", ""); + return Integer.valueOf(t); } - private void computeValueLowDateOrdinal(Date theLow) { + private void computeValueLowDateOrdinal(String theLow) { this.myValueLowDateOrdinal = generateOrdinalDateInteger(theLow); } diff --git a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDateTest.java b/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDateTest.java index c45245aa3b1..407d05163f5 100644 --- a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDateTest.java +++ b/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDateTest.java @@ -35,8 +35,8 @@ public class ResourceIndexedSearchParamDateTest { @Test public void equalsIsTrueForMatchingNullDates() { - ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", null, null, "SomeValue"); - ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", null, null, "SomeValue"); + ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", null, null, null, null, "SomeValue"); + ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", null, null, null, null, "SomeValue"); assertTrue(param.equals(param2)); assertTrue(param2.equals(param)); @@ -45,8 +45,8 @@ public class ResourceIndexedSearchParamDateTest { @Test public void equalsIsTrueForMatchingDates() { - ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue"); - ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1B, date2B, "SomeValue"); + ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date1A.toString(), date2A, date2A.toString(), "SomeValue"); + ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1B, date1B.toString(), date2B, date2B.toString(), "SomeValue"); assertTrue(param.equals(param2)); assertTrue(param2.equals(param)); @@ -55,8 +55,8 @@ public class ResourceIndexedSearchParamDateTest { @Test public void equalsIsTrueForMatchingTimeStampsThatMatch() { - ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1A, timestamp2A, "SomeValue"); - ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1B, timestamp2B, "SomeValue"); + ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1A, timestamp1A.toString(), timestamp2A, timestamp2A.toString(), "SomeValue"); + ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1B, timestamp1B.toString(), timestamp2B, timestamp2B.toString(), "SomeValue"); assertTrue(param.equals(param2)); assertTrue(param2.equals(param)); @@ -67,8 +67,8 @@ public class ResourceIndexedSearchParamDateTest { // other will be equivalent but will be a java.sql.Timestamp. Equals should work in both directions. @Test public void equalsIsTrueForMixedTimestampsAndDates() { - ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue"); - ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1A, timestamp2A, "SomeValue"); + ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date1A.toString(), date2A, date2A.toString(), "SomeValue"); + ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1A, timestamp1A.toString(), timestamp2A, timestamp2A.toString(), "SomeValue"); assertTrue(param.equals(param2)); assertTrue(param2.equals(param)); @@ -77,8 +77,8 @@ public class ResourceIndexedSearchParamDateTest { @Test public void equalsIsFalseForNonMatchingDates() { - ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue"); - ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date2A, date1A, "SomeValue"); + ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date1A.toString(), date2A, date2A.toString(), "SomeValue"); + ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date2A, date2A.toString(), date1A, date1A.toString(), "SomeValue"); assertFalse(param.equals(param2)); assertFalse(param2.equals(param)); @@ -87,8 +87,8 @@ public class ResourceIndexedSearchParamDateTest { @Test public void equalsIsFalseForNonMatchingDatesNullCase() { - ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue"); - ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", null, null, "SomeValue"); + ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date1A.toString(), date2A, date2A.toString(), "SomeValue"); + ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", null, null, null, null, "SomeValue"); assertFalse(param.equals(param2)); assertFalse(param2.equals(param)); @@ -97,8 +97,8 @@ public class ResourceIndexedSearchParamDateTest { @Test public void equalsIsFalseForNonMatchingTimeStamps() { - ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1A, timestamp2A, "SomeValue"); - ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp2A, timestamp1A, "SomeValue"); + ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1A, timestamp1A.toString(), timestamp2A, timestamp2A.toString(), "SomeValue"); + ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp2A, timestamp2A.toString(), timestamp1A, timestamp1A.toString(), "SomeValue"); assertFalse(param.equals(param2)); assertFalse(param2.equals(param)); @@ -107,8 +107,8 @@ public class ResourceIndexedSearchParamDateTest { @Test public void equalsIsFalseForMixedTimestampsAndDatesThatDoNotMatch() { - ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue"); - ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp2A, timestamp1A, "SomeValue"); + ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date1A.toString(), date2A, date2A.toString(), "SomeValue"); + ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp2A, timestamp2A.toString(), timestamp1A, timestamp1A.toString(), "SomeValue"); assertFalse(param.equals(param2)); assertFalse(param2.equals(param)); diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java index 79bf1c41f4a..d3b42491c51 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java @@ -605,9 +605,10 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor Date start = extractValueAsDate(myPeriodStartValueChild, theValue); String startAsString = extractValueAsString(myPeriodStartValueChild, theValue); Date end = extractValueAsDate(myPeriodEndValueChild, theValue); + String endAsString = extractValueAsString(myPeriodEndValueChild, theValue); if (start != null || end != null) { - ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(theResourceType, theSearchParam.getName(), start, end, startAsString); + ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(theResourceType, theSearchParam.getName(), start, startAsString, end, endAsString, startAsString); theParams.add(nextEntity); } } @@ -616,13 +617,17 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor List> values = extractValuesAsFhirDates(myTimingEventValueChild, theValue); TreeSet dates = new TreeSet<>(); + TreeSet dateStrings = new TreeSet<>(); String firstValue = null; + String finalValue = null; for (IPrimitiveType nextEvent : values) { if (nextEvent.getValue() != null) { dates.add(nextEvent.getValue()); + if (firstValue == null) { firstValue = nextEvent.getValueAsString(); } + finalValue = nextEvent.getValueAsString(); } } @@ -634,14 +639,16 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor if ("Period".equals(boundsType)) { Date start = extractValueAsDate(myPeriodStartValueChild, bounds.get()); Date end = extractValueAsDate(myPeriodEndValueChild, bounds.get()); + String endString = extractValueAsString(myPeriodEndValueChild, bounds.get()); dates.add(start); dates.add(end); + finalValue = endString; } } } if (!dates.isEmpty()) { - ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(theResourceType, theSearchParam.getName(), dates.first(), dates.last(), firstValue); + ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(theResourceType, theSearchParam.getName(), dates.first(), firstValue, dates.last(), finalValue, firstValue); theParams.add(nextEntity); } } @@ -828,7 +835,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor private void addDateTimeTypes(String theResourceType, Set theParams, RuntimeSearchParam theSearchParam, IBase theValue) { IPrimitiveType nextBaseDateTime = (IPrimitiveType) theValue; if (nextBaseDateTime.getValue() != null) { - ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(theResourceType, theSearchParam.getName(), nextBaseDateTime.getValue(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString()); + ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(theResourceType, theSearchParam.getName(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString(), nextBaseDateTime.getValueAsString()); theParams.add(param); } } diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java index 2a80eb7686e..bb111f87e86 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java @@ -208,7 +208,7 @@ public class InMemoryResourceMatcherR5Test { private ResourceIndexedSearchParams extractDateSearchParam(Observation theObservation) { ResourceIndexedSearchParams retval = new ResourceIndexedSearchParams(); BaseDateTimeType dateValue = (BaseDateTimeType) theObservation.getEffective(); - ResourceIndexedSearchParamDate dateParam = new ResourceIndexedSearchParamDate("Patient", "date", dateValue.getValue(), dateValue.getValue(), dateValue.getValueAsString()); + ResourceIndexedSearchParamDate dateParam = new ResourceIndexedSearchParamDate("Patient", "date", dateValue.getValue(), dateValue.getValueAsString(), dateValue.getValue(), dateValue.getValueAsString(), dateValue.getValueAsString()); retval.myDateParams.add(dateParam); return retval; }