More work on bugfixing #164 - Support for :missing

This commit is contained in:
James Agnew 2015-05-01 08:20:01 -04:00
parent 3fc6958ff2
commit 20a16d62fe
10 changed files with 103 additions and 61 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -70,6 +70,7 @@ abstract class BaseParam implements IQueryParameterType {
public final void setValueAsQueryToken(String theQualifier, String theValue) {
if (Constants.PARAMQUALIFIER_MISSING.equals(theQualifier)) {
myMissing = "true".equals(theValue);
doSetValueAsQueryToken(null, null);
} else {
myMissing = null;
doSetValueAsQueryToken(theQualifier, theValue);

View File

@ -144,15 +144,11 @@ public class QuantityParam extends BaseParam implements IQueryParameterType {
@Override
String doGetQueryParameterQualifier() {
return super.getQueryParameterQualifier();
return null;
}
@Override
String doGetValueAsQueryToken() {
if (super.getMissing() != null) {
return super.getValueAsQueryToken();
}
StringBuilder b = new StringBuilder();
if (myApproximate) {
b.append('~');
@ -179,11 +175,6 @@ public class QuantityParam extends BaseParam implements IQueryParameterType {
void doSetValueAsQueryToken(String theQualifier, String theValue) {
clear();
super.setValueAsQueryToken(theQualifier, theValue);
if (getMissing() != null) {
return;
}
if (theValue == null) {
return;
}

View File

@ -47,12 +47,12 @@ import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
@ -183,7 +183,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsDate");
return addPredicateParamMissing(thePids, "myParamsDate", theParamName, ResourceIndexedSearchParamDate.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
@ -193,7 +193,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
if (addPredicateMissingFalseIfPresent(theParamName, from, codePredicates, nextOr)) {
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
@ -296,7 +296,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsNumber");
return addPredicateParamMissing(thePids, "myParamsNumber", theParamName, ResourceIndexedSearchParamNumber.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
@ -308,7 +308,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresent(theParamName, from, codePredicates, nextOr)) {
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
@ -365,20 +365,58 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList());
}
private Set<Long> addPredicateParamMissing(Set<Long> thePids, String joinName) {
private Set<Long> addPredicateParamMissing(Set<Long> thePids, String joinName, String theParamName, Class<? extends BaseResourceIndexedSearchParam> theParamTable) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class));
Join<Object, Object> join = from.join(joinName, JoinType.LEFT);
Subquery<Long> subQ = cq.subquery(Long.class);
Root<? extends BaseResourceIndexedSearchParam> subQfrom = subQ.from(theParamTable);
subQ.select(subQfrom.get("myResourcePid").as(Long.class));
subQ.where(builder.equal(subQfrom.get("myParamName"), theParamName));
Predicate joinPredicate = builder.not(builder.in(from.get("myId")).value(subQ));
if (thePids.size() > 0) {
Predicate inPids = (from.get("myId").in(thePids));
cq.where(builder.and(inPids, join.isNull()));
cq.where(builder.and(inPids, joinPredicate));
} else {
cq.where(join.isNull());
cq.where(joinPredicate);
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
return new HashSet<Long>(q.getResultList());
List<Long> resultList = q.getResultList();
HashSet<Long> retVal = new HashSet<Long>(resultList);
return retVal;
}
private Set<Long> addPredicateParamMissingResourceLink(Set<Long> thePids, String joinName, String theParamName) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class));
Subquery<Long> subQ = cq.subquery(Long.class);
Root<ResourceLink> subQfrom = subQ.from(ResourceLink.class);
subQ.select(subQfrom.get("mySourceResourcePid").as(Long.class));
// subQ.where(builder.equal(subQfrom.get("myParamName"), theParamName));
subQ.where(createResourceLinkPathPredicate(theParamName, builder, subQfrom));
Predicate joinPredicate = builder.not(builder.in(from.get("myId")).value(subQ));
if (thePids.size() > 0) {
Predicate inPids = (from.get("myId").in(thePids));
cq.where(builder.and(inPids, joinPredicate));
} else {
cq.where(joinPredicate);
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
List<Long> resultList = q.getResultList();
HashSet<Long> retVal = new HashSet<Long>(resultList);
return retVal;
}
private Set<Long> addPredicateQuantity(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
@ -387,7 +425,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsQuantity");
return addPredicateParamMissing(thePids, "myParamsQuantity", theParamName, ResourceIndexedSearchParamQuantity.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
@ -399,7 +437,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresent(theParamName, from, codePredicates, nextOr)) {
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
@ -505,7 +543,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myResourceLinks");
return addPredicateParamMissingResourceLink(thePids, "myResourceLinks", theParamName);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
@ -518,7 +556,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresentForResourceLink(theParamName, from, codePredicates, nextOr)) {
if (addPredicateMissingFalseIfPresentForResourceLink(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
@ -586,10 +624,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
RuntimeSearchParam param = getContext().getResourceDefinition(getResourceType()).getSearchParam(theParamName);
String path = param.getPath();
Predicate type = builder.equal(from.get("mySourcePath"), path);
Predicate type = createResourceLinkPathPredicate(theParamName, builder, from);
if (pidsToRetain.size() > 0) {
Predicate inPids = (from.get("mySourceResourcePid").in(pidsToRetain));
cq.where(builder.and(type, masterCodePredicate, inPids));
@ -601,13 +636,21 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList());
}
private Predicate createResourceLinkPathPredicate(String theParamName, CriteriaBuilder builder, Root<? extends ResourceLink> from) {
RuntimeSearchParam param = getContext().getResourceDefinition(getResourceType()).getSearchParam(theParamName);
String path = param.getPath();
Predicate type = builder.equal(from.get("mySourcePath"), path);
return type;
}
private Set<Long> addPredicateString(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
if (theList == null || theList.isEmpty()) {
return thePids;
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsString");
return addPredicateParamMissing(thePids, "myParamsString", theParamName, ResourceIndexedSearchParamString.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
@ -618,7 +661,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
IQueryParameterType theParameter = nextOr;
if (addPredicateMissingFalseIfPresent(theParamName, from, codePredicates, nextOr)) {
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
@ -641,27 +684,29 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
return new HashSet<Long>(q.getResultList());
}
private boolean addPredicateMissingFalseIfPresent(String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "multipleParamsWithSameNameOneIsMissingTrue", theParamName));
}
Predicate singleCode = from.get("myId").isNotNull();
codePredicates.add(singleCode);
Predicate name = theBuilder.equal(from.get("myParamName"), theParamName);
codePredicates.add(theBuilder.and(name, singleCode));
missingFalse = true;
}
return missingFalse;
}
private boolean addPredicateMissingFalseIfPresentForResourceLink(String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
if (nextOr.getMissing().booleanValue() == true) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "multipleParamsWithSameNameOneIsMissingTrue", theParamName));
}
Predicate singleCode = from.get("mySourceResource").isNotNull();
codePredicates.add(singleCode);
Predicate name = createResourceLinkPathPredicate(theParamName, theBuilder, from);
codePredicates.add(theBuilder.and(name, singleCode));
missingFalse = true;
}
return missingFalse;
@ -673,7 +718,7 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
}
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
return addPredicateParamMissing(thePids, "myParamsToken");
return addPredicateParamMissing(thePids, "myParamsToken", theParamName, ResourceIndexedSearchParamToken.class);
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
@ -683,6 +728,10 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
if (nextOr instanceof TokenParam) {
TokenParam id = (TokenParam) nextOr;
if (id.isText()) {
@ -690,10 +739,6 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
}
}
if (addPredicateMissingFalseIfPresent(theParamName, from, codePredicates, nextOr)) {
continue;
}
Predicate singleCode = createPredicateToken(nextOr, theParamName, builder, from);
codePredicates.add(singleCode);
}

View File

@ -1582,7 +1582,7 @@ public class FhirResourceDaoDstu2Test {
QuantityParam param = new QuantityParam();
param.setMissing(false);
params.put(Observation.SP_VALUE_QUANTITY, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
List<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
@ -1591,7 +1591,7 @@ public class FhirResourceDaoDstu2Test {
QuantityParam param = new QuantityParam();
param.setMissing(true);
params.put(Observation.SP_VALUE_QUANTITY, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
List<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
@ -1618,7 +1618,7 @@ public class FhirResourceDaoDstu2Test {
TokenParam param = new TokenParam();
param.setMissing(false);
params.put(Observation.SP_CODE, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
List<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, not(containsInRelativeOrder(missing)));
assertThat(patients, containsInRelativeOrder(notMissing));
}
@ -1627,7 +1627,7 @@ public class FhirResourceDaoDstu2Test {
TokenParam param = new TokenParam();
param.setMissing(true);
params.put(Observation.SP_CODE, param);
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
List<IdDt> patients = toUnqualifiedVersionlessIds(ourObservationDao.search(params));
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
@ -2422,36 +2422,36 @@ public class FhirResourceDaoDstu2Test {
@Test
public void testUpdateMaintainsSearchParams() throws InterruptedException {
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsAAA");
p1.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsAAA");
p1.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsDstu2AAA");
p1.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsDstu2AAA");
IdDt p1id = ourPatientDao.create(p1).getId();
Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsBBB");
p2.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsBBB");
p2.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsDstu2BBB");
p2.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsDstu2BBB");
ourPatientDao.create(p2).getId();
Set<Long> ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
Set<Long> ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsDstu2AAA"));
assertEquals(1, ids.size());
assertThat(ids, contains(p1id.getIdPartAsLong()));
// Update the name
p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsBBB");
p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsDstu2BBB");
MethodOutcome update2 = ourPatientDao.update(p1);
IdDt p1id2 = update2.getId();
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsDstu2AAA"));
assertEquals(0, ids.size());
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsBBB"));
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsDstu2BBB"));
assertEquals(2, ids.size());
// Make sure vreads work
p1 = ourPatientDao.read(p1id);
assertEquals("testUpdateMaintainsSearchParamsAAA", p1.getNameFirstRep().getGivenAsSingleString());
assertEquals("testUpdateMaintainsSearchParamsDstu2AAA", p1.getNameFirstRep().getGivenAsSingleString());
p1 = ourPatientDao.read(p1id2);
assertEquals("testUpdateMaintainsSearchParamsBBB", p1.getNameFirstRep().getGivenAsSingleString());
assertEquals("testUpdateMaintainsSearchParamsDstu2BBB", p1.getNameFirstRep().getGivenAsSingleString());
}

View File

@ -286,7 +286,7 @@ public class SearchTest {
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("<details value=\"Incorrect Content-Type header value of &quot;application/x-www-form-urlencoded; charset=UTF-8&quot; was provided in the request. This is required for &quot;CREATE&quot; operation\"/>"));
assertThat(responseContent, containsString("<details value=\"Incorrect Content-Type header value of &quot;application/x-www-form-urlencoded; charset=UTF-8&quot; was provided in the request. A FHIR Content-Type is required for &quot;CREATE&quot; operation\"/>"));
}
/**
@ -473,7 +473,9 @@ public class SearchTest {
patient.addIdentifier("system", "identifier123");
if (theParam != null) {
patient.addName().addFamily("id" + theParam.getValue());
patient.addName().addFamily("name" + theName.getValue());
if (theName != null) {
patient.addName().addFamily("name" + theName.getValue());
}
}
retVal.add(patient);
return retVal;

View File

@ -10,10 +10,13 @@
<logger name="org.eclipse" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.apache" additivity="false" level="debug">
<logger name="org.apache" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.thymeleaf" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<!--
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">
<appender-ref ref="STDOUT" />