Refactor JPA SearchBuilder

This commit is contained in:
jamesagnew 2016-03-05 20:46:04 -05:00
parent eba3ca8e59
commit 99a4b2c29e
19 changed files with 516 additions and 389 deletions

View File

@ -70,7 +70,9 @@ public interface IQueryParameterType {
/**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale
* instead of a normal value
*
* @return Returns a reference to <code>this</code> for easier method chaining
*/
void setMissing(Boolean theMissing);
IQueryParameterType setMissing(Boolean theMissing);
}

View File

@ -194,7 +194,7 @@ public abstract class BaseCodingDt extends BaseIdentifiableElement implements IC
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
public IQueryParameterType setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}

View File

@ -135,7 +135,7 @@ public abstract class BaseIdentifierDt extends BaseIdentifiableElement implement
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
public IQueryParameterType setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}

View File

@ -231,7 +231,7 @@ public abstract class BaseQuantityDt extends BaseIdentifiableElement implements
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
public IQueryParameterType setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}

View File

@ -144,7 +144,7 @@ public class StringDt extends BasePrimitive<String> implements IQueryParameterTy
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
public IQueryParameterType setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}

View File

@ -61,11 +61,16 @@ abstract class BaseParam implements IQueryParameterType {
}
/**
* If set to non-null value, indicates that this parameter has been populated with a "[name]:missing=true" or "[name]:missing=false" vale instead of a normal value
* If set to non-null value, indicates that this parameter has been populated
* with a "[name]:missing=true" or "[name]:missing=false" vale instead of a
* normal value
*
* @return Returns a reference to <code>this</code> for easier method chaining
*/
@Override
public void setMissing(Boolean theMissing) {
public BaseParam setMissing(Boolean theMissing) {
myMissing = theMissing;
return this;
}
@Override

View File

@ -24,6 +24,7 @@ import java.util.List;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
@ -297,7 +298,7 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype
}
@Override
public void setMissing(Boolean theMissing) {
public IQueryParameterType setMissing(Boolean theMissing) {
throw new UnsupportedOperationException();
}

View File

@ -936,7 +936,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* @param theTag
* The tag
* @return Returns <code>true</code> if the tag should be removed
* @see <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security Labels</a> for a description of the logic that the default behaviour folows.
*/
@SuppressWarnings("unused")
protected void postPersist(ResourceTable theEntity, T theResource) {
@ -966,7 +965,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
IFhirResourceDao<R> dao = getDao(theResourceType);
Set<Long> ids = dao.searchForIdsWithAndOr(paramMap, new HashSet<Long>(), paramMap.getLastUpdated());
Set<Long> ids = dao.searchForIdsWithAndOr(paramMap, paramMap.getLastUpdated());
return ids;
}
@ -1168,14 +1167,16 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* The default implementation removes any profile declarations, but leaves tags and security labels in place.
* Subclasses may choose to override and change this behaviour.
* </p>
* <p>
* See <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security
* Labels</a> for a description of the logic that the default behaviour folows.
* </p>
*
* @param theEntity
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
* @param theTag
* The tag
* @return Retturns <code>true</code> if the tag should be removed
* @see <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security
* Labels</a> for a description of the logic that the default behaviour folows.
*/
protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
if (theTag.getTag().getTagType() == TagTypeEnum.PROFILE) {

View File

@ -932,7 +932,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) {
map.add(nextEntry.getKey(), (nextEntry.getValue()));
}
return searchForIdsWithAndOr(map, null, null);
return searchForIdsWithAndOr(map, null);
}
@Override
@ -941,10 +941,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
@Override
public Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams, Collection<Long> theInitialPids, DateRangeParam theLastUpdated) {
public Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams, DateRangeParam theLastUpdated) {
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao);
builder.setType(getResourceType(), getResourceName());
return builder.searchForIdsWithAndOr(theParams, theInitialPids, theLastUpdated);
builder.searchForIdsWithAndOr(theParams, theLastUpdated);
return builder.doGetPids();
}
@SuppressWarnings("unchecked")

View File

