Merge branch 'master' of github.com:jamesagnew/hapi-fhir
This commit is contained in:
commit
1a5ba99e2d
|
@ -25,9 +25,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -36,6 +36,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -208,6 +209,52 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lower bound using a string that is compliant with
|
||||
* FHIR dateTime format (ISO-8601).
|
||||
* <p>
|
||||
* This lower bound is assumed to have a <code>ge</code>
|
||||
* (greater than or equals) modifier.
|
||||
* </p>
|
||||
*/
|
||||
public DateRangeParam setLowerBound(String theLowerBound) {
|
||||
setLowerBound(new DateParam(GREATERTHAN_OR_EQUALS, theLowerBound));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lower bound to be greaterthan or equal to the given date
|
||||
*/
|
||||
public DateRangeParam setLowerBoundInclusive(Date theLowerBound) {
|
||||
validateAndSet(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, theLowerBound), myUpperBound);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the upper bound to be greaterthan or equal to the given date
|
||||
*/
|
||||
public DateRangeParam setUpperBoundInclusive(Date theUpperBound) {
|
||||
validateAndSet(myLowerBound, new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, theUpperBound));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the lower bound to be greaterthan to the given date
|
||||
*/
|
||||
public DateRangeParam setLowerBoundExclusive(Date theLowerBound) {
|
||||
validateAndSet(new DateParam(ParamPrefixEnum.GREATERTHAN, theLowerBound), myUpperBound);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the upper bound to be greaterthan to the given date
|
||||
*/
|
||||
public DateRangeParam setUpperBoundExclusive(Date theUpperBound) {
|
||||
validateAndSet(myLowerBound, new DateParam(ParamPrefixEnum.LESSTHAN, theUpperBound));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getLowerBoundAsInstant() {
|
||||
if (myLowerBound == null) {
|
||||
return null;
|
||||
|
@ -237,6 +284,19 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
return myUpperBound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the upper bound using a string that is compliant with
|
||||
* FHIR dateTime format (ISO-8601).
|
||||
* <p>
|
||||
* This upper bound is assumed to have a <code>le</code>
|
||||
* (less than or equals) modifier.
|
||||
* </p>
|
||||
*/
|
||||
public DateRangeParam setUpperBound(String theUpperBound) {
|
||||
setUpperBound(new DateParam(LESSTHAN_OR_EQUALS, theUpperBound));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DateRangeParam setUpperBound(DateParam theUpperBound) {
|
||||
validateAndSet(myLowerBound, theUpperBound);
|
||||
return this;
|
||||
|
@ -298,19 +358,6 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
return (getLowerBoundAsInstant() == null) && (getUpperBoundAsInstant() == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lower bound using a string that is compliant with
|
||||
* FHIR dateTime format (ISO-8601).
|
||||
* <p>
|
||||
* This lower bound is assumed to have a <code>ge</code>
|
||||
* (greater than or equals) modifier.
|
||||
* </p>
|
||||
*/
|
||||
public DateRangeParam setLowerBound(String theLowerBound) {
|
||||
setLowerBound(new DateParam(GREATERTHAN_OR_EQUALS, theLowerBound));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the range from a pair of dates, inclusive on both ends
|
||||
*
|
||||
|
@ -394,19 +441,6 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
validateAndSet(lowerBound, upperBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the upper bound using a string that is compliant with
|
||||
* FHIR dateTime format (ISO-8601).
|
||||
* <p>
|
||||
* This upper bound is assumed to have a <code>le</code>
|
||||
* (less than or equals) modifier.
|
||||
* </p>
|
||||
*/
|
||||
public DateRangeParam setUpperBound(String theUpperBound) {
|
||||
setUpperBound(new DateParam(LESSTHAN_OR_EQUALS, theUpperBound));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, List<QualifiedParamList> theParameters)
|
||||
throws InvalidRequestException {
|
||||
|
@ -512,4 +546,5 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
myLowerBound = lowerBound;
|
||||
myUpperBound = upperBound;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ public interface ISearchBuilder {
|
|||
void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation, EntityManager theEntityManager,
|
||||
FhirContext theContext, IDao theDao);
|
||||
|
||||
Set<Long> loadReverseIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode,
|
||||
DateRangeParam theLastUpdated);
|
||||
Set<Long> loadIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode,
|
||||
DateRangeParam theLastUpdated);
|
||||
|
||||
/**
|
||||
* How many results may be fetched at once
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -69,6 +69,7 @@ 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 javax.annotation.Nonnull;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.*;
|
||||
|
@ -114,7 +115,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
protected IResourceTagDao myResourceTagDao;
|
||||
protected IResourceSearchViewDao myResourceSearchViewDao;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -1603,17 +1604,17 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
// -- get the resource from the searchView
|
||||
Collection<ResourceSearchView> resourceSearchViewList = myResourceSearchViewDao.findByResourceIds(pids);
|
||||
|
||||
|
||||
//-- preload all tags with tag definition if any
|
||||
Map<Long, Collection<ResourceTag>> tagMap = getResourceTagMap(resourceSearchViewList);
|
||||
|
||||
|
||||
Long resourceId = null;
|
||||
for (ResourceSearchView next : resourceSearchViewList) {
|
||||
|
||||
|
||||
Class<? extends IBaseResource> resourceType = context.getResourceDefinition(next.getResourceType()).getImplementingClass();
|
||||
|
||||
|
||||
resourceId = next.getId();
|
||||
|
||||
|
||||
IBaseResource resource = theDao.toResource(resourceType, next, tagMap.get(resourceId), theForHistoryOperation);
|
||||
if (resource == null) {
|
||||
ourLog.warn("Unable to find resource {}/{}/_history/{} in database", next.getResourceType(), next.getIdDt().getIdPart(), next.getVersion());
|
||||
|
@ -1643,30 +1644,30 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private Map<Long, Collection<ResourceTag>> getResourceTagMap(Collection<ResourceSearchView> theResourceSearchViewList) {
|
||||
|
||||
private Map<Long, Collection<ResourceTag>> getResourceTagMap(Collection<ResourceSearchView> theResourceSearchViewList) {
|
||||
|
||||
List<Long> idList = new ArrayList<Long>(theResourceSearchViewList.size());
|
||||
|
||||
|
||||
//-- find all resource has tags
|
||||
for (ResourceSearchView resource: theResourceSearchViewList) {
|
||||
for (ResourceSearchView resource: theResourceSearchViewList) {
|
||||
if (resource.isHasTags())
|
||||
idList.add(resource.getId());
|
||||
}
|
||||
|
||||
|
||||
Map<Long, Collection<ResourceTag>> tagMap = new HashMap<>();
|
||||
|
||||
|
||||
//-- no tags
|
||||
if (idList.size() == 0)
|
||||
return tagMap;
|
||||
|
||||
|
||||
//-- get all tags for the idList
|
||||
Collection<ResourceTag> tagList = myResourceTagDao.findByResourceIds(idList);
|
||||
|
||||
|
||||
//-- build the map, key = resourceId, value = list of ResourceTag
|
||||
Long resourceId;
|
||||
Collection<ResourceTag> tagCol;
|
||||
for (ResourceTag tag : tagList) {
|
||||
|
||||
|
||||
resourceId = tag.getResourceId();
|
||||
tagCol = tagMap.get(resourceId);
|
||||
if (tagCol == null) {
|
||||
|
@ -1678,7 +1679,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
return tagMap;
|
||||
return tagMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1720,8 +1721,8 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
* THIS SHOULD RETURN HASHSET and not just Set because we add to it later (so it can't be Collections.emptySet())
|
||||
*/
|
||||
@Override
|
||||
public HashSet<Long> loadReverseIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes,
|
||||
boolean theReverseMode, DateRangeParam theLastUpdated) {
|
||||
public HashSet<Long> loadIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes,
|
||||
boolean theReverseMode, DateRangeParam theLastUpdated) {
|
||||
if (theMatches.size() == 0) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
@ -1828,8 +1829,10 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
if (theLastUpdated != null && (theLastUpdated.getLowerBoundAsInstant() != null || theLastUpdated.getUpperBoundAsInstant() != null)) {
|
||||
pidsToInclude = new HashSet<>(filterResourceIdsByLastUpdated(theEntityManager, theLastUpdated, pidsToInclude));
|
||||
if (theReverseMode) {
|
||||
if (theLastUpdated != null && (theLastUpdated.getLowerBoundAsInstant() != null || theLastUpdated.getUpperBoundAsInstant() != null)) {
|
||||
pidsToInclude = new HashSet<>(filterResourceIdsByLastUpdated(theEntityManager, theLastUpdated, pidsToInclude));
|
||||
}
|
||||
}
|
||||
for (Long next : pidsToInclude) {
|
||||
if (original.contains(next) == false && allAdded.contains(next) == false) {
|
||||
|
@ -1841,19 +1844,15 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
nextRoundMatches = pidsToInclude;
|
||||
} while (includes.size() > 0 && nextRoundMatches.size() > 0 && addedSomeThisRound);
|
||||
|
||||
ourLog.info("Loaded {} {} in {} rounds and {} ms", new Object[] {allAdded.size(), theReverseMode ? "_revincludes" : "_includes", roundCounts, w.getMillisAndRestart()});
|
||||
ourLog.info("Loaded {} {} in {} rounds and {} ms", allAdded.size(), theReverseMode ? "_revincludes" : "_includes", roundCounts, w.getMillisAndRestart());
|
||||
|
||||
return allAdded;
|
||||
}
|
||||
|
||||
private void searchForIdsWithAndOr(SearchParameterMap theParams) {
|
||||
SearchParameterMap params = theParams;
|
||||
if (params == null) {
|
||||
params = new SearchParameterMap();
|
||||
}
|
||||
private void searchForIdsWithAndOr(@Nonnull SearchParameterMap theParams) {
|
||||
myParams = theParams;
|
||||
|
||||
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamEntry : params.entrySet()) {
|
||||
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamEntry : myParams.entrySet()) {
|
||||
String nextParamName = nextParamEntry.getKey();
|
||||
List<List<? extends IQueryParameterType>> andOrParams = nextParamEntry.getValue();
|
||||
searchForIdsWithAndOr(myResourceName, nextParamName, andOrParams);
|
||||
|
@ -2182,7 +2181,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
myCurrentOffset = end;
|
||||
Collection<Long> pidsToScan = myCurrentPids.subList(start, end);
|
||||
Set<Include> includes = Collections.singleton(new Include("*", true));
|
||||
Set<Long> newPids = loadReverseIncludes(myCallingDao, myContext, myEntityManager, pidsToScan, includes, false, myParams.getLastUpdated());
|
||||
Set<Long> newPids = loadIncludes(myCallingDao, myContext, myEntityManager, pidsToScan, includes, false, myParams.getLastUpdated());
|
||||
myCurrentIterator = newPids.iterator();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,24 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
|
@ -11,9 +30,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -21,20 +40,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.*;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
public class SearchParameterMap extends LinkedHashMap<String, List<List<? extends IQueryParameterType>>> {
|
||||
|
||||
|
@ -48,7 +53,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
private Integer myLoadSynchronousUpTo;
|
||||
private Set<Include> myRevIncludes;
|
||||
private SortSpec mySort;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -130,7 +135,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
|
||||
private void addUrlIncludeParams(StringBuilder b, String paramName, Set<Include> theList) {
|
||||
ArrayList<Include> list = new ArrayList<Include>(theList);
|
||||
|
||||
|
||||
Collections.sort(list, new IncludeComparator());
|
||||
for (Include nextInclude : list) {
|
||||
addUrlParamSeparator(b);
|
||||
|
@ -158,10 +163,18 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return myCount;
|
||||
}
|
||||
|
||||
public void setCount(Integer theCount) {
|
||||
myCount = theCount;
|
||||
}
|
||||
|
||||
public EverythingModeEnum getEverythingMode() {
|
||||
return myEverythingMode;
|
||||
}
|
||||
|
||||
public void setEverythingMode(EverythingModeEnum theConsolidateMatches) {
|
||||
myEverythingMode = theConsolidateMatches;
|
||||
}
|
||||
|
||||
public Set<Include> getIncludes() {
|
||||
if (myIncludes == null) {
|
||||
myIncludes = new HashSet<Include>();
|
||||
|
@ -169,6 +182,10 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return myIncludes;
|
||||
}
|
||||
|
||||
public void setIncludes(Set<Include> theIncludes) {
|
||||
myIncludes = theIncludes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if there is no last updated value
|
||||
*/
|
||||
|
@ -181,6 +198,10 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return myLastUpdated;
|
||||
}
|
||||
|
||||
public void setLastUpdated(DateRangeParam theLastUpdated) {
|
||||
myLastUpdated = theLastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if there is no last updated value, and removes the lastupdated
|
||||
* value from this map
|
||||
|
@ -199,6 +220,19 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return myLoadSynchronousUpTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, tells the server to load these results synchronously, and not to load
|
||||
* more than X results. Note that setting this to a value will also set
|
||||
* {@link #setLoadSynchronous(boolean)} to true
|
||||
*/
|
||||
public SearchParameterMap setLoadSynchronousUpTo(Integer theLoadSynchronousUpTo) {
|
||||
myLoadSynchronousUpTo = theLoadSynchronousUpTo;
|
||||
if (myLoadSynchronousUpTo != null) {
|
||||
setLoadSynchronous(true);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set<Include> getRevIncludes() {
|
||||
if (myRevIncludes == null) {
|
||||
myRevIncludes = new HashSet<>();
|
||||
|
@ -206,10 +240,18 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return myRevIncludes;
|
||||
}
|
||||
|
||||
public void setRevIncludes(Set<Include> theRevIncludes) {
|
||||
myRevIncludes = theRevIncludes;
|
||||
}
|
||||
|
||||
public SortSpec getSort() {
|
||||
return mySort;
|
||||
}
|
||||
|
||||
public void setSort(SortSpec theSort) {
|
||||
mySort = theSort;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will only return true if all parameters have no modifier of any kind
|
||||
*/
|
||||
|
@ -234,22 +276,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return myLoadSynchronous;
|
||||
}
|
||||
|
||||
public void setCount(Integer theCount) {
|
||||
myCount = theCount;
|
||||
}
|
||||
|
||||
public void setEverythingMode(EverythingModeEnum theConsolidateMatches) {
|
||||
myEverythingMode = theConsolidateMatches;
|
||||
}
|
||||
|
||||
public void setIncludes(Set<Include> theIncludes) {
|
||||
myIncludes = theIncludes;
|
||||
}
|
||||
|
||||
public void setLastUpdated(DateRangeParam theLastUpdated) {
|
||||
myLastUpdated = theLastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, tells the server to load these results synchronously, and not to load
|
||||
* more than X results
|
||||
|
@ -259,27 +285,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, tells the server to load these results synchronously, and not to load
|
||||
* more than X results. Note that setting this to a value will also set
|
||||
* {@link #setLoadSynchronous(boolean)} to true
|
||||
*/
|
||||
public SearchParameterMap setLoadSynchronousUpTo(Integer theLoadSynchronousUpTo) {
|
||||
myLoadSynchronousUpTo = theLoadSynchronousUpTo;
|
||||
if (myLoadSynchronousUpTo != null) {
|
||||
setLoadSynchronous(true);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setRevIncludes(Set<Include> theRevIncludes) {
|
||||
myRevIncludes = theRevIncludes;
|
||||
}
|
||||
|
||||
public void setSort(SortSpec theSort) {
|
||||
mySort = theSort;
|
||||
}
|
||||
|
||||
public String toNormalizedQueryString(FhirContext theCtx) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
||||
|
@ -298,7 +303,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
nextValuesOrsOut.add(nextValueOrIn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Collections.sort(nextValuesOrsOut, new QueryParameterTypeComparator(theCtx));
|
||||
|
||||
if (nextValuesOrsOut.size() > 0) {
|
||||
|
@ -308,7 +313,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
} // for AND
|
||||
|
||||
Collections.sort(nextValuesAndsOut, new QueryParameterOrComparator(theCtx));
|
||||
|
||||
|
||||
for (List<IQueryParameterType> nextValuesAnd : nextValuesAndsOut) {
|
||||
addUrlParamSeparator(b);
|
||||
IQueryParameterType firstValue = nextValuesAnd.get(0);
|
||||
|
@ -319,18 +324,18 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
b.append('=');
|
||||
if (firstValue.getMissing()) {
|
||||
b.append(Constants.PARAMQUALIFIER_MISSING_TRUE);
|
||||
}else {
|
||||
} else {
|
||||
b.append(Constants.PARAMQUALIFIER_MISSING_FALSE);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isNotBlank(firstValue.getQueryParameterQualifier())){
|
||||
|
||||
if (isNotBlank(firstValue.getQueryParameterQualifier())) {
|
||||
b.append(firstValue.getQueryParameterQualifier());
|
||||
}
|
||||
|
||||
|
||||
b.append('=');
|
||||
|
||||
|
||||
for (int i = 0; i < nextValuesAnd.size(); i++) {
|
||||
IQueryParameterType nextValueOr = nextValuesAnd.get(i);
|
||||
if (i > 0) {
|
||||
|
@ -341,13 +346,13 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
b.append(UrlUtil.escapeUrlParam(valueAsQueryToken));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // for keys
|
||||
|
||||
|
||||
SortSpec sort = getSort();
|
||||
boolean first = true;
|
||||
while (sort != null) {
|
||||
|
||||
|
||||
if (isNotBlank(sort.getParamName())) {
|
||||
if (first) {
|
||||
addUrlParamSeparator(b);
|
||||
|
@ -362,32 +367,32 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
}
|
||||
b.append(sort.getParamName());
|
||||
}
|
||||
|
||||
|
||||
Validate.isTrue(sort != sort.getChain()); // just in case, shouldn't happen
|
||||
sort = sort.getChain();
|
||||
}
|
||||
|
||||
|
||||
addUrlIncludeParams(b, Constants.PARAM_INCLUDE, getIncludes());
|
||||
addUrlIncludeParams(b, Constants.PARAM_REVINCLUDE, getRevIncludes());
|
||||
|
||||
|
||||
if (getLastUpdated() != null) {
|
||||
DateParam lb = getLastUpdated().getLowerBound();
|
||||
addLastUpdateParam(b, lb);
|
||||
DateParam ub = getLastUpdated().getUpperBound();
|
||||
addLastUpdateParam(b, ub);
|
||||
}
|
||||
|
||||
|
||||
if (getCount() != null) {
|
||||
addUrlParamSeparator(b);
|
||||
b.append(Constants.PARAM_COUNT);
|
||||
b.append('=');
|
||||
b.append(getCount());
|
||||
}
|
||||
|
||||
|
||||
if (b.length() == 0) {
|
||||
b.append('?');
|
||||
}
|
||||
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
|
@ -439,7 +444,10 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
/*
|
||||
* Don't reorder! We rely on the ordinals
|
||||
*/
|
||||
ENCOUNTER_INSTANCE(false, true, true), ENCOUNTER_TYPE(false, true, false), PATIENT_INSTANCE(true, false, true), PATIENT_TYPE(true, false, false);
|
||||
ENCOUNTER_INSTANCE(false, true, true),
|
||||
ENCOUNTER_TYPE(false, true, false),
|
||||
PATIENT_INSTANCE(true, false, true),
|
||||
PATIENT_TYPE(true, false, false);
|
||||
|
||||
private final boolean myEncounter;
|
||||
|
||||
|
@ -447,7 +455,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
|
||||
private final boolean myPatient;
|
||||
|
||||
private EverythingModeEnum(boolean thePatient, boolean theEncounter, boolean theInstance) {
|
||||
EverythingModeEnum(boolean thePatient, boolean theEncounter, boolean theInstance) {
|
||||
assert thePatient ^ theEncounter;
|
||||
myPatient = thePatient;
|
||||
myEncounter = theEncounter;
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
package ca.uhn.fhir.jpa.entity;
|
||||
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.left;
|
||||
|
||||
/*
|
||||
|
@ -11,9 +19,9 @@ import static org.apache.commons.lang3.StringUtils.left;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -22,106 +30,88 @@ import static org.apache.commons.lang3.StringUtils.left;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.hibernate.annotations.Fetch;
|
||||
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
|
||||
@Entity
|
||||
@Table(name = "HFJ_SEARCH", uniqueConstraints= {
|
||||
@UniqueConstraint(name="IDX_SEARCH_UUID", columnNames="SEARCH_UUID")
|
||||
}, indexes= {
|
||||
@Index(name="IDX_SEARCH_LASTRETURNED", columnList="SEARCH_LAST_RETURNED"),
|
||||
@Index(name="IDX_SEARCH_RESTYPE_HASHS", columnList="RESOURCE_TYPE,SEARCH_QUERY_STRING_HASH,CREATED")
|
||||
@Table(name = "HFJ_SEARCH", uniqueConstraints = {
|
||||
@UniqueConstraint(name = "IDX_SEARCH_UUID", columnNames = "SEARCH_UUID")
|
||||
}, indexes = {
|
||||
@Index(name = "IDX_SEARCH_LASTRETURNED", columnList = "SEARCH_LAST_RETURNED"),
|
||||
@Index(name = "IDX_SEARCH_RESTYPE_HASHS", columnList = "RESOURCE_TYPE,SEARCH_QUERY_STRING_HASH,CREATED")
|
||||
})
|
||||
public class Search implements Serializable {
|
||||
|
||||
private static final int MAX_SEARCH_QUERY_STRING = 10000;
|
||||
private static final int UUID_COLUMN_LENGTH = 36;
|
||||
private static final int FAILURE_MESSAGE_LENGTH = 500;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
public static final int MAX_SEARCH_QUERY_STRING = 10000;
|
||||
public static final int UUID_COLUMN_LENGTH = 36;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name="CREATED", nullable=false, updatable=false)
|
||||
@Column(name = "CREATED", nullable = false, updatable = false)
|
||||
private Date myCreated;
|
||||
|
||||
@Column(name="FAILURE_CODE", nullable=true)
|
||||
@Column(name = "FAILURE_CODE", nullable = true)
|
||||
private Integer myFailureCode;
|
||||
|
||||
@Column(name="FAILURE_MESSAGE", length=FAILURE_MESSAGE_LENGTH, nullable=true)
|
||||
|
||||
@Column(name = "FAILURE_MESSAGE", length = FAILURE_MESSAGE_LENGTH, nullable = true)
|
||||
private String myFailureMessage;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_SEARCH")
|
||||
@SequenceGenerator(name="SEQ_SEARCH", sequenceName="SEQ_SEARCH")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SEARCH")
|
||||
@SequenceGenerator(name = "SEQ_SEARCH", sequenceName = "SEQ_SEARCH")
|
||||
@Column(name = "PID")
|
||||
private Long myId;
|
||||
|
||||
@OneToMany(mappedBy="mySearch")
|
||||
@OneToMany(mappedBy = "mySearch")
|
||||
private Collection<SearchInclude> myIncludes;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name="LAST_UPDATED_HIGH", nullable=true, insertable=true, updatable=false)
|
||||
@Column(name = "LAST_UPDATED_HIGH", nullable = true, insertable = true, updatable = false)
|
||||
private Date myLastUpdatedHigh;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name="LAST_UPDATED_LOW", nullable=true, insertable=true, updatable=false)
|
||||
@Column(name = "LAST_UPDATED_LOW", nullable = true, insertable = true, updatable = false)
|
||||
private Date myLastUpdatedLow;
|
||||
|
||||
@Column(name="NUM_FOUND", nullable=false)
|
||||
@Column(name = "NUM_FOUND", nullable = false)
|
||||
private int myNumFound;
|
||||
|
||||
@Column(name="PREFERRED_PAGE_SIZE", nullable=true)
|
||||
@Column(name = "PREFERRED_PAGE_SIZE", nullable = true)
|
||||
private Integer myPreferredPageSize;
|
||||
|
||||
@Column(name="RESOURCE_ID", nullable=true)
|
||||
@Column(name = "RESOURCE_ID", nullable = true)
|
||||
private Long myResourceId;
|
||||
|
||||
@Column(name="RESOURCE_TYPE", length=200, nullable=true)
|
||||
|
||||
@Column(name = "RESOURCE_TYPE", length = 200, nullable = true)
|
||||
private String myResourceType;
|
||||
|
||||
@OneToMany(mappedBy="mySearch")
|
||||
@OneToMany(mappedBy = "mySearch")
|
||||
private Collection<SearchResult> myResults;
|
||||
|
||||
@NotNull
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name="SEARCH_LAST_RETURNED", nullable=false, updatable=false)
|
||||
@Column(name = "SEARCH_LAST_RETURNED", nullable = false, updatable = false)
|
||||
private Date mySearchLastReturned;
|
||||
|
||||
@Lob()
|
||||
@Basic(fetch=FetchType.LAZY)
|
||||
@Column(name="SEARCH_QUERY_STRING", nullable=true, updatable=false, length = MAX_SEARCH_QUERY_STRING)
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
@Column(name = "SEARCH_QUERY_STRING", nullable = true, updatable = false, length = MAX_SEARCH_QUERY_STRING)
|
||||
private String mySearchQueryString;
|
||||
|
||||
@Column(name="SEARCH_QUERY_STRING_HASH", nullable=true, updatable=false)
|
||||
@Column(name = "SEARCH_QUERY_STRING_HASH", nullable = true, updatable = false)
|
||||
private Integer mySearchQueryStringHash;
|
||||
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(name="SEARCH_TYPE", nullable=false)
|
||||
@Column(name = "SEARCH_TYPE", nullable = false)
|
||||
private SearchTypeEnum mySearchType;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name="SEARCH_STATUS", nullable=false, length=10)
|
||||
@Column(name = "SEARCH_STATUS", nullable = false, length = 10)
|
||||
private SearchStatusEnum myStatus;
|
||||
|
||||
@Column(name="TOTAL_COUNT", nullable=true)
|
||||
@Column(name = "TOTAL_COUNT", nullable = true)
|
||||
private Integer myTotalCount;
|
||||
|
||||
@Column(name="SEARCH_UUID", length= UUID_COLUMN_LENGTH, nullable=false, updatable=false)
|
||||
|
||||
@Column(name = "SEARCH_UUID", length = UUID_COLUMN_LENGTH, nullable = false, updatable = false)
|
||||
private String myUuid;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -133,21 +123,33 @@ public class Search implements Serializable {
|
|||
return myCreated;
|
||||
}
|
||||
|
||||
public void setCreated(Date theCreated) {
|
||||
myCreated = theCreated;
|
||||
}
|
||||
|
||||
public Integer getFailureCode() {
|
||||
return myFailureCode;
|
||||
}
|
||||
|
||||
public void setFailureCode(Integer theFailureCode) {
|
||||
myFailureCode = theFailureCode;
|
||||
}
|
||||
|
||||
public String getFailureMessage() {
|
||||
return myFailureMessage;
|
||||
}
|
||||
|
||||
public void setFailureMessage(String theFailureMessage) {
|
||||
myFailureMessage = left(theFailureMessage, FAILURE_MESSAGE_LENGTH);
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public Collection<SearchInclude> getIncludes() {
|
||||
if (myIncludes == null) {
|
||||
myIncludes = new ArrayList<SearchInclude>();
|
||||
myIncludes = new ArrayList<>();
|
||||
}
|
||||
return myIncludes;
|
||||
}
|
||||
|
@ -160,6 +162,16 @@ public class Search implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
public void setLastUpdated(DateRangeParam theLastUpdated) {
|
||||
if (theLastUpdated == null) {
|
||||
myLastUpdatedLow = null;
|
||||
myLastUpdatedHigh = null;
|
||||
} else {
|
||||
myLastUpdatedLow = theLastUpdated.getLowerBoundAsInstant();
|
||||
myLastUpdatedHigh = theLastUpdated.getUpperBoundAsInstant();
|
||||
}
|
||||
}
|
||||
|
||||
public Date getLastUpdatedHigh() {
|
||||
return myLastUpdatedHigh;
|
||||
}
|
||||
|
@ -172,90 +184,46 @@ public class Search implements Serializable {
|
|||
return myNumFound;
|
||||
}
|
||||
|
||||
public void setNumFound(int theNumFound) {
|
||||
myNumFound = theNumFound;
|
||||
}
|
||||
|
||||
public Integer getPreferredPageSize() {
|
||||
return myPreferredPageSize;
|
||||
}
|
||||
|
||||
public void setPreferredPageSize(Integer thePreferredPageSize) {
|
||||
myPreferredPageSize = thePreferredPageSize;
|
||||
}
|
||||
|
||||
public Long getResourceId() {
|
||||
return myResourceId;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
public Date getSearchLastReturned() {
|
||||
return mySearchLastReturned;
|
||||
}
|
||||
|
||||
public String getSearchQueryString() {
|
||||
return mySearchQueryString;
|
||||
}
|
||||
|
||||
public SearchTypeEnum getSearchType() {
|
||||
return mySearchType;
|
||||
}
|
||||
|
||||
public SearchStatusEnum getStatus() {
|
||||
return myStatus;
|
||||
}
|
||||
|
||||
public Integer getTotalCount() {
|
||||
return myTotalCount;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return myUuid;
|
||||
}
|
||||
|
||||
public void setCreated(Date theCreated) {
|
||||
myCreated = theCreated;
|
||||
}
|
||||
|
||||
|
||||
public void setFailureCode(Integer theFailureCode) {
|
||||
myFailureCode = theFailureCode;
|
||||
}
|
||||
|
||||
public void setFailureMessage(String theFailureMessage) {
|
||||
myFailureMessage = left(theFailureMessage, FAILURE_MESSAGE_LENGTH);
|
||||
}
|
||||
|
||||
public void setLastUpdated(Date theLowerBound, Date theUpperBound) {
|
||||
myLastUpdatedLow = theLowerBound;
|
||||
myLastUpdatedHigh = theUpperBound;
|
||||
}
|
||||
public void setLastUpdated(DateRangeParam theLastUpdated) {
|
||||
if (theLastUpdated == null) {
|
||||
myLastUpdatedLow = null;
|
||||
myLastUpdatedHigh = null;
|
||||
} else {
|
||||
myLastUpdatedLow = theLastUpdated.getLowerBoundAsInstant();
|
||||
myLastUpdatedHigh = theLastUpdated.getUpperBoundAsInstant();
|
||||
}
|
||||
}
|
||||
|
||||
public void setNumFound(int theNumFound) {
|
||||
myNumFound = theNumFound;
|
||||
}
|
||||
|
||||
public void setPreferredPageSize(Integer thePreferredPageSize) {
|
||||
myPreferredPageSize = thePreferredPageSize;
|
||||
}
|
||||
|
||||
public void setResourceId(Long theResourceId) {
|
||||
myResourceId = theResourceId;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String theResourceType) {
|
||||
myResourceType = theResourceType;
|
||||
}
|
||||
|
||||
public Date getSearchLastReturned() {
|
||||
return mySearchLastReturned;
|
||||
}
|
||||
|
||||
public void setSearchLastReturned(Date theDate) {
|
||||
mySearchLastReturned = theDate;
|
||||
}
|
||||
|
||||
public String getSearchQueryString() {
|
||||
return mySearchQueryString;
|
||||
}
|
||||
|
||||
public void setSearchQueryString(String theSearchQueryString) {
|
||||
if (theSearchQueryString != null && theSearchQueryString.length() > MAX_SEARCH_QUERY_STRING) {
|
||||
mySearchQueryString = null;
|
||||
|
@ -264,26 +232,47 @@ public class Search implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
public void setSearchQueryStringHash(Integer theSearchQueryStringHash) {
|
||||
mySearchQueryStringHash = theSearchQueryStringHash;
|
||||
public SearchTypeEnum getSearchType() {
|
||||
return mySearchType;
|
||||
}
|
||||
|
||||
public void setSearchType(SearchTypeEnum theSearchType) {
|
||||
mySearchType = theSearchType;
|
||||
}
|
||||
|
||||
public SearchStatusEnum getStatus() {
|
||||
return myStatus;
|
||||
}
|
||||
|
||||
public void setStatus(SearchStatusEnum theStatus) {
|
||||
myStatus = theStatus;
|
||||
}
|
||||
|
||||
public Integer getTotalCount() {
|
||||
return myTotalCount;
|
||||
}
|
||||
|
||||
public void setTotalCount(Integer theTotalCount) {
|
||||
myTotalCount = theTotalCount;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return myUuid;
|
||||
}
|
||||
|
||||
public void setUuid(String theUuid) {
|
||||
myUuid = theUuid;
|
||||
}
|
||||
|
||||
public void setLastUpdated(Date theLowerBound, Date theUpperBound) {
|
||||
myLastUpdatedLow = theLowerBound;
|
||||
myLastUpdatedHigh = theUpperBound;
|
||||
}
|
||||
|
||||
public void setSearchQueryStringHash(Integer theSearchQueryStringHash) {
|
||||
mySearchQueryStringHash = theSearchQueryStringHash;
|
||||
}
|
||||
|
||||
private Set<Include> toIncList(boolean theWantReverse) {
|
||||
HashSet<Include> retVal = new HashSet<Include>();
|
||||
for (SearchInclude next : getIncludes()) {
|
||||
|
@ -301,5 +290,8 @@ public class Search implements Serializable {
|
|||
public Set<Include> toRevIncludesList() {
|
||||
return toIncList(true);
|
||||
}
|
||||
|
||||
|
||||
public void addInclude(SearchInclude theInclude) {
|
||||
getIncludes().add(theInclude);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
|||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
|
@ -134,13 +135,8 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
final List<Long> pidsSubList = mySearchCoordinatorSvc.getResources(myUuid, theFromIndex, theToIndex);
|
||||
|
||||
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||
template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
@Override
|
||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||
return toResourceList(sb, pidsSubList);
|
||||
}
|
||||
});
|
||||
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
return template.execute(theStatus -> toResourceList(sb, pidsSubList));
|
||||
}
|
||||
|
||||
private void ensureDependenciesInjected() {
|
||||
|
@ -273,8 +269,8 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
List<IBaseResource> toResourceList(ISearchBuilder sb, List<Long> pidsSubList) {
|
||||
Set<Long> includedPids = new HashSet<>();
|
||||
if (mySearchEntity.getSearchType() == SearchTypeEnum.SEARCH) {
|
||||
includedPids.addAll(sb.loadReverseIncludes(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated()));
|
||||
includedPids.addAll(sb.loadReverseIncludes(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated()));
|
||||
includedPids.addAll(sb.loadIncludes(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated()));
|
||||
includedPids.addAll(sb.loadIncludes(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated()));
|
||||
}
|
||||
|
||||
// Execute the query and make sure we return distinct results
|
||||
|
|
|
@ -258,8 +258,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
* individually for pages as we return them to clients
|
||||
*/
|
||||
final Set<Long> includedPids = new HashSet<Long>();
|
||||
includedPids.addAll(sb.loadReverseIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated()));
|
||||
includedPids.addAll(sb.loadReverseIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated()));
|
||||
includedPids.addAll(sb.loadIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated()));
|
||||
includedPids.addAll(sb.loadIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated()));
|
||||
|
||||
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
||||
sb.loadResourcesByPid(pids, resources, includedPids, false, myEntityManager, myContext, theCallingDao);
|
||||
|
@ -336,10 +336,10 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
search.setSearchQueryStringHash(queryString.hashCode());
|
||||
|
||||
for (Include next : theParams.getIncludes()) {
|
||||
search.getIncludes().add(new SearchInclude(search, next.getValue(), false, next.isRecurse()));
|
||||
search.addInclude(new SearchInclude(search, next.getValue(), false, next.isRecurse()));
|
||||
}
|
||||
for (Include next : theParams.getRevIncludes()) {
|
||||
search.getIncludes().add(new SearchInclude(search, next.getValue(), true, next.isRecurse()));
|
||||
search.addInclude(new SearchInclude(search, next.getValue(), true, next.isRecurse()));
|
||||
}
|
||||
|
||||
SearchTask task = new SearchTask(search, theCallingDao, theParams, theResourceType, searchUuid);
|
||||
|
|
|
@ -47,7 +47,7 @@ import static org.hamcrest.Matchers.*;
|
|||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({"unchecked", "Duplicates"})
|
||||
public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchNoFtTest.class);
|
||||
|
||||
|
@ -145,6 +145,124 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
assertThat(ids, empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* See #1053
|
||||
*/
|
||||
@Test
|
||||
public void testLastUpdateShouldntApplyToIncludes() {
|
||||
SearchParameterMap map;
|
||||
List<String> ids;
|
||||
|
||||
Date beforeAll = new Date();
|
||||
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100);
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName("O1");
|
||||
org.setId("O1");
|
||||
myOrganizationDao.update(org);
|
||||
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100);
|
||||
|
||||
Date beforePatient = new Date();
|
||||
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setId("P1");
|
||||
p.setActive(true);
|
||||
p.setManagingOrganization(new Reference("Organization/O1"));
|
||||
myPatientDao.update(p);
|
||||
|
||||
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100);
|
||||
Date afterAll = new Date();
|
||||
|
||||
// Search with between date (should still return Organization even though
|
||||
// it was created before that date, since it's an include)
|
||||
map = new SearchParameterMap();
|
||||
map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(beforePatient));
|
||||
map.addInclude(Patient.INCLUDE_ORGANIZATION);
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(ids, contains("Patient/P1", "Organization/O1"));
|
||||
|
||||
// Search before everything
|
||||
map = new SearchParameterMap();
|
||||
map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(beforeAll));
|
||||
map.addInclude(Patient.INCLUDE_ORGANIZATION);
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(ids, contains("Patient/P1", "Organization/O1"));
|
||||
|
||||
// Search after everything
|
||||
map = new SearchParameterMap();
|
||||
map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(afterAll));
|
||||
map.addInclude(Patient.INCLUDE_ORGANIZATION);
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(ids, empty());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #1053
|
||||
*
|
||||
* Note that I don't know that _lastUpdate actually should apply to reverse includes. The
|
||||
* spec doesn't say one way or ther other, but it seems like sensible behaviour to me.
|
||||
*
|
||||
* Definitely the $everything operation depends on this behaviour, so if we change it
|
||||
* we need to account for the everything operation...
|
||||
*/
|
||||
@Test
|
||||
public void testLastUpdateShouldApplyToReverseIncludes() {
|
||||
SearchParameterMap map;
|
||||
List<String> ids;
|
||||
|
||||
// This gets updated in a sec..
|
||||
Organization org = new Organization();
|
||||
org.setActive(false);
|
||||
org.setId("O1");
|
||||
myOrganizationDao.update(org);
|
||||
|
||||
Date beforeAll = new Date();
|
||||
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setId("P1");
|
||||
p.setActive(true);
|
||||
p.setManagingOrganization(new Reference("Organization/O1"));
|
||||
myPatientDao.update(p);
|
||||
|
||||
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100);
|
||||
|
||||
Date beforeOrg = new Date();
|
||||
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100);
|
||||
|
||||
org = new Organization();
|
||||
org.setActive(true);
|
||||
org.setId("O1");
|
||||
myOrganizationDao.update(org);
|
||||
|
||||
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(100);
|
||||
Date afterAll = new Date();
|
||||
|
||||
// Everything should come back
|
||||
map = new SearchParameterMap();
|
||||
map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(beforeAll));
|
||||
map.addRevInclude(Patient.INCLUDE_ORGANIZATION);
|
||||
ids = toUnqualifiedVersionlessIdValues(myOrganizationDao.search(map));
|
||||
assertThat(ids, contains("Organization/O1", "Patient/P1"));
|
||||
|
||||
// Search before everything
|
||||
map = new SearchParameterMap();
|
||||
map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(beforeOrg));
|
||||
map.addInclude(Patient.INCLUDE_ORGANIZATION);
|
||||
ids = toUnqualifiedVersionlessIdValues(myOrganizationDao.search(map));
|
||||
assertThat(ids, contains("Organization/O1"));
|
||||
|
||||
// Search after everything
|
||||
map = new SearchParameterMap();
|
||||
map.setLastUpdated(new DateRangeParam().setLowerBoundInclusive(afterAll));
|
||||
map.addInclude(Patient.INCLUDE_ORGANIZATION);
|
||||
ids = toUnqualifiedVersionlessIdValues(myOrganizationDao.search(map));
|
||||
assertThat(ids, empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEverythingTimings() {
|
||||
String methodName = "testEverythingTimings";
|
||||
|
|
|
@ -1503,7 +1503,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
ourLog.info(output);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
|
||||
ourLog.info(ids.toString());
|
||||
assertThat(ids, containsInAnyOrder(pId, cId));
|
||||
assertThat(ids, containsInAnyOrder(pId, cId, oId));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
@ -1518,7 +1518,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
ourLog.info(output);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
|
||||
ourLog.info(ids.toString());
|
||||
assertThat(ids, containsInAnyOrder(pId, cId));
|
||||
assertThat(ids, containsInAnyOrder(pId, cId, oId));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
|
|
@ -1,31 +1,5 @@
|
|||
package ca.uhn.fhir.rest.param;
|
||||
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.APPROXIMATE;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.EQUAL;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.NOT_EQUAL;
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
|
@ -33,18 +7,57 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
|||
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.*;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class DateRangeParamTest {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
private static final SimpleDateFormat ourFmt;
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DateRangeParamTest.class);
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
|
||||
static {
|
||||
ourFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
private static DateRangeParam create(String theLower, String theUpper) throws InvalidRequestException {
|
||||
DateRangeParam p = new DateRangeParam();
|
||||
List<QualifiedParamList> tokens = new ArrayList<QualifiedParamList>();
|
||||
tokens.add(QualifiedParamList.singleton(null, theLower));
|
||||
if (theUpper != null) {
|
||||
tokens.add(QualifiedParamList.singleton(null, theUpper));
|
||||
}
|
||||
p.setValuesAsQueryTokens(ourCtx, null, tokens);
|
||||
return p;
|
||||
}
|
||||
|
||||
public static Date parse(String theString) throws ParseException {
|
||||
return ourFmt.parse(theString);
|
||||
}
|
||||
|
||||
public static Date parseM1(String theString) throws ParseException {
|
||||
return new Date(ourFmt.parse(theString).getTime() - 1L);
|
||||
}
|
||||
|
||||
private DateRangeParam create(String theString) {
|
||||
return new DateRangeParam(new DateParam(theString));
|
||||
}
|
||||
|
@ -85,7 +98,7 @@ public class DateRangeParamTest {
|
|||
assertEquals(parseM1("2011-01-02 00:00:00.0000"), create(">=2011-01-01", "<2011-01-02").getUpperBoundAsInstant());
|
||||
assertEquals(parse("2011-01-02 00:00:00.0000"), create(">2011-01-01", "<=2011-01-02").getLowerBoundAsInstant());
|
||||
assertEquals(parseM1("2011-01-03 00:00:00.0000"), create(">2011-01-01", "<=2011-01-02").getUpperBoundAsInstant());
|
||||
|
||||
|
||||
assertEquals(parse("2011-01-01 00:00:00.0000"), create("ge2011-01-01", "lt2011-01-02").getLowerBoundAsInstant());
|
||||
assertEquals(parseM1("2011-01-02 00:00:00.0000"), create("ge2011-01-01", "lt2011-01-02").getUpperBoundAsInstant());
|
||||
assertEquals(parse("2011-01-02 00:00:00.0000"), create("gt2011-01-01", "le2011-01-02").getLowerBoundAsInstant());
|
||||
|
@ -119,6 +132,26 @@ public class DateRangeParamTest {
|
|||
assertEquals(parseM1("2011-01-02 00:00:00.0000"), create("2011-01-01").getUpperBoundAsInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBoundsWithDatesInclusive() {
|
||||
DateRangeParam range = new DateRangeParam();
|
||||
range.setLowerBoundInclusive(new Date());
|
||||
range.setUpperBoundInclusive(new Date());
|
||||
|
||||
assertEquals(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, range.getLowerBound().getPrefix());
|
||||
assertEquals(ParamPrefixEnum.LESSTHAN_OR_EQUALS, range.getUpperBound().getPrefix());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBoundsWithDatesExclusive() {
|
||||
DateRangeParam range = new DateRangeParam();
|
||||
range.setLowerBoundExclusive(new Date());
|
||||
range.setUpperBoundExclusive(new Date());
|
||||
|
||||
assertEquals(ParamPrefixEnum.GREATERTHAN, range.getLowerBound().getPrefix());
|
||||
assertEquals(ParamPrefixEnum.LESSTHAN, range.getUpperBound().getPrefix());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrList() {
|
||||
assertNotNull(new DateOrListParam().newInstance());
|
||||
|
@ -195,46 +228,22 @@ public class DateRangeParamTest {
|
|||
assertEquals(parseM1("2014-01-01 00:00:00.0000"), create("gt2011", "le2013").getUpperBoundAsInstant());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testEqualsAndHashCode() {
|
||||
Date lowerBound = new Date(currentTimeMillis());
|
||||
Date upperBound = new Date(lowerBound.getTime() + SECONDS.toMillis(1));
|
||||
new EqualsTester()
|
||||
.addEqualityGroup(new DateRangeParam(),
|
||||
new DateRangeParam((Date) null, (Date) null))
|
||||
new DateRangeParam((Date) null, (Date) null))
|
||||
.addEqualityGroup(new DateRangeParam(lowerBound, upperBound),
|
||||
new DateRangeParam(new DateParam(GREATERTHAN_OR_EQUALS, lowerBound), new DateParam(LESSTHAN_OR_EQUALS, upperBound)))
|
||||
new DateRangeParam(new DateParam(GREATERTHAN_OR_EQUALS, lowerBound), new DateParam(LESSTHAN_OR_EQUALS, upperBound)))
|
||||
.addEqualityGroup(new DateRangeParam(new DateParam(EQUAL, lowerBound)),
|
||||
new DateRangeParam(new DateParam(null, lowerBound)),
|
||||
new DateRangeParam(new DateParam(EQUAL, lowerBound), new DateParam(EQUAL, lowerBound)))
|
||||
new DateRangeParam(new DateParam(null, lowerBound)),
|
||||
new DateRangeParam(new DateParam(EQUAL, lowerBound), new DateParam(EQUAL, lowerBound)))
|
||||
.addEqualityGroup(new DateRangeParam(lowerBound, null),
|
||||
new DateRangeParam(new DateParam(GREATERTHAN_OR_EQUALS, lowerBound), null))
|
||||
new DateRangeParam(new DateParam(GREATERTHAN_OR_EQUALS, lowerBound), null))
|
||||
.addEqualityGroup(new DateRangeParam(null, upperBound),
|
||||
new DateRangeParam(null, new DateParam(LESSTHAN_OR_EQUALS, upperBound)))
|
||||
new DateRangeParam(null, new DateParam(LESSTHAN_OR_EQUALS, upperBound)))
|
||||
.testEquals();
|
||||
}
|
||||
|
||||
private static DateRangeParam create(String theLower, String theUpper) throws InvalidRequestException {
|
||||
DateRangeParam p = new DateRangeParam();
|
||||
List<QualifiedParamList> tokens = new ArrayList<QualifiedParamList>();
|
||||
tokens.add(QualifiedParamList.singleton(null, theLower));
|
||||
if (theUpper != null) {
|
||||
tokens.add(QualifiedParamList.singleton(null, theUpper));
|
||||
}
|
||||
p.setValuesAsQueryTokens(ourCtx, null, tokens);
|
||||
return p;
|
||||
}
|
||||
|
||||
public static Date parse(String theString) throws ParseException {
|
||||
return ourFmt.parse(theString);
|
||||
}
|
||||
|
||||
public static Date parseM1(String theString) throws ParseException {
|
||||
return new Date(ourFmt.parse(theString).getTime() - 1L);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -249,6 +249,10 @@
|
|||
queue (only the ID), which should reduce the memory/disk footprint of the queue
|
||||
when it grows long.
|
||||
</action>
|
||||
<action type="fix" issue="1053">
|
||||
A bug was fixed in JPA server searches: When performing a search with a _lastUpdate
|
||||
filter, the filter was applied to any _include values, which it should not have been.
|
||||
Thanks to Deepak Garg for reporting!
|
||||
<action type="add">
|
||||
When performing a ConceptMap/$translate operation with reverse="true" in the arguments,
|
||||
the equivalency flag is now set on the response just as it is for a non-reverse lookup.
|
||||
|
|
Loading…
Reference in New Issue