Allow terminology service to work without having Lucene enabled for

simple ValueSet expansions
This commit is contained in:
James Agnew 2019-02-14 20:47:28 -05:00
parent 10c348d44f
commit 485335e975
7 changed files with 119 additions and 30 deletions

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.parser;
* 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.

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.jaxrs.server.util;
* 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.

View File

@ -98,9 +98,9 @@ import static org.apache.commons.lang3.StringUtils.*;
* 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.

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term;
* 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.
@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
@ -139,35 +140,41 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
private TransactionTemplate myTxTemplate;
@Autowired
private PlatformTransactionManager myTransactionManager;
@Autowired(required = false)
private IFulltextSearchSvc myFulltextSearchSvc;
/**
* @param theAdd If true, add the code. If false, remove the code.
* @param theCodeCounter
*/
private void addCodeIfNotAlreadyAdded(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, TermConcept theConcept, boolean theAdd, AtomicInteger theCodeCounter) {
String codeSystem = theConcept.getCodeSystemVersion().getCodeSystem().getCodeSystemUri();
String code = theConcept.getCode();
if (theAdd && theAddedCodes.add(code)) {
String codeSystem = theConcept.getCodeSystemVersion().getCodeSystem().getCodeSystemUri();
String display = theConcept.getDisplay();
Collection<TermConceptDesignation> designations = theConcept.getDesignations();
addCodeIfNotAlreadyAdded(theExpansionComponent, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, code, display);
}
private void addCodeIfNotAlreadyAdded(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, Collection<TermConceptDesignation> theDesignations, boolean theAdd, AtomicInteger theCodeCounter, String theCodeSystem, String theCode, String theDisplay) {
if (isNotBlank(theCode) && theAdd && theAddedCodes.add(theCode)) {
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
contains.setCode(code);
contains.setSystem(codeSystem);
contains.setDisplay(theConcept.getDisplay());
for (TermConceptDesignation nextDesignation : theConcept.getDesignations()) {
contains
.addDesignation()
.setValue(nextDesignation.getValue())
.getUse()
.setSystem(nextDesignation.getUseSystem())
.setCode(nextDesignation.getUseCode())
.setDisplay(nextDesignation.getUseDisplay());
contains.setCode(theCode);
contains.setSystem(theCodeSystem);
contains.setDisplay(theDisplay);
if (theDesignations != null) {
for (TermConceptDesignation nextDesignation : theDesignations) {
contains
.addDesignation()
.setValue(nextDesignation.getValue())
.getUse()
.setSystem(nextDesignation.getUseSystem())
.setCode(nextDesignation.getUseCode())
.setDisplay(nextDesignation.getUseDisplay());
}
}
theCodeCounter.incrementAndGet();
}
if (!theAdd && theAddedCodes.remove(code)) {
String codeSystem = theConcept.getCodeSystemVersion().getCodeSystem().getCodeSystemUri();
removeCodeFromExpansion(codeSystem, code, theExpansionComponent);
if (!theAdd && theAddedCodes.remove(theCode)) {
removeCodeFromExpansion(theCodeSystem, theCode, theExpansionComponent);
theCodeCounter.decrementAndGet();
}
}
@ -480,6 +487,19 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
if (cs != null) {
TermCodeSystemVersion csv = cs.getCurrentVersion();
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
/*
* If FullText searching is not enabled, we can handle only basic expansions
* since we're going to do it without the database.
*/
if (myFulltextSearchSvc == null) {
expandWithoutHibernateSearch(theExpansionComponent, theAddedCodes, theInclude, system, theAdd, theCodeCounter);
return;
}
/*
* Ok, let's use hibernate search to build the expansion
*/
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
BooleanJunction<?> bool = qb.bool();
@ -663,6 +683,20 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
}
}
private void expandWithoutHibernateSearch(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, ValueSet.ConceptSetComponent theInclude, String theSystem, boolean theAdd, AtomicInteger theCodeCounter) {
ourLog.trace("Hibernate search is not enabled");
Validate.isTrue(theExpansionComponent.getParameter().isEmpty(), "Can not exapnd ValueSet with parameters - Hibernate Search is not enabled on this server.");
Validate.isTrue(theInclude.getFilter().isEmpty(), "Can not expand ValueSet with filters - Hibernate Search is not enabled on this server.");
Validate.isTrue(isNotBlank(theSystem), "Can not expand ValueSet without explicit system - Hibernate Search is not enabled on this server.");
for (ValueSet.ConceptReferenceComponent next : theInclude.getConcept()) {
if (!theSystem.equals(theInclude.getSystem())) {
continue;
}
addCodeIfNotAlreadyAdded(theExpansionComponent, theAddedCodes, null, theAdd, theCodeCounter, theSystem, next.getCode(), next.getDisplay());
}
}
private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
TermConcept nextChild = nextChildLink.getChild();

View File

@ -8,9 +8,13 @@ import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.*;
@ -27,7 +31,11 @@ import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import java.util.List;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestR4WithoutLuceneConfig.class})
@ -62,6 +70,12 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
@Qualifier("myCodeSystemDaoR4")
private IFhirResourceDao<CodeSystem> myCodeSystemDao;
@Autowired
@Qualifier("myValueSetDaoR4")
private IFhirResourceDao<ValueSet> myValueSetDao;
@Autowired
@Qualifier("myObservationDaoR4")
private IFhirResourceDao<Observation> myObservationDao;
@Autowired
@Qualifier("myCompartmentDefinitionDaoR4")
private IFhirResourceDao<CompartmentDefinition> myCompartmentDefinitionDao;
@Autowired
@ -168,6 +182,42 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
}
}
@Test
public void testSearchByCodeIn() {
CodeSystem cs = new CodeSystem();
cs.setUrl("http://fooCS");
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
cs.addConcept().setCode("CODEA");
cs.addConcept().setCode("CODEB");
myCodeSystemDao.create(cs);
ValueSet vs = new ValueSet();
vs.setUrl("http://fooVS");
vs.getCompose()
.addInclude()
.setSystem("http://fooCS")
.addConcept(new ValueSet.ConceptReferenceComponent().setCode("CODEA"));
myValueSetDao.create(vs);
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://fooCS").setCode("CODEA");
String obs1id = myObservationDao.create(obs).getId().toUnqualifiedVersionless().getValue();
obs = new Observation();
obs.getCode().addCoding().setSystem("http://fooCS").setCode("CODEB");
myObservationDao.create(obs).getId().toUnqualifiedVersionless().getValue();
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add("code", new TokenParam("http://fooVS").setModifier(TokenParamModifier.IN));
IBundleProvider results = myObservationDao.search(map);
List<IBaseResource> resultsList = results.getResources(0, 10);
assertEquals(1, resultsList.size());
assertEquals(obs1id, resultsList.get(0).getIdElement().toUnqualifiedVersionless().getValue());
}
@AfterClass
public static void afterClassClearContext() {

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server;
* 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.

View File

@ -31,6 +31,11 @@
requesting extensions to be included caused the extensions to be included but
not any values contained within. This has been corrected.
</action>
<action type="add">
The JPA terminology service can now detect when Hibvernate Search (Lucene)
is not enabled, and will perform simple ValueSet expansions without relying
on Hibenrate Search in such cases.
</action>
</release>
<release version="3.7.0" date="2019-02-06" description="Gale">
<action type="add">