Work on combo
This commit is contained in:
parent
9143f5995b
commit
c2ed5f8419
|
@ -314,9 +314,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
|||
* parameters all have no modifiers.
|
||||
*/
|
||||
private boolean isCompositeUniqueSpCandidate() {
|
||||
return myStorageSettings.isUniqueIndexesEnabled()
|
||||
&& myParams.getEverythingMode() == null
|
||||
&& myParams.isAllParametersHaveNoModifier();
|
||||
return myStorageSettings.isUniqueIndexesEnabled() && myParams.getEverythingMode() == null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
|
@ -1954,7 +1952,8 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
|||
IQueryParameterType nextOr = nextPermutation.get(paramIndex);
|
||||
String nextOrValue = nextOr.getValueAsQueryToken(myContext);
|
||||
|
||||
RuntimeSearchParam nextParamDef = mySearchParamRegistry.getActiveSearchParam(myResourceName, nextParamName);
|
||||
RuntimeSearchParam nextParamDef =
|
||||
mySearchParamRegistry.getActiveSearchParam(myResourceName, nextParamName);
|
||||
if (theComboParam.getComboSearchParamType() == ComboSearchParamType.NON_UNIQUE) {
|
||||
if (nextParamDef.getParamType() == RestSearchParameterTypeEnum.STRING) {
|
||||
nextOrValue = StringUtil.normalizeStringForSearchIndexing(nextOrValue);
|
||||
|
@ -1975,7 +1974,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
|||
|
||||
String indexString = searchStringBuilder.toString();
|
||||
ourLog.debug(
|
||||
"Checking for {} combo index for query: {}", theComboParam.getComboSearchParamType(), indexString);
|
||||
"Checking for {} combo index for query: {}", theComboParam.getComboSearchParamType(), indexString);
|
||||
|
||||
indexStrings.add(indexString);
|
||||
}
|
||||
|
@ -2008,10 +2007,16 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
|||
theParams.clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@literal true} if the actual parameter instances in a given query are actually usable for
|
||||
* searching against a combo param with the given parameter names. This might be {@literal false} if
|
||||
* parameters have modifiers (e.g. <code>?name:exact=SIMPSON</code>), prefixes
|
||||
* (e.g. <code>?date=gt2024-02-01</code>), etc.
|
||||
*/
|
||||
private boolean validateParamValuesAreValidForComboParam(
|
||||
RequestDetails theRequest, @Nonnull SearchParameterMap theParams, List<String> comboParamNames) {
|
||||
RequestDetails theRequest, @Nonnull SearchParameterMap theParams, List<String> theComboParamNames) {
|
||||
boolean paramValuesAreValidForCombo = true;
|
||||
for (String nextParamName : comboParamNames) {
|
||||
for (String nextParamName : theComboParamNames) {
|
||||
List<List<IQueryParameterType>> nextValues = theParams.get(nextParamName);
|
||||
|
||||
if (nextValues.isEmpty()) {
|
||||
|
@ -2024,7 +2029,9 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
|||
if (nextOrValue instanceof DateParam) {
|
||||
DateParam dateParam = (DateParam) nextOrValue;
|
||||
if (dateParam.getPrecision() != TemporalPrecisionEnum.DAY) {
|
||||
String message = "Search with params " + comboParamNames + " is not a candidate for combo searching - Date search with non-DAY precision for parameter '" + nextParamName + "'";
|
||||
String message = "Search with params " + theComboParamNames
|
||||
+ " is not a candidate for combo searching - Date search with non-DAY precision for parameter '"
|
||||
+ nextParamName + "'";
|
||||
firePerformanceInfo(theRequest, message);
|
||||
paramValuesAreValidForCombo = false;
|
||||
break;
|
||||
|
@ -2033,12 +2040,23 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
|||
if (nextOrValue instanceof BaseParamWithPrefix) {
|
||||
BaseParamWithPrefix<?> paramWithPrefix = (BaseParamWithPrefix<?>) nextOrValue;
|
||||
if (paramWithPrefix.getPrefix() != null) {
|
||||
String message = "Search with params " + comboParamNames + " is not a candidate for combo searching - Parameter '" + nextParamName + "' has prefix: '" + paramWithPrefix.getPrefix().getValue() + "'";
|
||||
String message = "Search with params " + theComboParamNames
|
||||
+ " is not a candidate for combo searching - Parameter '" + nextParamName
|
||||
+ "' has prefix: '"
|
||||
+ paramWithPrefix.getPrefix().getValue() + "'";
|
||||
firePerformanceInfo(theRequest, message);
|
||||
paramValuesAreValidForCombo = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isNotBlank(nextOrValue.getQueryParameterQualifier())) {
|
||||
String message = "Search with params " + theComboParamNames
|
||||
+ " is not a candidate for combo searching - Parameter '" + nextParamName
|
||||
+ "' has modifier: '" + nextOrValue.getQueryParameterQualifier() + "'";
|
||||
firePerformanceInfo(theRequest, message);
|
||||
paramValuesAreValidForCombo = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2452,8 +2470,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
|||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||
.add(StorageProcessingMessage.class, message);
|
||||
CompositeInterceptorBroadcaster.doCallHooks(
|
||||
myInterceptorBroadcaster, theRequest, pointcut, params);
|
||||
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, pointcut, params);
|
||||
}
|
||||
|
||||
public static int getMaximumPageSize() {
|
||||
|
|
|
@ -44,19 +44,20 @@ public class ComboNonUniqueSearchParameterPredicateBuilder extends BaseSearchPar
|
|||
myColumnHashComplete = getTable().addColumn("HASH_COMPLETE");
|
||||
}
|
||||
|
||||
public Condition createPredicateHashComplete(RequestPartitionId theRequestPartitionId, List<String> theIndexStrings) {
|
||||
public Condition createPredicateHashComplete(
|
||||
RequestPartitionId theRequestPartitionId, List<String> theIndexStrings) {
|
||||
PartitionablePartitionId partitionId =
|
||||
PartitionablePartitionId.toStoragePartition(theRequestPartitionId, getPartitionSettings());
|
||||
PartitionablePartitionId.toStoragePartition(theRequestPartitionId, getPartitionSettings());
|
||||
Condition predicate;
|
||||
if (theIndexStrings.size() == 1) {
|
||||
long hash = ResourceIndexedComboTokenNonUnique.calculateHashComplete(
|
||||
getPartitionSettings(), partitionId, theIndexStrings.get(0));
|
||||
getPartitionSettings(), partitionId, theIndexStrings.get(0));
|
||||
predicate = BinaryCondition.equalTo(myColumnHashComplete, generatePlaceholder(hash));
|
||||
} else {
|
||||
List<Long> hashes = theIndexStrings
|
||||
.stream()
|
||||
.map(t -> ResourceIndexedComboTokenNonUnique.calculateHashComplete(getPartitionSettings(), partitionId, t))
|
||||
.collect(Collectors.toList());
|
||||
List<Long> hashes = theIndexStrings.stream()
|
||||
.map(t -> ResourceIndexedComboTokenNonUnique.calculateHashComplete(
|
||||
getPartitionSettings(), partitionId, t))
|
||||
.collect(Collectors.toList());
|
||||
predicate = new InCondition(myColumnHashComplete, generatePlaceholders(hashes));
|
||||
}
|
||||
return combineWithRequestPartitionIdPredicate(theRequestPartitionId, predicate);
|
||||
|
|
|
@ -41,7 +41,8 @@ public class ComboUniqueSearchParameterPredicateBuilder extends BaseSearchParamP
|
|||
myColumnString = getTable().addColumn("IDX_STRING");
|
||||
}
|
||||
|
||||
public Condition createPredicateIndexString(RequestPartitionId theRequestPartitionId, List<String> theIndexStrings) {
|
||||
public Condition createPredicateIndexString(
|
||||
RequestPartitionId theRequestPartitionId, List<String> theIndexStrings) {
|
||||
Condition predicate;
|
||||
if (theIndexStrings.size() == 1) {
|
||||
predicate = BinaryCondition.equalTo(myColumnString, generatePlaceholder(theIndexStrings.get(0)));
|
||||
|
|
|
@ -65,13 +65,15 @@ public class PermutationBuilder {
|
|||
* @param <T> The type associated with {@literal theInput}. The actual type doesn't matter, this method does not look at the
|
||||
* values at all other than to copy them to the output lists.
|
||||
*/
|
||||
private static <T> void doCalculatePermutationsIntoIndicesArrayAndPopulateList(int thePositionX, int[] theIndices, List<List<T>> theInput, List<List<T>> theOutput) {
|
||||
private static <T> void doCalculatePermutationsIntoIndicesArrayAndPopulateList(
|
||||
int thePositionX, int[] theIndices, List<List<T>> theInput, List<List<T>> theOutput) {
|
||||
if (thePositionX != theInput.size()) {
|
||||
// If we're not at the end of the list of input vectors, recursively self-invoke once for each
|
||||
// possible option at the current position in the list of input vectors.
|
||||
for (int positionY = 0; positionY < theInput.get(thePositionX).size(); positionY++) {
|
||||
theIndices[thePositionX] = positionY;
|
||||
doCalculatePermutationsIntoIndicesArrayAndPopulateList(thePositionX + 1, theIndices, theInput, theOutput);
|
||||
doCalculatePermutationsIntoIndicesArrayAndPopulateList(
|
||||
thePositionX + 1, theIndices, theInput, theOutput);
|
||||
}
|
||||
} else {
|
||||
// If we're at the end of the list of input vectors, then we've been passed the
|
||||
|
@ -97,5 +99,4 @@ public class PermutationBuilder {
|
|||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -353,22 +353,6 @@ public class SearchParameterMap implements Serializable {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will only return true if all parameters have no modifier of any kind
|
||||
*/
|
||||
public boolean isAllParametersHaveNoModifier() {
|
||||
for (List<List<IQueryParameterType>> nextParamName : values()) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamName) {
|
||||
for (IQueryParameterType nextOr : nextAnd) {
|
||||
if (isNotBlank(nextOr.getQueryParameterQualifier())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, tells the server to load these results synchronously, and not to load
|
||||
* more than X results
|
||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboTokenNonUnique;
|
||||
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.submit.interceptor.SearchParamValidatingInterceptor;
|
||||
import ca.uhn.fhir.jpa.util.SqlQuery;
|
||||
|
@ -58,6 +59,8 @@ public class FhirResourceDaoR4ComboNonUniqueParamTest extends BaseComboParamsR4T
|
|||
|
||||
@AfterEach
|
||||
public void restoreInterceptor() {
|
||||
myStorageSettings.setIndexMissingFields(new StorageSettings().getIndexMissingFields());
|
||||
|
||||
if (myInterceptorFound) {
|
||||
myInterceptorService.unregisterInterceptor(mySearchParamValidatingInterceptor);
|
||||
}
|
||||
|
@ -765,6 +768,58 @@ public class FhirResourceDaoR4ComboNonUniqueParamTest extends BaseComboParamsR4T
|
|||
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
public void testStringModifier(boolean theUseExact) {
|
||||
IIdType id1 = createObservation("2021-01-02");
|
||||
createObservation("2023-01-02");
|
||||
|
||||
SearchParameterMap params = SearchParameterMap
|
||||
.newSynchronous()
|
||||
.add("note-text", new StringParam("Hello").setExact(theUseExact))
|
||||
.add("date", new DateParam("2021-01-02"));
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider results = myObservationDao.search(params, mySrd);
|
||||
List<String> actual = toUnqualifiedVersionlessIdValues(results);
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
assertThat(actual).contains(id1.toUnqualifiedVersionless().getValue());
|
||||
|
||||
if (theUseExact) {
|
||||
assertComboIndexNotUsed();
|
||||
assertThat(myMessages.toString()).contains("INFO Search with params [date, note-text] is not a candidate for combo searching - Parameter 'note-text' has modifier: ':exact'");
|
||||
} else {
|
||||
assertComboIndexUsed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
public void testMissing(boolean theUseMissing) {
|
||||
myStorageSettings.setIndexMissingFields(StorageSettings.IndexEnabledEnum.ENABLED);
|
||||
|
||||
IIdType id1 = createObservation("2021-01-02");
|
||||
createObservation("2023-01-02");
|
||||
|
||||
SearchParameterMap params = SearchParameterMap
|
||||
.newSynchronous()
|
||||
.add("note-text", new StringParam("Hello").setMissing(theUseMissing ? false : null))
|
||||
.add("date", new DateParam("2021-01-02"));
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider results = myObservationDao.search(params, mySrd);
|
||||
List<String> actual = toUnqualifiedVersionlessIdValues(results);
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
assertThat(actual).contains(id1.toUnqualifiedVersionless().getValue());
|
||||
|
||||
if (theUseMissing) {
|
||||
assertComboIndexNotUsed();
|
||||
assertThat(myMessages.toString()).contains("INFO Search with params [date, note-text] is not a candidate for combo searching - Parameter 'note-text' has modifier: ':missing'");
|
||||
} else {
|
||||
assertComboIndexUsed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void assertComboIndexUsed() {
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(sql).contains("HFJ_IDX_CMB_TOK_NU");
|
||||
|
|
Loading…
Reference in New Issue