Improve efficiency of JPA queries with _lastUpdated

This commit is contained in:
jamesagnew 2015-10-12 09:32:25 -04:00
parent 8c0b665565
commit b827823004
7 changed files with 393 additions and 326 deletions

View File

@ -58,20 +58,6 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
setRangeFromDatesInclusive(theLowerBound, theUpperBound); setRangeFromDatesInclusive(theLowerBound, theUpperBound);
} }
/**
* Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
*
* @param theLowerBound
* A qualified date param representing the lower date bound (optionally may include time), e.g.
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
* @param theUpperBound
* A qualified date param representing the upper date bound (optionally may include time), e.g.
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
*/
public DateRangeParam(DateTimeDt theLowerBound, DateTimeDt theUpperBound) {
setRangeFromDatesInclusive(theLowerBound, theUpperBound);
}
/** /**
* Sets the range from a single date param. If theDateParam has no qualifier, treats it as the lower and upper bound * Sets the range from a single date param. If theDateParam has no qualifier, treats it as the lower and upper bound
* (e.g. 2011-01-02 would match any time on that day). If theDateParam has a qualifier, treats it as either the * (e.g. 2011-01-02 would match any time on that day). If theDateParam has a qualifier, treats it as either the
@ -106,6 +92,20 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
validateAndThrowDataFormatExceptionIfInvalid(); validateAndThrowDataFormatExceptionIfInvalid();
} }
/**
* Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
*
* @param theLowerBound
* A qualified date param representing the lower date bound (optionally may include time), e.g.
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
* @param theUpperBound
* A qualified date param representing the upper date bound (optionally may include time), e.g.
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
*/
public DateRangeParam(DateTimeDt theLowerBound, DateTimeDt theUpperBound) {
setRangeFromDatesInclusive(theLowerBound, theUpperBound);
}
/** /**
* Constructor which takes two strings representing the lower and upper bounds of the range (inclusive on both ends) * Constructor which takes two strings representing the lower and upper bounds of the range (inclusive on both ends)
* *
@ -120,6 +120,39 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
setRangeFromDatesInclusive(theLowerBound, theUpperBound); setRangeFromDatesInclusive(theLowerBound, theUpperBound);
} }
private void addParam(DateParam theParsed) throws InvalidRequestException {
if (theParsed.getComparator() == null) {
if (myLowerBound != null || myUpperBound != null) {
throw new InvalidRequestException("Can not have multiple date range parameters for the same param without a qualifier");
}
myLowerBound = new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theParsed.getValueAsString());
myUpperBound = new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theParsed.getValueAsString());
} else {
switch (theParsed.getComparator()) {
case GREATERTHAN:
case GREATERTHAN_OR_EQUALS:
if (myLowerBound != null) {
throw new InvalidRequestException("Can not have multiple date range parameters for the same param that specify a lower bound");
}
myLowerBound = theParsed;
break;
case LESSTHAN:
case LESSTHAN_OR_EQUALS:
if (myUpperBound != null) {
throw new InvalidRequestException("Can not have multiple date range parameters for the same param that specify an upper bound");
}
myUpperBound = theParsed;
break;
default:
throw new InvalidRequestException("Unknown comparator: " + theParsed.getComparator());
}
}
}
public DateParam getLowerBound() { public DateParam getLowerBound() {
return myLowerBound; return myLowerBound;
} }
@ -182,6 +215,18 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
return retVal; return retVal;
} }
private boolean haveLowerBound() {
return myLowerBound != null && myLowerBound.isEmpty() == false;
}
private boolean haveUpperBound() {
return myUpperBound != null && myUpperBound.isEmpty() == false;
}
public boolean isEmpty() {
return (getLowerBoundAsInstant() == null) && (getUpperBoundAsInstant() == null);
}
public void setLowerBound(DateParam theLowerBound) { public void setLowerBound(DateParam theLowerBound) {
myLowerBound = theLowerBound; myLowerBound = theLowerBound;
validateAndThrowDataFormatExceptionIfInvalid(); validateAndThrowDataFormatExceptionIfInvalid();
@ -275,37 +320,32 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
} }
private void addParam(DateParam theParsed) throws InvalidRequestException { @Override
if (theParsed.getComparator() == null) { public String toString() {
if (myLowerBound != null || myUpperBound != null) { StringBuilder b = new StringBuilder();
throw new InvalidRequestException("Can not have multiple date range parameters for the same param without a qualifier"); b.append(getClass().getSimpleName());
b.append("[");
if (haveLowerBound()) {
if (myLowerBound.getComparator() != null) {
b.append(myLowerBound.getComparator().getCode());
} }
b.append(myLowerBound.getValueAsString());
myLowerBound = new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theParsed.getValueAsString());
myUpperBound = new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theParsed.getValueAsString());
} else {
switch (theParsed.getComparator()) {
case GREATERTHAN:
case GREATERTHAN_OR_EQUALS:
if (myLowerBound != null) {
throw new InvalidRequestException("Can not have multiple date range parameters for the same param that specify a lower bound");
}
myLowerBound = theParsed;
break;
case LESSTHAN:
case LESSTHAN_OR_EQUALS:
if (myUpperBound != null) {
throw new InvalidRequestException("Can not have multiple date range parameters for the same param that specify an upper bound");
}
myUpperBound = theParsed;
break;
default:
throw new InvalidRequestException("Unknown comparator: " + theParsed.getComparator());
}
} }
if (haveUpperBound()) {
if(haveLowerBound()) {
b.append(" ");
}
if (myUpperBound.getComparator() != null) {
b.append(myUpperBound.getComparator().getCode());
}
b.append(myUpperBound.getValueAsString());
} else {
if (!haveLowerBound()) {
b.append("empty");
}
}
b.append("]");
return b.toString();
} }
private void validateAndThrowDataFormatExceptionIfInvalid() { private void validateAndThrowDataFormatExceptionIfInvalid() {
@ -354,40 +394,4 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
} }
private boolean haveUpperBound() {
return myUpperBound != null && myUpperBound.isEmpty() == false;
}
private boolean haveLowerBound() {
return myLowerBound != null && myLowerBound.isEmpty() == false;
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(getClass().getSimpleName());
b.append("[");
if (haveLowerBound()) {
if (myLowerBound.getComparator() != null) {
b.append(myLowerBound.getComparator().getCode());
}
b.append(myLowerBound.getValueAsString());
}
if (haveUpperBound()) {
if(haveLowerBound()) {
b.append(" ");
}
if (myUpperBound.getComparator() != null) {
b.append(myUpperBound.getComparator().getCode());
}
b.append(myUpperBound.getValueAsString());
} else {
if (!haveLowerBound()) {
b.append("empty");
}
}
b.append("]");
return b.toString();
}
} }

View File

@ -772,7 +772,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
IFhirResourceDao<R> dao = getDao(theResourceType); IFhirResourceDao<R> dao = getDao(theResourceType);
Set<Long> ids = dao.searchForIdsWithAndOr(paramMap, new HashSet<Long>()); Set<Long> ids = dao.searchForIdsWithAndOr(paramMap, new HashSet<Long>(), paramMap.getLastUpdated());
return ids; return ids;
} }

View File

@ -228,7 +228,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
codePredicates.add(p); codePredicates.add(p);
} }
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate masterCodePredicate = builder.or(toArray(codePredicates));
Predicate type = builder.equal(from.get("myResourceType"), myResourceName); Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
Predicate name = builder.equal(from.get("myParamName"), theParamName); Predicate name = builder.equal(from.get("myParamName"), theParamName);
@ -243,7 +243,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
private Set<Long> addPredicateId(Set<Long> theExistingPids, Set<Long> thePids) { private Set<Long> addPredicateId(Set<Long> theExistingPids, Set<Long> thePids, DateRangeParam theLastUpdated) {
if (thePids == null || thePids.isEmpty()) { if (thePids == null || thePids.isEmpty()) {
return Collections.emptySet(); return Collections.emptySet();
} }
@ -253,10 +253,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
Root<ResourceTable> from = cq.from(ResourceTable.class); Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class)); cq.select(from.get("myId").as(Long.class));
Predicate typePredicate = builder.equal(from.get("myResourceType"), myResourceName); List<Predicate> predicates = new ArrayList<Predicate>();
Predicate idPrecidate = from.get("myId").in(thePids); predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
predicates.add(from.get("myId").in(thePids));
cq.where(builder.and(typePredicate, idPrecidate)); predicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, from));
cq.where(toArray(predicates));
TypedQuery<Long> q = myEntityManager.createQuery(cq); TypedQuery<Long> q = myEntityManager.createQuery(cq);
HashSet<Long> found = new HashSet<Long>(q.getResultList()); HashSet<Long> found = new HashSet<Long>(q.getResultList());
@ -272,7 +274,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
// IQueryParameterType> theList) { // IQueryParameterType> theList) {
// } // }
private Set<Long> addPredicateLanguage(Set<Long> thePids, List<List<? extends IQueryParameterType>> theList) { private Set<Long> addPredicateLanguage(Set<Long> thePids, List<List<? extends IQueryParameterType>> theList, DateRangeParam theLastUpdated) {
Set<Long> retVal = thePids; Set<Long> retVal = thePids;
if (theList == null || theList.isEmpty()) { if (theList == null || theList.isEmpty()) {
return retVal; return retVal;
@ -301,17 +303,18 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
return retVal; return retVal;
} }
Predicate typePredicate = builder.equal(from.get("myResourceType"), myResourceName); List<Predicate> predicates = new ArrayList<Predicate>();
Predicate langPredicate = from.get("myLanguage").as(String.class).in(values); predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
Predicate masterCodePredicate = builder.and(typePredicate, langPredicate); predicates.add(from.get("myLanguage").as(String.class).in(values));
Predicate notDeletedPredicate = builder.isNull(from.get("myDeleted"));
if (retVal.size() > 0) { if (retVal.size() > 0) {
Predicate inPids = (from.get("myId").in(retVal)); Predicate inPids = (from.get("myId").in(retVal));
cq.where(builder.and(masterCodePredicate, inPids, notDeletedPredicate)); predicates.add(inPids);
} else { }
cq.where(builder.and(masterCodePredicate, notDeletedPredicate));
} predicates.add(builder.isNull(from.get("myDeleted")));
cq.where(toArray(predicates));
TypedQuery<Long> q = myEntityManager.createQuery(cq); TypedQuery<Long> q = myEntityManager.createQuery(cq);
retVal = new HashSet<Long>(q.getResultList()); retVal = new HashSet<Long>(q.getResultList());
@ -413,7 +416,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate masterCodePredicate = builder.or(toArray(codePredicates));
Predicate type = builder.equal(from.get("myResourceType"), myResourceName); Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
Predicate name = builder.equal(from.get("myParamName"), theParamName); Predicate name = builder.equal(from.get("myParamName"), theParamName);
@ -592,7 +595,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
} }
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate masterCodePredicate = builder.or(toArray(codePredicates));
Predicate type = builder.equal(from.get("myResourceType"), myResourceName); Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
Predicate name = builder.equal(from.get("myParamName"), theParamName); Predicate name = builder.equal(from.get("myParamName"), theParamName);
@ -746,7 +749,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate masterCodePredicate = builder.or(toArray(codePredicates));
Predicate type = createResourceLinkPathPredicate(theParamName, builder, from); Predicate type = createResourceLinkPathPredicate(theParamName, builder, from);
if (pidsToRetain.size() > 0) { if (pidsToRetain.size() > 0) {
@ -785,7 +788,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
codePredicates.add(singleCode); codePredicates.add(singleCode);
} }
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate masterCodePredicate = builder.or(toArray(codePredicates));
Predicate type = builder.equal(from.get("myResourceType"), myResourceName); Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
Predicate name = builder.equal(from.get("myParamName"), theParamName); Predicate name = builder.equal(from.get("myParamName"), theParamName);
@ -800,7 +803,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
private Set<Long> addPredicateTag(Set<Long> thePids, List<List<? extends IQueryParameterType>> theList, String theParamName) { private Set<Long> addPredicateTag(Set<Long> thePids, List<List<? extends IQueryParameterType>> theList, String theParamName, DateRangeParam theLastUpdated) {
Set<Long> pids = thePids; Set<Long> pids = thePids;
if (theList == null || theList.isEmpty()) { if (theList == null || theList.isEmpty()) {
return pids; return pids;
@ -874,14 +877,17 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
if (orPredicates.isEmpty() == false) { if (orPredicates.isEmpty() == false) {
andPredicates.add(builder.or(orPredicates.toArray(new Predicate[0]))); andPredicates.add(builder.or(toArray(orPredicates)));
} }
From<ResourceTag, ResourceTable> defJoin = from.join("myResource"); From<ResourceTag, ResourceTable> defJoin = from.join("myResource");
Predicate notDeletedPredicatePrediate = builder.isNull(defJoin.get("myDeleted")); Predicate notDeletedPredicatePrediate = builder.isNull(defJoin.get("myDeleted"));
andPredicates.add(notDeletedPredicatePrediate); andPredicates.add(notDeletedPredicatePrediate);
if (theLastUpdated != null) {
Predicate masterCodePredicate = builder.and(andPredicates.toArray(new Predicate[0])); andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin));
}
Predicate masterCodePredicate = builder.and(toArray(andPredicates));
if (pids.size() > 0) { if (pids.size() > 0) {
Predicate inPids = (from.get("myResourceId").in(pids)); Predicate inPids = (from.get("myResourceId").in(pids));
@ -928,7 +934,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
codePredicates.add(singleCode); codePredicates.add(singleCode);
} }
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate masterCodePredicate = builder.or(toArray(codePredicates));
Predicate type = builder.equal(from.get("myResourceType"), myResourceName); Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
Predicate name = builder.equal(from.get("myParamName"), theParamName); Predicate name = builder.equal(from.get("myParamName"), theParamName);
@ -943,6 +949,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
private static Predicate[] toArray(List<Predicate> thePredicates) {
return thePredicates.toArray(new Predicate[thePredicates.size()]);
}
private Set<Long> addPredicateUri(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { private Set<Long> addPredicateUri(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
if (theList == null || theList.isEmpty()) { if (theList == null || theList.isEmpty()) {
return thePids; return thePids;
@ -981,7 +991,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate masterCodePredicate = builder.or(toArray(codePredicates));
Predicate type = builder.equal(from.get("myResourceType"), myResourceName); Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
Predicate name = builder.equal(from.get("myParamName"), theParamName); Predicate name = builder.equal(from.get("myParamName"), theParamName);
@ -1227,7 +1237,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} else { } else {
singleCodePredicates.add(theBuilder.isNull(theFrom.get("myValue"))); singleCodePredicates.add(theBuilder.isNull(theFrom.get("myValue")));
} }
Predicate singleCode = theBuilder.and(singleCodePredicates.toArray(new Predicate[0])); Predicate singleCode = theBuilder.and(toArray(singleCodePredicates));
return singleCode; return singleCode;
} }
@ -1662,87 +1672,87 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
} }
@Override // @Override
public IBundleProvider everything(IIdType theId) { // public IBundleProvider everything(IIdType theId) {
Search search = new Search(); // Search search = new Search();
search.setUuid(UUID.randomUUID().toString()); // search.setUuid(UUID.randomUUID().toString());
search.setCreated(new Date()); // search.setCreated(new Date());
myEntityManager.persist(search); // myEntityManager.persist(search);
//
List<SearchResult> results = new ArrayList<SearchResult>(); // List<SearchResult> results = new ArrayList<SearchResult>();
if (theId != null) { // if (theId != null) {
Long pid = translateForcedIdToPid(theId); // Long pid = translateForcedIdToPid(theId);
ResourceTable entity = myEntityManager.find(ResourceTable.class, pid); // ResourceTable entity = myEntityManager.find(ResourceTable.class, pid);
validateGivenIdIsAppropriateToRetrieveResource(theId, entity); // validateGivenIdIsAppropriateToRetrieveResource(theId, entity);
SearchResult res = new SearchResult(search); // SearchResult res = new SearchResult(search);
res.setResourcePid(pid); // res.setResourcePid(pid);
results.add(res); // results.add(res);
} else { // } else {
TypedQuery<Tuple> query = createSearchAllByTypeQuery(); // TypedQuery<Tuple> query = createSearchAllByTypeQuery();
for (Tuple next : query.getResultList()) { // for (Tuple next : query.getResultList()) {
SearchResult res = new SearchResult(search); // SearchResult res = new SearchResult(search);
res.setResourcePid(next.get(0, Long.class)); // res.setResourcePid(next.get(0, Long.class));
results.add(res); // results.add(res);
} // }
} // }
//
int totalCount = results.size(); // int totalCount = results.size();
mySearchResultDao.save(results); // mySearchResultDao.save(results);
mySearchResultDao.flush(); // mySearchResultDao.flush();
//
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); // CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
//
// Load _revincludes // // Load _revincludes
CriteriaQuery<Long> cq = builder.createQuery(Long.class); // CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceLink> from = cq.from(ResourceLink.class); // Root<ResourceLink> from = cq.from(ResourceLink.class);
cq.select(from.get("mySourceResourcePid").as(Long.class)); // cq.select(from.get("mySourceResourcePid").as(Long.class));
//
Subquery<Long> pidsSubquery = cq.subquery(Long.class); // Subquery<Long> pidsSubquery = cq.subquery(Long.class);
Root<SearchResult> pidsSubqueryFrom = pidsSubquery.from(SearchResult.class); // Root<SearchResult> pidsSubqueryFrom = pidsSubquery.from(SearchResult.class);
pidsSubquery.select(pidsSubqueryFrom.get("myResourcePid").as(Long.class)); // pidsSubquery.select(pidsSubqueryFrom.get("myResourcePid").as(Long.class));
pidsSubquery.where(pidsSubqueryFrom.get("mySearch").in(search)); // pidsSubquery.where(pidsSubqueryFrom.get("mySearch").in(search));
//
cq.where(from.get("myTargetResourceId").in(pidsSubquery)); // cq.where(from.get("myTargetResourceId").in(pidsSubquery));
TypedQuery<Long> query = myEntityManager.createQuery(cq); // TypedQuery<Long> query = myEntityManager.createQuery(cq);
//
results = new ArrayList<SearchResult>(); // results = new ArrayList<SearchResult>();
for (Long next : query.getResultList()) { // for (Long next : query.getResultList()) {
SearchResult res = new SearchResult(search); // SearchResult res = new SearchResult(search);
res.setResourcePid(next); // res.setResourcePid(next);
results.add(res); // results.add(res);
} // }
//
// Save _revincludes // // Save _revincludes
totalCount += results.size(); // totalCount += results.size();
mySearchResultDao.save(results); // mySearchResultDao.save(results);
mySearchResultDao.flush(); // mySearchResultDao.flush();
//
final int finalTotalCount = totalCount; // final int finalTotalCount = totalCount;
return new IBundleProvider() { // return new IBundleProvider() {
//
@Override // @Override
public int size() { // public int size() {
return finalTotalCount; // return finalTotalCount;
} // }
//
@Override // @Override
public Integer preferredPageSize() { // public Integer preferredPageSize() {
return null; // return null;
} // }
//
@Override // @Override
public List<IBaseResource> getResources(int theFromIndex, int theToIndex) { // public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
// TODO Auto-generated method stub // // TODO Auto-generated method stub
return null; // return null;
} // }
//
@Override // @Override
public InstantDt getPublished() { // public InstantDt getPublished() {
// TODO Auto-generated method stub // // TODO Auto-generated method stub
return null; // return null;
} // }
}; // };
} // }
/** /**
* THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet()) * THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet())
@ -2150,6 +2160,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
StopWatch w = new StopWatch(); StopWatch w = new StopWatch();
final InstantDt now = InstantDt.withCurrentTime(); final InstantDt now = InstantDt.withCurrentTime();
DateRangeParam lu = theParams.getLastUpdated();
if (lu != null && lu.isEmpty()) {
lu = null;
}
Collection<Long> loadPids; Collection<Long> loadPids;
if (theParams.getEverythingMode() != null) { if (theParams.getEverythingMode() != null) {
@ -2163,7 +2178,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
predicates.add(builder.equal(from.get("myResourceType"), myResourceName)); predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
predicates.add(builder.isNull(from.get("myDeleted"))); predicates.add(builder.isNull(from.get("myDeleted")));
cq.where(builder.and(predicates.toArray(new Predicate[predicates.size()]))); cq.where(builder.and(toArray(predicates)));
Join<Object, Object> join = from.join("myIncomingResourceLinks", JoinType.LEFT); Join<Object, Object> join = from.join("myIncomingResourceLinks", JoinType.LEFT);
cq.multiselect(from.get("myId").as(Long.class), join.get("mySourceResourcePid").as(Long.class)); cq.multiselect(from.get("myId").as(Long.class), join.get("mySourceResourcePid").as(Long.class));
@ -2181,7 +2196,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} else if (theParams.isEmpty()) { } else if (theParams.isEmpty()) {
loadPids = new HashSet<Long>(); loadPids = new HashSet<Long>();
TypedQuery<Tuple> query = createSearchAllByTypeQuery(); TypedQuery<Tuple> query = createSearchAllByTypeQuery(lu);
lu = null;
for (Tuple next : query.getResultList()) { for (Tuple next : query.getResultList()) {
loadPids.add(next.get(0, Long.class)); loadPids.add(next.get(0, Long.class));
} }
@ -2205,7 +2221,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
if (theParams.isEmpty()) { if (theParams.isEmpty()) {
loadPids = searchResultPids; loadPids = searchResultPids;
} else { } else {
loadPids = searchForIdsWithAndOr(theParams, searchResultPids); loadPids = searchForIdsWithAndOr(theParams, searchResultPids, lu);
} }
if (loadPids.isEmpty()) { if (loadPids.isEmpty()) {
return new SimpleBundleProvider(); return new SimpleBundleProvider();
@ -2222,9 +2238,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
// } // }
// Handle _lastUpdated // Handle _lastUpdated
final DateRangeParam lu = theParams.getLastUpdated(); if (lu != null) {
if (lu != null && (lu.getLowerBoundAsInstant() != null || lu.getUpperBoundAsInstant() != null)) {
List<Long> resultList = filterResourceIdsByLastUpdated(loadPids, lu); List<Long> resultList = filterResourceIdsByLastUpdated(loadPids, lu);
loadPids.clear(); loadPids.clear();
for (Long next : resultList) { for (Long next : resultList) {
@ -2271,7 +2285,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
// Load includes // Load includes
pidsSubList = new ArrayList<Long>(pidsSubList); pidsSubList = new ArrayList<Long>(pidsSubList);
revIncludedPids.addAll(loadReverseIncludes(pidsSubList, theParams.getIncludes(), false, null, lu)); revIncludedPids.addAll(loadReverseIncludes(pidsSubList, theParams.getIncludes(), false, null, theParams.getLastUpdated()));
// Execute the query and make sure we return distinct results // Execute the query and make sure we return distinct results
List<IBaseResource> resources = new ArrayList<IBaseResource>(); List<IBaseResource> resources = new ArrayList<IBaseResource>();
@ -2305,29 +2319,44 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
Root<ResourceTable> from = cq.from(ResourceTable.class); Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class)); cq.select(from.get("myId").as(Long.class));
Predicate predicateIds = (from.get("myId").in(thePids)); List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(theLastUpdated, builder, from);
Predicate predicateLower = theLastUpdated.getLowerBoundAsInstant() != null ? builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getLowerBoundAsInstant()) : null; lastUpdatedPredicates.add(0, from.get("myId").in(thePids));
Predicate predicateUpper = theLastUpdated.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getUpperBoundAsInstant()) : null;
if (predicateLower != null && predicateUpper != null) { cq.where(toArray(lastUpdatedPredicates));
cq.where(predicateIds, predicateLower, predicateUpper);
} else if (predicateLower != null) {
cq.where(predicateIds, predicateLower);
} else {
cq.where(predicateIds, predicateUpper);
}
TypedQuery<Long> query = myEntityManager.createQuery(cq); TypedQuery<Long> query = myEntityManager.createQuery(cq);
List<Long> resultList = query.getResultList(); List<Long> resultList = query.getResultList();
return resultList; return resultList;
} }
private TypedQuery<Tuple> createSearchAllByTypeQuery() { private List<Predicate> createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From<?, ResourceTable> from) {
List<Predicate> lastUpdatedPredicates = new ArrayList<Predicate>();
if (theLastUpdated != null) {
if (theLastUpdated.getLowerBoundAsInstant() != null) {
Predicate predicateLower = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getLowerBoundAsInstant());
lastUpdatedPredicates.add(predicateLower);
}
if (theLastUpdated.getUpperBoundAsInstant() != null) {
Predicate predicateUpper = builder.lessThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getUpperBoundAsInstant());
lastUpdatedPredicates.add(predicateUpper);
}
}
return lastUpdatedPredicates;
}
private TypedQuery<Tuple> createSearchAllByTypeQuery(DateRangeParam theLastUpdated) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = builder.createTupleQuery(); CriteriaQuery<Tuple> cq = builder.createTupleQuery();
Root<ResourceTable> from = cq.from(ResourceTable.class); Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.multiselect(from.get("myId").as(Long.class)); cq.multiselect(from.get("myId").as(Long.class));
Predicate typeEquals = builder.equal(from.get("myResourceType"), myResourceName); List<Predicate> predicates = new ArrayList<Predicate>();
Predicate notDeleted = builder.isNull(from.get("myDeleted")); predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
cq.where(builder.and(typeEquals, notDeleted)); predicates.add(builder.isNull(from.get("myDeleted")));
if (theLastUpdated != null) {
predicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, from));
}
cq.where(toArray(predicates));
TypedQuery<Tuple> query = myEntityManager.createQuery(cq); TypedQuery<Tuple> query = myEntityManager.createQuery(cq);
return query; return query;
@ -2348,7 +2377,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
Collection<Long> originalPids = theLoadPids; Collection<Long> originalPids = theLoadPids;
LinkedHashSet<Long> loadPids = new LinkedHashSet<Long>(); LinkedHashSet<Long> loadPids = new LinkedHashSet<Long>();
cq.multiselect(from.get("myId").as(Long.class)); cq.multiselect(from.get("myId").as(Long.class));
cq.where(predicates.toArray(new Predicate[0])); cq.where(toArray(predicates));
cq.orderBy(orders); cq.orderBy(orders);
TypedQuery<Tuple> query = myEntityManager.createQuery(cq); TypedQuery<Tuple> query = myEntityManager.createQuery(cq);
@ -2398,7 +2427,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) { for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) {
map.add(nextEntry.getKey(), (nextEntry.getValue())); map.add(nextEntry.getKey(), (nextEntry.getValue()));
} }
return searchForIdsWithAndOr(map, null); return searchForIdsWithAndOr(map, null, null);
} }
@Override @Override
@ -2407,7 +2436,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
@Override @Override
public Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams, Collection<Long> theInitialPids) { public Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams, Collection<Long> theInitialPids, DateRangeParam theLastUpdated) {
SearchParameterMap params = theParams; SearchParameterMap params = theParams;
if (params == null) { if (params == null) {
params = new SearchParameterMap(); params = new SearchParameterMap();
@ -2451,7 +2480,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
} }
pids = addPredicateId(pids, joinPids); pids = addPredicateId(pids, joinPids, theLastUpdated);
if (pids.isEmpty()) { if (pids.isEmpty()) {
return new HashSet<Long>(); return new HashSet<Long>();
} }
@ -2466,11 +2495,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} else if (nextParamName.equals(BaseResource.SP_RES_LANGUAGE)) { } else if (nextParamName.equals(BaseResource.SP_RES_LANGUAGE)) {
pids = addPredicateLanguage(pids, nextParamEntry.getValue()); pids = addPredicateLanguage(pids, nextParamEntry.getValue(), theLastUpdated);
} else if (nextParamName.equals(Constants.PARAM_TAG) || nextParamName.equals(Constants.PARAM_PROFILE) || nextParamName.equals(Constants.PARAM_SECURITY)) { } else if (nextParamName.equals(Constants.PARAM_TAG) || nextParamName.equals(Constants.PARAM_PROFILE) || nextParamName.equals(Constants.PARAM_SECURITY)) {
pids = addPredicateTag(pids, nextParamEntry.getValue(), nextParamName); pids = addPredicateTag(pids, nextParamEntry.getValue(), nextParamName, theLastUpdated);
} else { } else {

View File

@ -28,7 +28,6 @@ import java.util.Date;
import java.util.List; import java.util.List;
import javax.persistence.Query; import javax.persistence.Query;
import javax.persistence.TypedQuery;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -77,6 +76,9 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
@Autowired @Autowired
private ISubscriptionTableDao mySubscriptionTableDao; private ISubscriptionTableDao mySubscriptionTableDao;
@Autowired
private PlatformTransactionManager myTxManager;
private void createSubscriptionTable(ResourceTable theEntity, Subscription theSubscription) { private void createSubscriptionTable(ResourceTable theEntity, Subscription theSubscription) {
SubscriptionTable subscriptionEntity = new SubscriptionTable(); SubscriptionTable subscriptionEntity = new SubscriptionTable();
subscriptionEntity.setCreated(new Date()); subscriptionEntity.setCreated(new Date());
@ -87,15 +89,32 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
myEntityManager.persist(subscriptionEntity); myEntityManager.persist(subscriptionEntity);
} }
@Autowired @Override
private PlatformTransactionManager myTxManager; public Long getSubscriptionTablePidForSubscriptionResource(IIdType theId) {
ResourceTable entity = readEntityLatestVersion(theId);
@Scheduled(fixedDelay = 10 * DateUtils.MILLIS_PER_SECOND) SubscriptionTable table = mySubscriptionTableDao.findOneByResourcePid(entity.getId());
@Transactional(propagation = Propagation.NOT_SUPPORTED) if (table == null) {
public synchronized void pollForNewUndeliveredResourcesScheduler() { return null;
pollForNewUndeliveredResources(); }
return table.getId();
} }
@Override
public synchronized List<IBaseResource> getUndeliveredResourcesAndPurge(Long theSubscriptionPid) {
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
Page<SubscriptionFlaggedResource> flaggedResources = mySubscriptionFlaggedResourceDataDao.findAllBySubscriptionId(theSubscriptionPid, new PageRequest(0, 100));
for (SubscriptionFlaggedResource nextFlaggedResource : flaggedResources) {
retVal.add(toResource(nextFlaggedResource.getResource(), false));
}
mySubscriptionFlaggedResourceDataDao.delete(flaggedResources);
mySubscriptionFlaggedResourceDataDao.flush();
mySubscriptionTableDao.updateLastClientPoll(new Date());
return retVal;
}
@Override @Override
@Transactional(propagation = Propagation.NOT_SUPPORTED) @Transactional(propagation = Propagation.NOT_SUPPORTED)
public synchronized int pollForNewUndeliveredResources() { public synchronized int pollForNewUndeliveredResources() {
@ -189,6 +208,12 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
return results.size(); return results.size();
} }
@Scheduled(fixedDelay = 10 * DateUtils.MILLIS_PER_SECOND)
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public synchronized void pollForNewUndeliveredResourcesScheduler() {
pollForNewUndeliveredResources();
}
@Override @Override
protected void postPersist(ResourceTable theEntity, Subscription theSubscription) { protected void postPersist(ResourceTable theEntity, Subscription theSubscription) {
super.postPersist(theEntity, theSubscription); super.postPersist(theEntity, theSubscription);
@ -196,105 +221,6 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
createSubscriptionTable(theEntity, theSubscription); createSubscriptionTable(theEntity, theSubscription);
} }
@Override
protected ResourceTable updateEntity(IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
Date theUpdateTime) {
ResourceTable retVal = super.updateEntity(theResource, theEntity, theUpdateHistory, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime);
Subscription resource = (Subscription) theResource;
Long resourceId = theEntity.getId();
if (theDeletedTimestampOrNull != null) {
Long subscriptionId = getSubscriptionTablePidForSubscriptionResource(theEntity.getIdDt());
if (subscriptionId != null) {
mySubscriptionFlaggedResourceDataDao.deleteAllForSubscription(subscriptionId);
mySubscriptionTableDao.deleteAllForSubscription(subscriptionId);
}
} else {
Query q = myEntityManager.createNamedQuery("Q_HFJ_SUBSCRIPTION_SET_STATUS");
q.setParameter("res_id", resourceId);
q.setParameter("status", resource.getStatusElement().getValueAsEnum());
if (q.executeUpdate() > 0) {
ourLog.info("Updated subscription status for subscription {} to {}", resourceId, resource.getStatusElement().getValueAsEnum());
} else {
createSubscriptionTable(retVal, resource);
}
}
return retVal;
}
@Override
protected void validateResourceForStorage(Subscription theResource, ResourceTable theEntityToSave) {
super.validateResourceForStorage(theResource, theEntityToSave);
RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource);
IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass());
if (dao == null) {
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resDef);
}
if (theResource.getChannel().getType() == null) {
throw new UnprocessableEntityException("Subscription.channel.type must be populated on this server");
}
SubscriptionStatusEnum status = theResource.getStatusElement().getValueAsEnum();
if (status == null) {
throw new UnprocessableEntityException("Subscription.status must be populated on this server");
}
}
private RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) {
String query = theResource.getCriteria();
if (isBlank(query)) {
throw new UnprocessableEntityException("Subscription.criteria must be populated");
}
int sep = query.indexOf('?');
if (sep <= 1) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
}
String resType = query.substring(0, sep);
if (resType.contains("/")) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
}
RuntimeResourceDefinition resDef;
try {
resDef = getContext().getResourceDefinition(resType);
} catch (DataFormatException e) {
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resType);
}
return resDef;
}
@Override
public synchronized List<IBaseResource> getUndeliveredResourcesAndPurge(Long theSubscriptionPid) {
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
Page<SubscriptionFlaggedResource> flaggedResources = mySubscriptionFlaggedResourceDataDao.findAllBySubscriptionId(theSubscriptionPid, new PageRequest(0, 100));
for (SubscriptionFlaggedResource nextFlaggedResource : flaggedResources) {
retVal.add(toResource(nextFlaggedResource.getResource(), false));
}
mySubscriptionFlaggedResourceDataDao.delete(flaggedResources);
mySubscriptionFlaggedResourceDataDao.flush();
mySubscriptionTableDao.updateLastClientPoll(new Date());
return retVal;
}
@Override
public Long getSubscriptionTablePidForSubscriptionResource(IIdType theId) {
ResourceTable entity = readEntityLatestVersion(theId);
SubscriptionTable table = mySubscriptionTableDao.findOneByResourcePid(entity.getId());
if (table == null) {
return null;
}
return table.getId();
}
@Scheduled(fixedDelay = DateUtils.MILLIS_PER_MINUTE) @Scheduled(fixedDelay = DateUtils.MILLIS_PER_MINUTE)
@Transactional(propagation = Propagation.NOT_SUPPORTED) @Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override @Override
@ -323,4 +249,77 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
} }
} }
@Override
protected ResourceTable updateEntity(IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
Date theUpdateTime) {
ResourceTable retVal = super.updateEntity(theResource, theEntity, theUpdateHistory, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime);
Subscription resource = (Subscription) theResource;
Long resourceId = theEntity.getId();
if (theDeletedTimestampOrNull != null) {
Long subscriptionId = getSubscriptionTablePidForSubscriptionResource(theEntity.getIdDt());
if (subscriptionId != null) {
mySubscriptionFlaggedResourceDataDao.deleteAllForSubscription(subscriptionId);
mySubscriptionTableDao.deleteAllForSubscription(subscriptionId);
}
} else {
Query q = myEntityManager.createNamedQuery("Q_HFJ_SUBSCRIPTION_SET_STATUS");
q.setParameter("res_id", resourceId);
q.setParameter("status", resource.getStatusElement().getValueAsEnum());
if (q.executeUpdate() > 0) {
ourLog.info("Updated subscription status for subscription {} to {}", resourceId, resource.getStatusElement().getValueAsEnum());
} else {
createSubscriptionTable(retVal, resource);
}
}
return retVal;
}
private RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) {
String query = theResource.getCriteria();
if (isBlank(query)) {
throw new UnprocessableEntityException("Subscription.criteria must be populated");
}
int sep = query.indexOf('?');
if (sep <= 1) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
}
String resType = query.substring(0, sep);
if (resType.contains("/")) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
}
RuntimeResourceDefinition resDef;
try {
resDef = getContext().getResourceDefinition(resType);
} catch (DataFormatException e) {
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resType);
}
return resDef;
}
@Override
protected void validateResourceForStorage(Subscription theResource, ResourceTable theEntityToSave) {
super.validateResourceForStorage(theResource, theEntityToSave);
RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource);
IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass());
if (dao == null) {
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resDef);
}
if (theResource.getChannel().getType() == null) {
throw new UnprocessableEntityException("Subscription.channel.type must be populated on this server");
}
SubscriptionStatusEnum status = theResource.getStatusElement().getValueAsEnum();
if (status == null) {
throw new UnprocessableEntityException("Subscription.status must be populated on this server");
}
}
} }

View File

@ -36,6 +36,7 @@ import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@ -123,7 +124,7 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
Set<Long> searchForIds(String theParameterName, IQueryParameterType theValue); Set<Long> searchForIds(String theParameterName, IQueryParameterType theValue);
Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams, Collection<Long> theInitialPids); Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams, Collection<Long> theInitialPids, DateRangeParam theLastUpdated);
DaoMethodOutcome update(T theResource); DaoMethodOutcome update(T theResource);
@ -146,9 +147,9 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
*/ */
DaoMethodOutcome deleteByUrl(String theUrl, boolean theTransaction); DaoMethodOutcome deleteByUrl(String theUrl, boolean theTransaction);
/** // /**
* Invoke the everything operation // * Invoke the everything operation
*/ // */
IBundleProvider everything(IIdType theId); // IBundleProvider everything(IIdType theId);
} }

