mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-02-17 02:15:22 +00:00
Merge pull request #656 from aehrc/feature/scalable_searchbuilder
Improve SearchBuilder query generation for code:modifier searches
This commit is contained in:
commit
4caa5dfa8d
@ -24,7 +24,6 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.substring;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
@ -44,6 +43,7 @@ import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.AbstractQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaBuilder.In;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.From;
|
||||
@ -62,13 +62,27 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
@ -1219,9 +1233,24 @@ public class SearchBuilder implements ISearchBuilder {
|
||||
|
||||
} else {
|
||||
List<Predicate> orPredicates = new ArrayList<Predicate>();
|
||||
Map<String, List<VersionIndependentConcept>> map = new HashMap<String, List<VersionIndependentConcept>>();
|
||||
for (VersionIndependentConcept nextCode : codes) {
|
||||
Predicate systemPredicate = theBuilder.equal(theFrom.get("mySystem"), nextCode.getSystem());
|
||||
Predicate codePredicate = theBuilder.equal(theFrom.get("myValue"), nextCode.getCode());
|
||||
List<VersionIndependentConcept> systemCodes = map.get(nextCode.getSystem());
|
||||
if (null == systemCodes) {
|
||||
systemCodes = new ArrayList<VersionIndependentConcept>();
|
||||
map.put(nextCode.getSystem(), systemCodes);
|
||||
}
|
||||
systemCodes.add(nextCode);
|
||||
}
|
||||
// Use "in" in case of large numbers of codes due to param modifiers
|
||||
final Path<String> systemExpression = theFrom.get("mySystem");
|
||||
final Path<String> valueExpression = theFrom.get("myValue");
|
||||
for (Map.Entry<String, List<VersionIndependentConcept>> entry: map.entrySet()) {
|
||||
Predicate systemPredicate = theBuilder.equal(systemExpression, entry.getKey());
|
||||
In<String> codePredicate = theBuilder.in(valueExpression);
|
||||
for (VersionIndependentConcept nextCode: entry.getValue()) {
|
||||
codePredicate.value(nextCode.getCode());
|
||||
}
|
||||
orPredicates.add(theBuilder.and(systemPredicate, codePredicate));
|
||||
}
|
||||
|
||||
@ -1814,6 +1843,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(Class<? extends IBaseResource> theResourceType, String theResourceName) {
|
||||
myResourceType = theResourceType;
|
||||
myResourceName = theResourceName;
|
||||
|
@ -118,6 +118,38 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
||||
return codeSystem;
|
||||
}
|
||||
|
||||
private CodeSystem createExternalCsLarge() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
|
||||
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
|
||||
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
|
||||
|
||||
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
|
||||
|
||||
TermCodeSystemVersion cs = new TermCodeSystemVersion();
|
||||
cs.setResource(table);
|
||||
cs.setResourceVersionId(table.getVersion());
|
||||
|
||||
TermConcept parentA = new TermConcept(cs, "codeA").setDisplay("CodeA");
|
||||
cs.getConcepts().add(parentA);
|
||||
|
||||
for (int i = 0; i < 450; i++) {
|
||||
TermConcept childI = new TermConcept(cs, "subCodeA"+i).setDisplay("Sub-code A"+i);
|
||||
parentA.addChild(childI, RelationshipTypeEnum.ISA);
|
||||
}
|
||||
|
||||
TermConcept parentB = new TermConcept(cs, "codeB").setDisplay("CodeB");
|
||||
cs.getConcepts().add(parentB);
|
||||
|
||||
for (int i = 0; i < 450; i++) {
|
||||
TermConcept childI = new TermConcept(cs, "subCodeB"+i).setDisplay("Sub-code B"+i);
|
||||
parentB.addChild(childI, RelationshipTypeEnum.ISA);
|
||||
}
|
||||
|
||||
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
|
||||
return codeSystem;
|
||||
}
|
||||
|
||||
private void createExternalCsAndLocalVs() {
|
||||
CodeSystem codeSystem = createExternalCs();
|
||||
|
||||
@ -934,6 +966,36 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchCodeBelowExternalCodesystemLarge() {
|
||||
createExternalCsLarge();
|
||||
|
||||
Observation obs0 = new Observation();
|
||||
obs0.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("codeA");
|
||||
IIdType id0 = myObservationDao.create(obs0, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Observation obs1 = new Observation();
|
||||
obs1.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("subCodeA1");
|
||||
IIdType id1 = myObservationDao.create(obs1, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Observation obs2 = new Observation();
|
||||
obs2.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("subCodeA2");
|
||||
IIdType id2 = myObservationDao.create(obs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Observation obs3 = new Observation();
|
||||
obs3.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("subCodeB3");
|
||||
IIdType id3 = myObservationDao.create(obs3, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "codeA").setModifier(TokenParamModifier.BELOW));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(id0.getValue(), id1.getValue(), id2.getValue()));
|
||||
|
||||
params = new SearchParameterMap();
|
||||
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "subCodeB1").setModifier(TokenParamModifier.BELOW));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchCodeInBuiltInValueSet() {
|
||||
AllergyIntolerance ai1 = new AllergyIntolerance();
|
||||
|
Loading…
x
Reference in New Issue
Block a user