@ -1,7 +1,5 @@
package ca.uhn.fhir.jpa.dao;
import java.util.Collection;
/*
* #%L
* HAPI FHIR JPA Server
@ -164,7 +162,7 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
Set<Long> searchForIds(String theParameterName, IQueryParameterType theValue);
Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams, Collection<Long> theInitialPids, DateRangeParam theLastUpdated);
Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams, DateRangeParam theLastUpdated);
DaoMethodOutcome update(T theResource, RequestDetails theRequestDetails);

View File

@ -53,13 +53,13 @@ public class Search implements Serializable {
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_SEARCH")
@SequenceGenerator(name="SEQ_SEARCH", sequenceName="SEQ_SEARCH")
@Id
@Column(name = "PID")
private Long myId;
@Column(name="TOTAL_COUNT")
private int myTotalCount;
@Id
@Column(name="SEARCH_UUID", length=40, nullable=false)
private String myUuid;

View File

@ -4,10 +4,9 @@ import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Set;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.ValidationUtils;
import ca.uhn.fhir.context.FhirContext;
@ -44,7 +43,7 @@ public class TerminologySvcImpl implements ITerminologySvc {
@Override
@Transactional(value=TxType.REQUIRED)
@Transactional(propagation=Propagation.REQUIRED)
public void storeNewCodeSystemVersion(String theSystemUri, TermCodeSystemVersion theCodeSystem) {
ourLog.info("Storing code system");
@ -108,7 +107,7 @@ public class TerminologySvcImpl implements ITerminologySvc {
theConceptsStack.remove(theConcept);
}
@Transactional(value=TxType.REQUIRED)
@Transactional(propagation=Propagation.REQUIRED)
@Override
public Set<TermConcept> findCodesBelow(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) {
TermCodeSystemVersion codeSystem = myCodeSystemVersionDao.findByCodeSystemResourceAndVersion(theCodeSystemResourcePid, theCodeSystemVersionPid);

View File

@ -395,6 +395,60 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
}
@SuppressWarnings("unused")
@Test
public void testSearchResourceReferenceMissing() {
IIdType oid1;
{
Organization org = new Organization();
org.setName("ORG1");
oid1 = myOrganizationDao.create(org, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
}
IIdType pid1;
{
Patient patient = new Patient();
patient.addName().addFamily("FAMILY1");
pid1 = myPatientDao.create(patient, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
}
IIdType pid2;
{
Patient patient = new Patient();
patient.addName().addFamily("FAMILY1");
patient.getManagingOrganization().setReference(oid1);
pid2 = myPatientDao.create(patient, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
}
IIdType pid3;
{
Patient patient = new Patient();
patient.addName().addFamily("FAMILY2");
pid3 = myPatientDao.create(patient, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
}
IIdType pid4;
{
Patient patient = new Patient();
patient.addName().addFamily("FAMILY2");
patient.getManagingOrganization().setReference(oid1);
pid4 = myPatientDao.create(patient, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
}
SearchParameterMap params;
params = new SearchParameterMap();
params.add(Patient.SP_NAME, new StringParam("FAMILY1"));
params.add(Patient.SP_ORGANIZATION, new ReferenceParam().setMissing(true));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(pid1));
params = new SearchParameterMap();
params.add(Patient.SP_ORGANIZATION, new ReferenceParam().setMissing(true));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(pid1, pid3));
params = new SearchParameterMap();
params.add(Patient.SP_NAME, new StringParam("FAMILY9999"));
params.add(Patient.SP_ORGANIZATION, new ReferenceParam().setMissing(true));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty());
}
@Test
public void testSearchByIdParamOr() {
IIdType id1;
@ -1290,6 +1344,23 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam2");
myPatientDao.create(patient, new ServletRequestDetails());
{
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamCode", true));
assertEquals(0, myPatientDao.search(map).size());
}
{
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamCode", true));
map.add(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testSearchTokenParam001"));
assertEquals(0, myPatientDao.search(map).size());
}
{
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamComText", true));
map.add(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testSearchTokenParam001"));
assertEquals(1, myPatientDao.search(map).size());
}
{
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testSearchTokenParam001"));
@ -1307,11 +1378,6 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
map.add(Patient.SP_LANGUAGE, new IdentifierDt("testSearchTokenParamSystem", "testSearchTokenParamCode"));
assertEquals(1, myPatientDao.search(map).size());
}
{
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamCode", true));
assertEquals(0, myPatientDao.search(map).size());
}
{
// Complete match
SearchParameterMap map = new SearchParameterMap();
@ -1896,15 +1962,6 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
notMissing = myPatientDao.create(patient, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
}
// String Param
{
HashMap<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
StringParam param = new StringParam();
param.setMissing(false);
params.put(Patient.SP_FAMILY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
{
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
StringParam param = new StringParam();
@ -1914,8 +1971,47 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
{
HashMap<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
StringParam param = new StringParam();
param.setMissing(false);
params.put(Patient.SP_FAMILY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
}
/**
* See #310
*/
@Test
@Ignore
public void testSearchMultiMatches() {
for (int i = 0; i < 100; i++) {
Practitioner p = new Practitioner();
p.addAddress().addLine("FOO");
IIdType pid = myPractitionerDao.create(p, mySrd).getId().toUnqualifiedVersionless();
Patient pt = new Patient();
pt.addCareProvider().setReference(pid);
IIdType ptid = myPatientDao.create(pt, mySrd).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.setSubject(new ResourceReferenceDt(ptid));
myObservationDao.create(obs, mySrd);
}
SearchParameterMap map = new SearchParameterMap();
map.addInclude(new Include("Patient:careprovider"));
map.addRevInclude(new Include("Observation:patient"));
myPatientDao.search(map);
}
@Test
public void testSearchWithNoResults() {
Device dev = new Device();

View File

@ -2186,7 +2186,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
map.add(Organization.SP_NAME, new StringParam("X" + methodName + "X"));
map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION));
IBundleProvider resultsP = myOrganizationDao.search(map);
assertEquals(2, resultsP.size());
assertEquals(1, resultsP.size());
List<IBaseResource> results = resultsP.getResources(0, resultsP.size());
assertEquals(2, results.size());