View File

@ -399,6 +399,7 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
patient.addIdentifier().setSystem("urn:system").setValue("001"); patient.addIdentifier().setSystem("urn:system").setValue("001");
id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless(); id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
} }
long betweenTime = System.currentTimeMillis();
IIdType id2; IIdType id2;
{ {
Patient patient = new Patient(); Patient patient = new Patient();
@ -418,6 +419,13 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam("999999999999"))); params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam("999999999999")));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1)); assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
// With lastupdated
params = new SearchParameterMap();
params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id2.getIdPart())));
params.setLastUpdated(new DateRangeParam(new Date(betweenTime), null));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id2));
} }
@Test @Test
@ -610,6 +618,9 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
patient.addName().addFamily("testSearchLanguageParam").addGiven("Joe"); patient.addName().addFamily("testSearchLanguageParam").addGiven("Joe");
id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless(); id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
} }
Date betweenTime = new Date();
IIdType id2; IIdType id2;
{ {
Patient patient = new Patient(); Patient patient = new Patient();
@ -623,6 +634,12 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
params.add(BaseResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US"))); params.add(BaseResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US")));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1, id2)); assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1, id2));
} }
{
SearchParameterMap params = new SearchParameterMap();
params.add(BaseResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US")));
params.setLastUpdated(new DateRangeParam(betweenTime, null));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id2));
}
{ {
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.add(BaseResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ"))); params.add(BaseResource.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
@ -1943,6 +1960,9 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
ResourceMetadataKeyEnum.TAG_LIST.put(org, tagList); ResourceMetadataKeyEnum.TAG_LIST.put(org, tagList);
tag1id = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); tag1id = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
} }
Date betweenDate = new Date();
IIdType tag2id; IIdType tag2id;
{ {
Organization org = new Organization(); Organization org = new Organization();
@ -1978,6 +1998,17 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
List<IIdType> patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id, tag2id)); assertThat(patients, containsInAnyOrder(tag1id, tag2id));
} }
{
// Or tags with lastupdated
SearchParameterMap params = new SearchParameterMap();
TokenOrListParam orListParam = new TokenOrListParam();
orListParam.add(new TokenParam("urn:taglist", methodName + "1a"));
orListParam.add(new TokenParam("urn:taglist", methodName + "2a"));
params.add("_tag", orListParam);
params.setLastUpdated(new DateRangeParam(betweenDate, null));
List<IIdType> patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag2id));
}
// TODO: get multiple/AND working // TODO: get multiple/AND working
{ {
// And tags // And tags

View File

@ -162,6 +162,9 @@
meaning that the same operation can also be invoked meaning that the same operation can also be invoked
at the type level. at the type level.
</action> </action>
<action type="add">
Make JPA search queries with _lastUpdated parameter a bit more efficient
</action>
</release> </release>
<release version="1.2" date="2015-09-18"> <release version="1.2" date="2015-09-18">
<action type="add"> <action type="add">