Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed an issue with the _lastUpdated search param not being recognised as a valid sort spec with Elastic Search enabled. #6670

Merged
merged 20 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
type: fix
AbayomiE-T marked this conversation as resolved.
Show resolved Hide resolved
issue: 6697
title: "Previously, operation $apply-codesystem-delta-add issued with Hibernate Search enabled and default search params option turned off resulted in an invalid sort specification error. This has been fixed."
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
Expand All @@ -46,12 +47,19 @@
import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.QTY_VALUE_NORM;
import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.SEARCH_PARAM_ROOT;
import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.URI_VALUE;
import static java.util.Objects.isNull;

/**
* Used to build HSearch sort clauses.
*/
public class HSearchSortHelperImpl implements IHSearchSortHelper {
private static final Logger ourLog = LoggerFactory.getLogger(HSearchSortHelperImpl.class);
public static final Map<String, RestSearchParameterTypeEnum> ourSortingParamNameToParamType = Map.of(
Constants.PARAM_LASTUPDATED, RestSearchParameterTypeEnum.DATE,
Constants.PARAM_ID, RestSearchParameterTypeEnum.TOKEN,
Constants.PARAM_TAG, RestSearchParameterTypeEnum.TOKEN,
Constants.PARAM_SECURITY, RestSearchParameterTypeEnum.TOKEN,
Constants.PARAM_SOURCE, RestSearchParameterTypeEnum.TOKEN);

/** Indicates which HSearch properties must be sorted for each RestSearchParameterTypeEnum **/
private Map<RestSearchParameterTypeEnum, List<String>> mySortPropertyListMap = Map.of(
Expand Down Expand Up @@ -151,14 +159,20 @@ Optional<SortFinalStep> getSortClause(SearchSortFactory theF, SortSpec theSortSp
*/
@VisibleForTesting
Optional<RestSearchParameterTypeEnum> getParamType(String theResourceTypeName, String theParamName) {
AbayomiE-T marked this conversation as resolved.
Show resolved Hide resolved
RestSearchParameterTypeEnum value;

ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams(
theResourceTypeName, ISearchParamRegistry.SearchParamLookupContextEnum.SEARCH);
RuntimeSearchParam searchParam = activeSearchParams.get(theParamName);

if (searchParam == null) {
return Optional.empty();
AbayomiE-T marked this conversation as resolved.
Show resolved Hide resolved
value = isNull(theParamName) ? null : ourSortingParamNameToParamType.get(theParamName);

} else {
value = searchParam.getParamType();
}

return Optional.of(searchParam.getParamType());
return Optional.ofNullable(value);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public UploadStatistics applyDeltaCodeSystemsAdd(String theSystem, CustomTermino
Validate.notNull(csv);

CodeSystem codeSystem = myTerminologySvc.fetchCanonicalCodeSystemFromCompleteContext(theSystem);
if (codeSystem.getContent() != CodeSystem.CodeSystemContentMode.NOTPRESENT) {
if (codeSystem != null && codeSystem.getContent() != CodeSystem.CodeSystemContentMode.NOTPRESENT) {
throw new InvalidRequestException(
Msg.code(844) + "CodeSystem with url[" + Constants.codeSystemWithDefaultDescription(theSystem)
+ "] can not apply a delta - wrong content mode: " + codeSystem.getContent());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.dao.search;

import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
Expand All @@ -13,13 +14,17 @@
import org.hibernate.search.engine.search.sort.dsl.SortFinalStep;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
Expand Down Expand Up @@ -81,6 +86,36 @@ void testGetParamType() {
assertFalse(paramType.isEmpty());
}

private static Stream<Arguments> provideArgumentsForGetParamType() {
Stream.Builder<Arguments> retVal = Stream.builder();
HSearchSortHelperImpl.ourSortingParamNameToParamType.forEach((theSortSpecName, theRestSearchParameterTypeEnum) ->
{
SortSpec sortSpec = new SortSpec(theSortSpecName);
retVal.add(Arguments.of(sortSpec, Optional.of(theRestSearchParameterTypeEnum)));
});

return retVal.build();
}
/**
* Validates that getParamType() returns a param type when _id, _lastUpdated, _tag, _security and _source are absent from
* the search param registry.
*/
@ParameterizedTest
@MethodSource("provideArgumentsForGetParamType")
void testGetParamTypeWhenParamNameIsNotInSearchParamRegistry(SortSpec sortSpec, Optional<RestSearchParameterTypeEnum> expectedSearchParamType) {
//Given that we have params absent from the SearchParamsRegistry
String resourceType = "CodeSystem";
String absentSearchParam = sortSpec.getParamName();
when(mockSearchParamRegistry.getActiveSearchParams(eq(resourceType), any())).thenReturn(mockResourceSearchParams);
when(mockResourceSearchParams.get(absentSearchParam)).thenReturn(null);

//Execute
AbayomiE-T marked this conversation as resolved.
Show resolved Hide resolved
Optional<RestSearchParameterTypeEnum> paramType = tested.getParamType(resourceType, absentSearchParam);

//Validate
assertThat(paramType).isEqualTo(expectedSearchParamType);
}

@Test
void testGetSortClause() {
SortSpec sortSpec = new SortSpec();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import ca.uhn.fhir.jpa.rp.r4.PatientResourceProvider;
import ca.uhn.fhir.jpa.search.BaseSourceSearchParameterTestCases;
import ca.uhn.fhir.jpa.search.CompositeSearchParameterTestCases;
import ca.uhn.fhir.jpa.search.IIdSearchTestTemplate;
import ca.uhn.fhir.jpa.search.QuantitySearchParameterTestCases;
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchRestClientFactory;
Expand Down Expand Up @@ -2616,5 +2617,16 @@ protected void beforeOrAfterTestClass(TestContext testContext, DirtiesContext.Cl
}
}

@Nested
class IdTestCases implements IIdSearchTestTemplate {
@Override
public TestDaoSearch getSearch() {
return myTestDaoSearch;
}

@Override
public ITestDataBuilder getBuilder() {
return myTestDataBuilder;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,37 @@ public void testApplyDeltaAdd_UsingCodeSystem() {
);
}

@Test
public void testApplyDeltaAdd_UsingCodeSystemWithElasticSearch() {
//Given: Advance HSearch indexing is enabled
myStorageSettings.setHibernateSearchIndexFullText(true);
myStorageSettings.setHibernateSearchIndexSearchParams(true);
myStorageSettings.setStoreResourceInHSearchIndex(true);

//Given: We have a non-existent code system
AbayomiE-T marked this conversation as resolved.
Show resolved Hide resolved
CodeSystem codeSystem = new CodeSystem();
myClient.create().resource(codeSystem).execute();
CodeSystem.ConceptDefinitionComponent chem = codeSystem.addConcept().setCode("CHEM").setDisplay("Chemistry");
chem.addConcept().setCode("HB").setDisplay("Hemoglobin");
chem.addConcept().setCode("NEUT").setDisplay("Neutrophils");
CodeSystem.ConceptDefinitionComponent micro = codeSystem.addConcept().setCode("MICRO").setDisplay("Microbiology");
micro.addConcept().setCode("C&S").setDisplay("Culture And Sensitivity");

//Execute
Parameters outcome = myClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD)
.withParameter(Parameters.class, TerminologyUploaderProvider.PARAM_SYSTEM, new UriType("http://example.com/cs"))
.andParameter(TerminologyUploaderProvider.PARAM_CODESYSTEM, codeSystem)
.prettyPrint()
.execute();

//Validate
IntegerType conceptCount = (IntegerType) outcome.getParameter("conceptCount").getValue();
assertThat(conceptCount.getValue()).isEqualTo(5);
}

@Test
public void testApplyDeltaAdd_UsingCodeSystemWithConceptProprieties() {
CodeSystem codeSystem = new CodeSystem();
Expand Down
Loading