View File

@ -2250,7 +2250,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
map.add(Organization.SP_NAME, new StringParam("X" + methodName + "X"));
map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION));
IBundleProvider resultsP = myOrganizationDao.search(map);
assertEquals(2, resultsP.size());
assertEquals(1, resultsP.size());
List<IBaseResource> results = resultsP.getResources(0, resultsP.size());
assertEquals(2, results.size());

View File

@ -424,7 +424,7 @@ public class IdentifierDt
*/
@Deprecated
@Override
public void setMissing(Boolean theMissing) {
public IQueryParameterType setMissing(Boolean theMissing) {
throw new UnsupportedOperationException("get/setMissing is not supported in StringDt. Use {@link StringParam} instead if you need this functionality");
}

View File

@ -846,6 +846,10 @@
<groupId>org.apache.maven.scm</groupId>
<artifactId>maven-scm-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven.doxia</groupId>
<artifactId>doxia-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven.doxia</groupId>
<artifactId>doxia-module-markdown</artifactId>
@ -1263,6 +1267,11 @@
<artifactId>maven-scm-api</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.apache.maven.doxia</groupId>
<artifactId>doxia-core</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.maven.doxia</groupId>
<artifactId>doxia-module-markdown</artifactId>

View File

@ -32,14 +32,13 @@
<body>
<head>
<![CDATA[
<!-- Syntax Highlighter -->
<!--
-->
<script type="text/javascript" src="syntaxhighlighter/shCore.js"></script>
<script type="text/javascript" src="syntaxhighlighter/shBrushJScript.js"></script>
<script type="text/javascript" src="syntaxhighlighter/shBrushJava.js"></script>
<script type="text/javascript" src="syntaxhighlighter/shBrushBash.js"></script>
<script type="text/javascript" src="syntaxhighlighter/shBrushXml.js"></script>
<script type="text/javascript" src="syntaxhighlighter/shCore.js" />
<script type="text/javascript" src="syntaxhighlighter/shBrushJScript.js" />
<script type="text/javascript" src="syntaxhighlighter/shBrushJava.js" />
<script type="text/javascript" src="syntaxhighlighter/shBrushBash.js" />
<script type="text/javascript" src="syntaxhighlighter/shBrushXml.js" />
<link href="syntaxhighlighter/shCore.css" rel="stylesheet" type="text/css" />
<link href="syntaxhighlighter/shThemeDefault.css" rel="stylesheet" type="text/css" />
@ -47,12 +46,11 @@
HAPI stylesheet comes after because it overwrites the Syntax Highlighter
font size
-->
<!--
-->
<link rel="shortcut icon" href="http://jamesagnew.github.io/hapi-fhir/images/favicon.png" />
<link rel="stylesheet" type="text/css" href="hapi.css" />
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />
]]>
</head>
<breadcrumbs>