implements IDao {
myPlatformTransactionManager = thePlatformTransactionManager;
}
+ private void setUpdatedTime(Collection extends BaseResourceIndexedSearchParam> theParams, Date theUpdateTime) {
+ for (BaseResourceIndexedSearchParam nextSearchParam : theParams) {
+ nextSearchParam.setUpdated(theUpdateTime);
+ }
+ }
+
/**
* This method is called when an update to an existing resource detects that the resource supplied for update is missing a tag/profile/security label that the currently persisted resource holds.
*
@@ -1224,6 +1290,7 @@ public abstract class BaseHapiFhirDao implements IDao {
Set coordsParams = null;
Set links = null;
+ Set populatedResourceLinkParameters = Collections.emptySet();
if (theDeletedTimestampOrNull != null) {
stringParams = Collections.emptySet();
@@ -1264,6 +1331,14 @@ public abstract class BaseHapiFhirDao implements IDao {
}
}
+ Set> activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType()).entrySet();
+ findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.STRING, stringParams);
+ findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.NUMBER, numberParams);
+ findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.QUANTITY, quantityParams);
+ findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.DATE, dateParams);
+ findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.URI, uriParams);
+ findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.TOKEN, tokenParams);
+
setUpdatedTime(stringParams, theUpdateTime);
setUpdatedTime(numberParams, theUpdateTime);
setUpdatedTime(quantityParams, theUpdateTime);
@@ -1323,7 +1398,7 @@ public abstract class BaseHapiFhirDao implements IDao {
}
links = new HashSet();
- extractResourceLinks(theEntity, theResource, links, theUpdateTime);
+ populatedResourceLinkParameters = extractResourceLinks(theEntity, theResource, links, theUpdateTime);
/*
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
@@ -1392,6 +1467,26 @@ public abstract class BaseHapiFhirDao implements IDao {
postUpdate(theEntity, (T) theResource);
}
+ /*
+ * Update the "search param present" table which is used for the
+ * ?foo:missing=true queries
+ */
+ if (thePerformIndexing) {
+ Map presentSearchParams = new HashMap();
+ for (String nextKey : populatedResourceLinkParameters) {
+ presentSearchParams.put(nextKey, Boolean.TRUE);
+ }
+ Set> activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType()).entrySet();
+ for (Entry nextSpEntry : activeSearchParams) {
+ if (nextSpEntry.getValue().getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
+ if (!presentSearchParams.containsKey(nextSpEntry.getKey())) {
+ presentSearchParams.put(nextSpEntry.getKey(), Boolean.FALSE);
+ }
+ }
+ }
+ mySearchParamPresenceSvc.updatePresence(theEntity, presentSearchParams);
+ }
+
/*
* Create history entry
*/
@@ -1482,16 +1577,16 @@ public abstract class BaseHapiFhirDao implements IDao {
return theEntity;
}
- private void setUpdatedTime(Collection extends BaseResourceIndexedSearchParam> theParams, Date theUpdateTime) {
- for (BaseResourceIndexedSearchParam nextSearchParam : theParams) {
- nextSearchParam.setUpdated(theUpdateTime);
- }
- }
-
protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable entity, Date theDeletedTimestampOrNull, Date theUpdateTime) {
return updateEntity(theResource, entity, theDeletedTimestampOrNull, true, true, theUpdateTime);
}
+ private void updateSearchParamPresent(Map presentSearchParams, Set extends BaseResourceIndexedSearchParam> params) {
+ for (BaseResourceIndexedSearchParam nextSearchParam : params) {
+ presentSearchParams.put(nextSearchParam.getParamName(), Boolean.TRUE);
+ }
+ }
+
private void validateChildReferences(IBase theElement, String thePath) {
if (theElement == null) {
return;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
index 9034b735247..c6120c51e0f 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
@@ -78,8 +78,6 @@ public abstract class BaseHapiFhirResourceDao extends B
protected PlatformTransactionManager myPlatformTransactionManager;
@Autowired
private IResourceHistoryTableDao myResourceHistoryTableDao;
- @Autowired()
- protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
private String myResourceName;
@Autowired
protected IResourceTableDao myResourceTableDao;
@@ -89,10 +87,6 @@ public abstract class BaseHapiFhirResourceDao extends B
@Autowired()
protected ISearchResultDao mySearchResultDao;
private String mySecondaryPrimaryKeyParamName;
- @Autowired
- private ISearchParamRegistry mySerarchParamRegistry;
- @Autowired()
- protected IHapiTerminologySvc myTerminologySvc;
@Override
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
@@ -599,87 +593,6 @@ public abstract class BaseHapiFhirResourceDao extends B
return retVal;
}
- // @Override
- // public IBundleProvider everything(IIdType theId) {
- // Search search = new Search();
- // search.setUuid(UUID.randomUUID().toString());
- // search.setCreated(new Date());
- // myEntityManager.persist(search);
- //
- // List results = new ArrayList();
- // if (theId != null) {
- // Long pid = translateForcedIdToPid(theId);
- // ResourceTable entity = myEntityManager.find(ResourceTable.class, pid);
- // validateGivenIdIsAppropriateToRetrieveResource(theId, entity);
- // SearchResult res = new SearchResult(search);
- // res.setResourcePid(pid);
- // results.add(res);
- // } else {
- // TypedQuery query = createSearchAllByTypeQuery();
- // for (Tuple next : query.getResultList()) {
- // SearchResult res = new SearchResult(search);
- // res.setResourcePid(next.get(0, Long.class));
- // results.add(res);
- // }
- // }
- //
- // int totalCount = results.size();
- // mySearchResultDao.save(results);
- // mySearchResultDao.flush();
- //
- // CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- //
- // // Load _revincludes
- // CriteriaQuery cq = builder.createQuery(Long.class);
- // Root from = cq.from(ResourceLink.class);
- // cq.select(from.get("mySourceResourcePid").as(Long.class));
- //
- // Subquery pidsSubquery = cq.subquery(Long.class);
- // Root pidsSubqueryFrom = pidsSubquery.from(SearchResult.class);
- // pidsSubquery.select(pidsSubqueryFrom.get("myResourcePid").as(Long.class));
- // pidsSubquery.where(pidsSubqueryFrom.get("mySearch").in(search));
- //
- // cq.where(from.get("myTargetResourceId").in(pidsSubquery));
- // TypedQuery query = myEntityManager.createQuery(cq);
- //
- // results = new ArrayList();
- // for (Long next : query.getResultList()) {
- // SearchResult res = new SearchResult(search);
- // res.setResourcePid(next);
- // results.add(res);
- // }
- //
- // // Save _revincludes
- // totalCount += results.size();
- // mySearchResultDao.save(results);
- // mySearchResultDao.flush();
- //
- // final int finalTotalCount = totalCount;
- // return new IBundleProvider() {
- //
- // @Override
- // public int size() {
- // return finalTotalCount;
- // }
- //
- // @Override
- // public Integer preferredPageSize() {
- // return null;
- // }
- //
- // @Override
- // public List getResources(int theFromIndex, int theToIndex) {
- // // TODO Auto-generated method stub
- // return null;
- // }
- //
- // @Override
- // public InstantDt getPublished() {
- // // TODO Auto-generated method stub
- // return null;
- // }
- // };
- // }
@Override
public MT metaGetOperation(Class theType, IIdType theId, RequestDetails theRequestDetails) {
@@ -761,7 +674,6 @@ public abstract class BaseHapiFhirResourceDao extends B
* Subclasses may override to provide behaviour. Invoked within a delete
* transaction with the resource that is about to be deleted.
*/
- @SuppressWarnings("unused")
protected void preDelete(T theResourceToDelete, ResourceTable theEntityToDelete) {
// nothing by default
}
@@ -948,32 +860,29 @@ public abstract class BaseHapiFhirResourceDao extends B
ourLog.info("Processed remove tag {}/{} on {} in {}ms", new Object[] { theScheme, theTerm, theId.getValue(), w.getMillisAndRestart() });
}
- @Override
- public IBundleProvider search(Map theParams) {
- SearchParameterMap map = new SearchParameterMap();
- for (Entry nextEntry : theParams.entrySet()) {
- map.add(nextEntry.getKey(), (nextEntry.getValue()));
- }
- return search(map);
- }
-
@Override
public IBundleProvider search(final SearchParameterMap theParams) {
- // Notify interceptors
- ActionRequestDetails requestDetails = new ActionRequestDetails(theParams.getRequestDetails(), getContext(), getResourceName(), null);
- notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
-
- SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao,
- myTerminologySvc, mySerarchParamRegistry);
- builder.setType(getResourceType(), getResourceName());
- return builder.search(theParams);
+ return search(theParams, null);
}
@Override
- public IBundleProvider search(String theParameterName, IQueryParameterType theValue) {
- return search(Collections.singletonMap(theParameterName, theValue));
+ public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails) {
+ // Notify interceptors
+ if (theRequestDetails != null) {
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), getResourceName(), null);
+ notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
+
+ if (theRequestDetails.isSubRequest()) {
+ theParams.setLoadSynchronous(true);
+ theParams.setLoadSynchronousUpTo(myDaoConfig.getMaximumSearchResultCountInTransaction());
+ }
+
+ }
+
+ return mySearchCoordinatorSvc.registerSearch(this, theParams, getResourceName());
}
+
@Override
public Set searchForIds(Map theParams) {
SearchParameterMap map = new SearchParameterMap();
@@ -990,13 +899,20 @@ public abstract class BaseHapiFhirResourceDao extends B
@Override
public Set searchForIdsWithAndOr(SearchParameterMap theParams) {
- theParams.setPersistResults(false);
- SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao,
- myTerminologySvc, mySerarchParamRegistry);
+ SearchBuilder builder = newSearchBuilder();
builder.setType(getResourceType(), getResourceName());
- builder.search(theParams);
- return builder.doGetPids();
+
+ // FIXME: fail if too many results
+
+ HashSet retVal = new HashSet();
+
+ Iterator iter = builder.createQuery(theParams);
+ while (iter.hasNext()) {
+ retVal.add(iter.next());
+ }
+
+ return retVal;
}
@SuppressWarnings("unchecked")
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
index 930fdcae038..6b8ccffa376 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
@@ -48,6 +48,13 @@ public class DaoConfig {
"http://hl7.org/fhir/codesystem-*",
"http://hl7.org/fhir/StructureDefinition/*")));
+ /**
+ * Default value for {@link #setMaximumSearchResultCountInTransaction(int)}
+ *
+ * @see #setMaximumSearchResultCountInTransaction(int)
+ */
+ private static final int DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION = 500;
+
// ***
// update setter javadoc if default changes
// ***
@@ -86,12 +93,14 @@ public class DaoConfig {
// update setter javadoc if default changes
// ***
private int myMaximumExpansionSize = 5000;
+ private int myMaximumSearchResultCountInTransaction = DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION;
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
private boolean mySchedulingDisabled;
private boolean mySubscriptionEnabled;
private long mySubscriptionPollDelay = 1000;
private Long mySubscriptionPurgeInactiveAfterMillis;
private Set myTreatBaseUrlsAsLocal = new HashSet();
+
private Set myTreatReferencesAsLogical = new HashSet(DEFAULT_LOGICAL_BASE_URLS);
/**
@@ -173,6 +182,18 @@ public class DaoConfig {
return myMaximumExpansionSize;
}
+ /**
+ * Provides the maximum number of results which may be returned by a search within a FHIR transaction
+ * operation. For example, if this value is set to 100
and a FHIR transaction is processed with a sub-request
+ * for Patient?gender=male
, the server will throw an error (and the transaction will fail) if there are more than
+ * 100 resources on the server which match this query.
+ *
+ * @see #DEFAULT_LOGICAL_BASE_URLS The default value for this setting
+ */
+ public int getMaximumSearchResultCountInTransaction() {
+ return myMaximumSearchResultCountInTransaction;
+ }
+
public ResourceEncodingEnum getResourceEncoding() {
return myResourceEncoding;
}
@@ -499,6 +520,18 @@ public class DaoConfig {
myMaximumExpansionSize = theMaximumExpansionSize;
}
+ /**
+ * Provides the maximum number of results which may be returned by a search within a FHIR transaction
+ * operation. For example, if this value is set to 100
and a FHIR transaction is processed with a sub-request
+ * for Patient?gender=male
, the server will throw an error (and the transaction will fail) if there are more than
+ * 100 resources on the server which match this query.
+ *
+ * @see #DEFAULT_LOGICAL_BASE_URLS The default value for this setting
+ */
+ public void setMaximumSearchResultCountInTransaction(int theMaximumSearchResultCountInTransaction) {
+ myMaximumSearchResultCountInTransaction = theMaximumSearchResultCountInTransaction;
+ }
+
public void setResourceEncoding(ResourceEncodingEnum theResourceEncoding) {
myResourceEncoding = theResourceEncoding;
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java
index 43eb3ec6556..c9c48450959 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java
@@ -26,7 +26,6 @@ import javax.servlet.http.HttpServletRequest;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
-import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.model.api.IResource;
@@ -47,9 +46,6 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2im
super();
}
- @Autowired
- private ISearchParamRegistry mySerarchParamRegistry;
-
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
@@ -69,9 +65,7 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2im
paramMap.add("_id", new StringParam(theId.getIdPart()));
}
- SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao, myTerminologySvc, mySerarchParamRegistry);
- builder.setType(getResourceType(), getResourceName());
- return builder.search(paramMap);
+ return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName());
}
@Override
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java
index 511a1b98f03..9905cd282dc 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java
@@ -110,7 +110,10 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2
if (defaultValueSet != null) {
source = getContext().newJsonParser().parseResource(ValueSet.class, myRiCtx.newJsonParser().encodeResourceToString(defaultValueSet));
} else {
- IBundleProvider ids = search(ValueSet.SP_URL, new UriParam(theUri));
+ SearchParameterMap params = new SearchParameterMap();
+ params.setLoadSynchronousUpTo(1);
+ params.add(ValueSet.SP_URL, new UriParam(theUri));
+ IBundleProvider ids = search(params);
if (ids.size() == 0) {
throw new InvalidRequestException("Unknown ValueSet URI: " + theUri);
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
index aebb98b7765..1bda6f5509d 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
@@ -66,6 +66,13 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao implem
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager myEntityManager;
+ /**
+ * Constructor
+ */
+ public FulltextSearchSvcImpl() {
+ super();
+ }
+
private void addTextSearch(QueryBuilder theQueryBuilder, BooleanJunction> theBoolean, List> theTerms, String theFieldName, String theFieldNameEdgeNGram, String theFieldNameNGram) {
if (theTerms == null) {
return;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java
index 098b090a123..f0470b6c2df 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java
@@ -64,21 +64,23 @@ public interface IDao {
FhirContext getContext();
+ RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName);
+
+ Collection getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef);
+
/**
* Populate all of the runtime dependencies that a bundle provider requires in order to work
*/
void injectDependenciesIntoBundleProvider(PersistedJpaBundleProvider theProvider);
+ ISearchBuilder newSearchBuilder();
+
+ void populateFullTextFields(IBaseResource theResource, ResourceTable theEntity);
+
Set processMatchUrl(String theMatchUrl, Class theResourceType);
IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation);
R toResource(Class theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation);
- void populateFullTextFields(IBaseResource theResource, ResourceTable theEntity);
-
- RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName);
-
- Collection getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef);
-
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java
index 85cb52645bb..81da4189ca4 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java
@@ -174,11 +174,9 @@ public interface IFhirResourceDao extends IDao {
void removeTag(IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode);
- IBundleProvider search(Map theParams);
+ IBundleProvider search(SearchParameterMap theParams);
- IBundleProvider search(SearchParameterMap theMap);
-
- IBundleProvider search(String theParameterName, IQueryParameterType theValue);
+ IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails);
Set searchForIds(Map theParams);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java
new file mode 100644
index 00000000000..24f282619ea
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java
@@ -0,0 +1,28 @@
+package ca.uhn.fhir.jpa.dao;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.persistence.EntityManager;
+
+import org.hl7.fhir.instance.model.api.IBaseResource;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.api.Include;
+import ca.uhn.fhir.rest.param.DateRangeParam;
+
+public interface ISearchBuilder {
+
+ Iterator createQuery(SearchParameterMap theParams);
+
+ void setType(Class extends IBaseResource> theResourceType, String theResourceName);
+
+ void loadResourcesByPid(Collection theIncludePids, List theResourceListToPopulate, Set theRevIncludedPids, boolean theForHistoryOperation, EntityManager theEntityManager,
+ FhirContext theContext, IDao theDao);
+
+ Set loadReverseIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection theMatches, Set theRevIncludes, boolean theReverseMode,
+ DateRangeParam theLastUpdated);
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java
index 94093e8d685..37060cf2bfe 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaValidationSupportDstu2.java
@@ -66,9 +66,11 @@ public class JpaValidationSupportDstu2 implements IJpaValidationSupportDstu2 {
String resourceName = myRiCtx.getResourceDefinition(theClass).getName();
IBundleProvider search;
if ("ValueSet".equals(resourceName)) {
- search = myValueSetDao.search(ca.uhn.fhir.model.dstu2.resource.ValueSet.SP_URL, new UriParam(theUri));
+ SearchParameterMap params = new SearchParameterMap(ca.uhn.fhir.model.dstu2.resource.ValueSet.SP_URL, new UriParam(theUri));
+ params.setLoadSynchronousUpTo(10);
+ search = myValueSetDao.search(params);
} else if ("StructureDefinition".equals(resourceName)) {
- search = myStructureDefinitionDao.search(ca.uhn.fhir.model.dstu2.resource.StructureDefinition.SP_URL, new UriParam(theUri));
+ search = myStructureDefinitionDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ca.uhn.fhir.model.dstu2.resource.StructureDefinition.SP_URL, new UriParam(theUri)));
} else {
throw new IllegalArgumentException("Can't fetch resource type: " + resourceName);
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
index 7beae42edc5..cc547808ea3 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
@@ -12,7 +12,7 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
* 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
+ * 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,
@@ -31,20 +31,17 @@ import java.util.*;
import java.util.Map.Entry;
import javax.persistence.EntityManager;
-import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.TransactionStatus;
-import org.springframework.transaction.support.TransactionCallback;
-import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -54,7 +51,6 @@ import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.*;
-import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
import ca.uhn.fhir.jpa.util.StopWatch;
@@ -73,39 +69,38 @@ import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
-import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.UrlUtil;
-public class SearchBuilder {
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class);
+public class SearchBuilder implements ISearchBuilder {
+ private static Long NO_MORE = Long.valueOf(-1);
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class);
+ private CriteriaBuilder myBuilder;
private BaseHapiFhirDao> myCallingDao;
private FhirContext myContext;
private EntityManager myEntityManager;
private IForcedIdDao myForcedIdDao;
+ private IFulltextSearchSvc myFulltextSearchSvc;
private SearchParameterMap myParams;
- private Collection myPids;
- private PlatformTransactionManager myPlatformTransactionManager;
+ private ArrayList myPredicates;
private IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
private String myResourceName;
+ private AbstractQuery myResourceTableQuery;
+ private Root myResourceTableRoot;
private Class extends IBaseResource> myResourceType;
- private IFulltextSearchSvc mySearchDao;
- private Search mySearchEntity;
- private ISearchResultDao mySearchResultDao;
private ISearchParamRegistry mySearchParamRegistry;
private IHapiTerminologySvc myTerminologySvc;
- public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, IFulltextSearchSvc theSearchDao, ISearchResultDao theSearchResultDao, BaseHapiFhirDao> theDao,
+ public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, IFulltextSearchSvc theFulltextSearchSvc,
+ ISearchResultDao theSearchResultDao, BaseHapiFhirDao> theDao,
IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao, IForcedIdDao theForcedIdDao, IHapiTerminologySvc theTerminologySvc, ISearchParamRegistry theSearchParamRegistry) {
myContext = theFhirContext;
myEntityManager = theEntityManager;
- myPlatformTransactionManager = thePlatformTransactionManager;
- mySearchDao = theSearchDao;
- mySearchResultDao = theSearchResultDao;
+ myFulltextSearchSvc = theFulltextSearchSvc;
myCallingDao = theDao;
myResourceIndexedSearchParamUriDao = theResourceIndexedSearchParamUriDao;
myForcedIdDao = theForcedIdDao;
@@ -113,77 +108,68 @@ public class SearchBuilder {
mySearchParamRegistry = theSearchParamRegistry;
}
- private void addPredicateComposite(RuntimeSearchParam theParamDef, List extends IQueryParameterType> theNextAnd) {
+ private void addPredicateComposite(String theResourceName, RuntimeSearchParam theParamDef, List extends IQueryParameterType> theNextAnd) {
// TODO: fail if missing is set for a composite query
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceTable.class);
- cq.select(from.get("myId").as(Long.class));
-
IQueryParameterType or = theNextAnd.get(0);
if (!(or instanceof CompositeParam, ?>)) {
throw new InvalidRequestException("Invalid type for composite param (must be " + CompositeParam.class.getSimpleName() + ": " + or.getClass());
}
CompositeParam, ?> cp = (CompositeParam, ?>) or;
- List predicates = new ArrayList();
- predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
-
RuntimeSearchParam left = theParamDef.getCompositeOf().get(0);
IQueryParameterType leftValue = cp.getLeftValue();
- predicates.add(createCompositeParamPart(builder, from, left, leftValue));
+ myPredicates.add(createCompositeParamPart(theResourceName, myResourceTableRoot, left, leftValue));
RuntimeSearchParam right = theParamDef.getCompositeOf().get(1);
IQueryParameterType rightValue = cp.getRightValue();
- predicates.add(createCompositeParamPart(builder, from, right, rightValue));
-
- createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
- cq.where(builder.and(toArray(predicates)));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(q.getResultList());
+ myPredicates.add(createCompositeParamPart(theResourceName, myResourceTableRoot, right, rightValue));
}
- private void addPredicateDate(String theParamName, List extends IQueryParameterType> theList) {
+ private void addPredicateDate(String theResourceName, String theParamName, List extends IQueryParameterType> theList) {
- if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
- addPredicateParamMissing("myParamsDate", theParamName, ResourceIndexedSearchParamDate.class);
+ Join join = myResourceTableRoot.join("myParamsDate", JoinType.LEFT);
+
+ if (theList.get(0).getMissing() != null) {
+ addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join);
return;
}
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceIndexedSearchParamDate.class);
- cq.select(from.get("myResourcePid").as(Long.class));
-
List codePredicates = new ArrayList();
for (IQueryParameterType nextOr : theList) {
- if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
- continue;
- }
-
IQueryParameterType params = nextOr;
- Predicate p = createPredicateDate(builder, from, params);
+ Predicate p = createPredicateDate(params, theResourceName, theParamName, myBuilder, join);
codePredicates.add(p);
}
- Predicate masterCodePredicate = builder.or(toArray(codePredicates));
+ Predicate orPredicates = myBuilder.or(toArray(codePredicates));
+ myPredicates.add(orPredicates);
- List predicates = new ArrayList();
- predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
- predicates.add(builder.equal(from.get("myParamName"), theParamName));
- createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
- createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
- predicates.add(masterCodePredicate);
-
- cq.where(builder.and(toArray(predicates)));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(q.getResultList());
}
+ // private void addPredicateId(Set thePids) {
+ // if (thePids == null || thePids.isEmpty()) {
+ // return;
+ // }
+ //
+ // CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
+ // CriteriaQuery cq = builder.createQuery(Long.class);
+ // Root from = cq.from(ResourceTable.class);
+ // cq.select(from.get("myId").as(Long.class));
+ //
+ // List predicates = new ArrayList();
+ // predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
+ // predicates.add(from.get("myId").in(thePids));
+ // createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
+ // createPredicateLastUpdatedForResourceTable(builder, from, predicates);
+ //
+ // cq.where(toArray(predicates));
+ //
+ // TypedQuery q = myEntityManager.createQuery(cq);
+ // doSetPids(q.getResultList());
+ // }
+
private void addPredicateHas(List> theHasParameters, DateRangeParam theLastUpdated) {
for (List extends IQueryParameterType> nextOrList : theHasParameters) {
@@ -229,65 +215,20 @@ public class SearchBuilder {
Class extends IBaseResource> resourceType = targetResourceDefinition.getImplementingClass();
Set match = myCallingDao.processMatchUrl(matchUrl, resourceType);
if (match.isEmpty()) {
- doSetPids(new ArrayList());
- return;
+ // Pick a PID that can never match
+ match = Collections.singleton(-1L);
}
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceLink.class);
- cq.select(from.get("myTargetResourcePid").as(Long.class));
+ Join join = myResourceTableRoot.join("myIncomingResourceLinks", JoinType.LEFT);
- List predicates = new ArrayList();
- predicates.add(builder.equal(from.get("mySourceResourceType"), targetResourceType));
- predicates.add(from.get("mySourceResourcePid").in(match));
- predicates.add(createResourceLinkPathPredicate(myCallingDao, myContext, owningParameter, from, resourceType));
- predicates.add(builder.equal(from.get("myTargetResourceType"), myResourceName));
- createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
- createPredicateLastUpdatedForResourceLink(builder, from, predicates);
-
- cq.where(toArray(predicates));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(q.getResultList());
- if (doHaveNoResults()) {
- return;
- }
-
- return;
+ Predicate predicate = join.get("mySourceResourcePid").in(match);
+ myPredicates.add(predicate);
}
}
- private void addPredicateId(Set thePids) {
- if (thePids == null || thePids.isEmpty()) {
- return;
- }
-
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceTable.class);
- cq.select(from.get("myId").as(Long.class));
-
- List predicates = new ArrayList();
- predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
- predicates.add(from.get("myId").in(thePids));
- createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
- createPredicateLastUpdatedForResourceTable(builder, from, predicates);
-
- cq.where(toArray(predicates));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(q.getResultList());
- }
-
private void addPredicateLanguage(List> theList) {
for (List extends IQueryParameterType> nextList : theList) {
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceTable.class);
- cq.select(from.get("myId").as(Long.class));
-
Set values = new HashSet();
for (IQueryParameterType next : nextList) {
if (next instanceof StringParam) {
@@ -305,74 +246,26 @@ public class SearchBuilder {
continue;
}
- List predicates = new ArrayList();
- predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
- predicates.add(from.get("myLanguage").as(String.class).in(values));
- createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
- createPredicateLastUpdatedForResourceTable(builder, from, predicates);
-
- predicates.add(builder.isNull(from.get("myDeleted")));
-
- cq.where(toArray(predicates));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(q.getResultList());
- if (doHaveNoResults()) {
- return;
- }
+ Predicate predicate = myResourceTableRoot.get("myLanguage").as(String.class).in(values);
+ myPredicates.add(predicate);
}
return;
}
- private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root extends BaseResourceIndexedSearchParam> from, List codePredicates, IQueryParameterType nextOr) {
- boolean missingFalse = false;
- if (nextOr.getMissing() != null) {
- if (nextOr.getMissing().booleanValue() == true) {
- throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "multipleParamsWithSameNameOneIsMissingTrue", theParamName));
- }
- Predicate singleCode = from.get("myId").isNotNull();
- Predicate name = theBuilder.equal(from.get("myParamName"), theParamName);
- codePredicates.add(theBuilder.and(name, singleCode));
- missingFalse = true;
- }
- return missingFalse;
- }
+ private void addPredicateNumber(String theResourceName, String theParamName, List extends IQueryParameterType> theList) {
- private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root extends ResourceLink> from, List codePredicates, IQueryParameterType nextOr) {
- boolean missingFalse = false;
- if (nextOr.getMissing() != null) {
- if (nextOr.getMissing().booleanValue() == true) {
- throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "multipleParamsWithSameNameOneIsMissingTrue", theParamName));
- }
- Predicate singleCode = from.get("mySourceResource").isNotNull();
- Predicate name = createResourceLinkPathPredicate(theParamName, from);
- codePredicates.add(theBuilder.and(name, singleCode));
- missingFalse = true;
- }
- return missingFalse;
- }
+ Join join = myResourceTableRoot.join("myParamsNumber", JoinType.LEFT);
- private void addPredicateNumber(String theParamName, List extends IQueryParameterType> theList) {
-
- if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
- addPredicateParamMissing("myParamsNumber", theParamName, ResourceIndexedSearchParamNumber.class);
+ if (theList.get(0).getMissing() != null) {
+ addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join);
return;
}
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceIndexedSearchParamNumber.class);
- cq.select(from.get("myResourcePid").as(Long.class));
-
List codePredicates = new ArrayList();
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
- if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
- continue;
- }
-
if (params instanceof NumberParam) {
NumberParam param = (NumberParam) params;
@@ -381,12 +274,12 @@ public class SearchBuilder {
continue;
}
- final Expression fromObj = from.get("myValue");
+ final Expression fromObj = join.get("myValue");
ParamPrefixEnum prefix = ObjectUtils.defaultIfNull(param.getPrefix(), ParamPrefixEnum.EQUAL);
String invalidMessageName = "invalidNumberPrefix";
String valueAsString = param.getValue().toPlainString();
- Predicate num = createPredicateNumeric(builder, params, prefix, value, fromObj, invalidMessageName, valueAsString);
+ Predicate num = createPredicateNumeric(theResourceName, theParamName, join, myBuilder, params, prefix, value, fromObj, invalidMessageName, valueAsString);
codePredicates.add(num);
} else {
@@ -395,172 +288,108 @@ public class SearchBuilder {
}
- List predicates = new ArrayList();
- predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
- predicates.add(builder.equal(from.get("myParamName"), theParamName));
- predicates.add(builder.or(toArray(codePredicates)));
- createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
- createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
-
- cq.where(builder.and(toArray(predicates)));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(q.getResultList());
+ myPredicates.add(myBuilder.or(toArray(codePredicates)));
}
- private void addPredicateParamMissing(String joinName, String theParamName, Class extends BaseResourceIndexedSearchParam> theParamTable) {
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceTable.class);
- cq.select(from.get("myId").as(Long.class));
+ private void addPredicateParamMissing(String theResourceName, String theParamName, boolean theMissing) {
+ Join paramPresentJoin = myResourceTableRoot.join("mySearchParamPresents", JoinType.LEFT);
+ Join paramJoin = paramPresentJoin.join("mySearchParam", JoinType.LEFT);
- Subquery subQ = cq.subquery(Long.class);
- Root extends BaseResourceIndexedSearchParam> subQfrom = subQ.from(theParamTable);
- subQ.select(subQfrom.get("myResourcePid").as(Long.class));
- Predicate subQname = builder.equal(subQfrom.get("myParamName"), theParamName);
- Predicate subQtype = builder.equal(subQfrom.get("myResourceType"), myResourceName);
- subQ.where(builder.and(subQtype, subQname));
-
- List predicates = new ArrayList();
- predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
- predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
- predicates.add(builder.isNull(from.get("myDeleted")));
- createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
-
- cq.where(builder.and(toArray(predicates)));
-
- ourLog.info("Adding :missing qualifier for parameter '{}'", theParamName);
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(q.getResultList());
+ myPredicates.add(myBuilder.equal(paramJoin.get("myResourceName"), theResourceName));
+ myPredicates.add(myBuilder.equal(paramJoin.get("myParamName"), theParamName));
+ myPredicates.add(myBuilder.equal(paramPresentJoin.get("myPresent"), !theMissing));
}
- private void addPredicateParamMissingResourceLink(String joinName, String theParamName) {
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceTable.class);
- cq.select(from.get("myId").as(Long.class));
+ private void addPredicateParamMissing(String theResourceName, String theParamName, boolean theMissing, Join theJoin) {
- Subquery subQ = cq.subquery(Long.class);
- Root subQfrom = subQ.from(ResourceLink.class);
- subQ.select(subQfrom.get("mySourceResourcePid").as(Long.class));
-
- // subQ.where(builder.equal(subQfrom.get("myParamName"), theParamName));
- Predicate path = createResourceLinkPathPredicate(theParamName, subQfrom);
- subQ.where(path);
-
- List predicates = new ArrayList();
- createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
- predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
- predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
-
- cq.where(builder.and(toArray(predicates)));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- List resultList = q.getResultList();
- doSetPids(new HashSet(resultList));
+ myPredicates.add(myBuilder.equal(theJoin.get("myResourceType"), theResourceName));
+ myPredicates.add(myBuilder.equal(theJoin.get("myParamName"), theParamName));
+ myPredicates.add(myBuilder.equal(theJoin.get("myMissing"), theMissing));
}
- private void addPredicateQuantity(String theParamName, List extends IQueryParameterType> theList) {
- if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
- addPredicateParamMissing("myParamsQuantity", theParamName, ResourceIndexedSearchParamQuantity.class);
+ private void addPredicateQuantity(String theResourceName, String theParamName, List extends IQueryParameterType> theList) {
+ Join join = myResourceTableRoot.join("myParamsQuantity", JoinType.LEFT);
+
+ if (theList.get(0).getMissing() != null) {
+ addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join);
return;
}
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceIndexedSearchParamQuantity.class);
- cq.select(from.get("myResourcePid").as(Long.class));
-
List codePredicates = new ArrayList();
for (IQueryParameterType nextOr : theList) {
- if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
- continue;
- }
- Predicate singleCode = createPredicateQuantity(builder, from, nextOr);
+ Predicate singleCode = createPredicateQuantity(nextOr, theResourceName, theParamName, myBuilder, join);
codePredicates.add(singleCode);
}
- List predicates = new ArrayList();
- predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
- predicates.add(builder.equal(from.get("myParamName"), theParamName));
- predicates.add(builder.or(toArray(codePredicates)));
- createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
- createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
-
- cq.where(builder.and(toArray(predicates)));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(new HashSet(q.getResultList()));
+ myPredicates.add(myBuilder.or(toArray(codePredicates)));
}
- private void addPredicateReference(String theParamName, List extends IQueryParameterType> theList) {
+ /**
+ * Add reference predicate to the current search
+ */
+ private void addPredicateReference(String theResourceName, String theParamName, List extends IQueryParameterType> theList) {
assert theParamName.contains(".") == false;
- if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
- addPredicateParamMissingResourceLink("myResourceLinks", theParamName);
+ if (theList.get(0).getMissing() != null) {
+ addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing());
return;
}
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceLink.class);
- cq.select(from.get("mySourceResourcePid").as(Long.class));
+ Join join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
List codePredicates = new ArrayList();
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
- if (addPredicateMissingFalseIfPresentForResourceLink(builder, theParamName, from, codePredicates, nextOr)) {
- continue;
- }
-
if (params instanceof ReferenceParam) {
ReferenceParam ref = (ReferenceParam) params;
if (isBlank(ref.getChain())) {
IIdType dt = new IdDt(ref.getBaseUrl(), ref.getResourceType(), ref.getIdPart(), null);
-
+
if (dt.hasBaseUrl()) {
if (myCallingDao.getConfig().getTreatBaseUrlsAsLocal().contains(dt.getBaseUrl())) {
dt = dt.toUnqualified();
} else {
ourLog.debug("Searching for resource link with target URL: {}", dt.getValue());
- Predicate eq = builder.equal(from.get("myTargetResourceUrl"), dt.getValue());
+ Predicate eq = myBuilder.equal(join.get("myTargetResourceUrl"), dt.getValue());
codePredicates.add(eq);
continue;
}
}
-
+
List targetPid;
try {
targetPid = myCallingDao.translateForcedIdToPids(dt);
} catch (ResourceNotFoundException e) {
- doSetPids(new ArrayList());
- return;
+ // Use a PID that will never exist
+ targetPid = Collections.singletonList(-1L);
}
for (Long next : targetPid) {
ourLog.debug("Searching for resource link with target PID: {}", next);
- Predicate eq = builder.equal(from.get("myTargetResourcePid"), next);
- codePredicates.add(eq);
+
+ Predicate pathPredicate = createResourceLinkPathPredicate(theResourceName, theParamName, join);
+ Predicate pidPredicate = myBuilder.equal(join.get("myTargetResourcePid"), next);
+ codePredicates.add(myBuilder.and(pathPredicate, pidPredicate));
}
+
} else {
-
+
List> resourceTypes;
String resourceId;
if (!ref.getValue().matches("[a-zA-Z]+\\/.*")) {
-
+
RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceType);
String paramPath = myCallingDao.getSearchParamByName(resourceDef, theParamName).getPath();
if (paramPath.endsWith(".as(Reference)")) {
paramPath = paramPath.substring(0, paramPath.length() - ".as(Reference)".length()) + "Reference";
}
-
+
BaseRuntimeChildDefinition def = myContext.newTerser().getDefinition(myResourceType, paramPath);
if (def instanceof RuntimeChildChoiceDefinition) {
- RuntimeChildChoiceDefinition choiceDef = (RuntimeChildChoiceDefinition)def;
+ RuntimeChildChoiceDefinition choiceDef = (RuntimeChildChoiceDefinition) def;
resourceTypes = choiceDef.getResourceTypes();
} else if (def instanceof RuntimeChildResourceDefinition) {
RuntimeChildResourceDefinition resDef = (RuntimeChildResourceDefinition) def;
@@ -568,9 +397,9 @@ public class SearchBuilder {
} else {
throw new ConfigurationException("Property " + paramPath + " of type " + myResourceName + " is not a resource: " + def.getClass());
}
-
+
resourceId = ref.getValue();
-
+
} else {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(ref.getResourceType());
resourceTypes = new ArrayList>(1);
@@ -590,6 +419,7 @@ public class SearchBuilder {
for (Class extends IBaseResource> nextType : resourceTypes) {
RuntimeResourceDefinition typeDef = myContext.getResourceDefinition(nextType);
+ String subResourceName = typeDef.getName();
IFhirResourceDao> dao = myCallingDao.getDao(nextType);
if (dao == null) {
@@ -634,13 +464,39 @@ public class SearchBuilder {
foundChainMatch = true;
- Set pids = dao.searchForIds(chain, chainValue);
- if (pids.isEmpty()) {
- continue;
- }
+ Subquery subQ = myResourceTableQuery.subquery(Long.class);
+ Root subQfrom = subQ.from(ResourceTable.class);
+ subQ.select(subQfrom.get("myId").as(Long.class));
- Predicate eq = from.get("myTargetResourcePid").in(pids);
- codePredicates.add(eq);
+ List> andOrParams = new ArrayList>();
+ andOrParams.add(Collections.singletonList(chainValue));
+
+ /*
+ * We're doing a chain call, so push the current query root
+ * and predicate list down and put new ones at the top of the
+ * stack and run a subuery
+ */
+ Root stackRoot = myResourceTableRoot;
+ ArrayList stackPredicates = myPredicates;
+ myResourceTableRoot = subQfrom;
+ myPredicates = new ArrayList();
+
+ // Create the subquery predicates
+ myPredicates.add(myBuilder.equal(myResourceTableRoot.get("myResourceType"), subResourceName));
+ myPredicates.add(myBuilder.isNull(myResourceTableRoot.get("myDeleted")));
+ searchForIdsWithAndOr(subResourceName, chain, andOrParams);
+
+ subQ.where(toArray(myPredicates));
+
+ /*
+ * Pop the old query root and predicate list back
+ */
+ myResourceTableRoot = stackRoot;
+ myPredicates = stackPredicates;
+
+ Predicate pathPredicate = createResourceLinkPathPredicate(theResourceName, theParamName, join);
+ Predicate pidPredicate = join.get("myTargetResourcePid").in(subQ);
+ codePredicates.add(myBuilder.and(pathPredicate, pidPredicate));
}
@@ -655,52 +511,68 @@ public class SearchBuilder {
}
- List predicates = new ArrayList();
- predicates.add(createResourceLinkPathPredicate(theParamName, from));
- predicates.add(builder.or(toArray(codePredicates)));
- createPredicateResourceId(builder, cq, predicates, from.get("mySourceResourcePid").as(Long.class));
- createPredicateLastUpdatedForResourceLink(builder, from, predicates);
-
- cq.where(builder.and(toArray(predicates)));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(new HashSet(q.getResultList()));
+ myPredicates.add(myBuilder.or(toArray(codePredicates)));
}
- private void addPredicateString(String theParamName, List extends IQueryParameterType> theList) {
- if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
- addPredicateParamMissing("myParamsString", theParamName, ResourceIndexedSearchParamString.class);
+ private void addPredicateResourceId(List> theValues) {
+ for (List extends IQueryParameterType> nextValue : theValues) {
+ Set orPids = new HashSet();
+ for (IQueryParameterType next : nextValue) {
+ String value = next.getValueAsQueryToken(myContext);
+ if (value != null && value.startsWith("|")) {
+ value = value.substring(1);
+ }
+
+ IdDt valueAsId = new IdDt(value);
+ if (isNotBlank(value)) {
+ if (valueAsId.isIdPartValidLong()) {
+ orPids.add(valueAsId.getIdPartAsLong());
+ } else {
+ try {
+ BaseHasResource entity = myCallingDao.readEntity(valueAsId);
+ if (entity.getDeleted() == null) {
+ orPids.add(entity.getId());
+ }
+ } catch (ResourceNotFoundException e) {
+ /*
+ * This isn't an error, just means no result found
+ * that matches the ID the client provided
+ */
+ }
+ }
+ }
+ }
+
+ if (orPids.size() > 0) {
+ Predicate nextPredicate = myResourceTableRoot.get("myId").as(Long.class).in(orPids);
+ myPredicates.add(nextPredicate);
+ } else {
+ // This will never match
+ Predicate nextPredicate = myBuilder.equal(myResourceTableRoot.get("myId").as(Long.class), -1);
+ myPredicates.add(nextPredicate);
+ }
+
+ }
+ }
+
+ private void addPredicateString(String theResourceName, String theParamName, List extends IQueryParameterType> theList) {
+
+ Join join = myResourceTableRoot.join("myParamsString", JoinType.LEFT);
+
+ if (theList.get(0).getMissing() != null) {
+ addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join);
return;
}
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceIndexedSearchParamString.class);
- cq.select(from.get("myResourcePid").as(Long.class));
-
List codePredicates = new ArrayList();
for (IQueryParameterType nextOr : theList) {
IQueryParameterType theParameter = nextOr;
- if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
- continue;
- }
-
- Predicate singleCode = createPredicateString(theParameter, theParamName, builder, from);
+ Predicate singleCode = createPredicateString(theParameter, theResourceName, theParamName, myBuilder, join);
codePredicates.add(singleCode);
}
- List predicates = new ArrayList();
- predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
- predicates.add(builder.equal(from.get("myParamName"), theParamName));
- predicates.add(builder.or(toArray(codePredicates)));
+ myPredicates.add(myBuilder.or(toArray(codePredicates)));
- createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
- createPredicateLastUpdatedForIndexedSearchParam(builder, from, predicates);
-
- cq.where(builder.and(toArray(predicates)));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- doSetPids(new HashSet(q.getResultList()));
}
private void addPredicateTag(List> theList, String theParamName, DateRangeParam theLastUpdated) {
@@ -715,24 +587,6 @@ public class SearchBuilder {
throw new IllegalArgumentException("Param name: " + theParamName); // shouldn't happen
}
- /*
- * CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery cq =
- * builder.createQuery(Long.class); Root from = cq.from(ResourceTable.class);
- * cq.select(from.get("myId").as(Long.class));
- *
- * Subquery subQ = cq.subquery(Long.class); Root extends BaseResourceIndexedSearchParam> subQfrom =
- * subQ.from(theParamTable); subQ.select(subQfrom.get("myResourcePid").as(Long.class));
- * Predicate subQname = builder.equal(subQfrom.get("myParamName"), theParamName); Predicate subQtype =
- * builder.equal(subQfrom.get("myResourceType"), myResourceName);
- * subQ.where(builder.and(subQtype, subQname));
- *
- * List predicates = new ArrayList();
- * predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
- * predicates.add(builder.equal(from.get("myResourceType"),
- * myResourceName)); predicates.add(builder.isNull(from.get("myDeleted"))); createPredicateResourceId(builder, cq,
- * predicates, from.get("myId").as(Long.class));
- */
-
List> notTags = Lists.newArrayList();
for (List extends IQueryParameterType> nextAndParams : theList) {
for (IQueryParameterType nextOrParams : nextAndParams) {
@@ -791,8 +645,6 @@ public class SearchBuilder {
continue;
}
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
-
boolean paramInverted = false;
List> tokens = Lists.newArrayList();
for (IQueryParameterType nextOrParams : nextAndParams) {
@@ -823,19 +675,11 @@ public class SearchBuilder {
if (paramInverted) {
ourLog.debug("Searching for _tag:not");
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root newFrom = cq.from(ResourceTable.class);
-
- Subquery subQ = cq.subquery(Long.class);
+ Subquery subQ = myResourceTableQuery.subquery(Long.class);
Root subQfrom = subQ.from(ResourceTag.class);
subQ.select(subQfrom.get("myResourceId").as(Long.class));
- cq.select(newFrom.get("myId").as(Long.class));
-
- List andPredicates = new ArrayList();
- andPredicates = new ArrayList();
- andPredicates.add(builder.equal(newFrom.get("myResourceType"), myResourceName));
- andPredicates.add(builder.not(builder.in(newFrom.get("myId")).value(subQ)));
+ myPredicates.add(myBuilder.not(myBuilder.in(myResourceTableRoot.get("myId")).value(subQ)));
Subquery defJoin = subQ.subquery(Long.class);
Root defJoinFrom = defJoin.from(TagDefinition.class);
@@ -843,79 +687,43 @@ public class SearchBuilder {
subQ.where(subQfrom.get("myTagId").as(Long.class).in(defJoin));
- List orPredicates = createPredicateTagList(defJoinFrom, builder, tagType, tokens);
+ List orPredicates = createPredicateTagList(defJoinFrom, myBuilder, tagType, tokens);
defJoin.where(toArray(orPredicates));
- cq.where(toArray(andPredicates));
-
- TypedQuery q = myEntityManager.createQuery(cq);
- Set pids = new HashSet(q.getResultList());
- doSetPids(pids);
continue;
}
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceTag.class);
- List andPredicates = new ArrayList();
- andPredicates.add(builder.equal(from.get("myResourceType"), myResourceName));
- From defJoin = from.join("myTag");
+ Join tagJoin = myResourceTableRoot.join("myTags", JoinType.LEFT);
+ From defJoin = tagJoin.join("myTag");
- Join, ResourceTable> defJoin2 = from.join("myResource");
+ List orPredicates = createPredicateTagList(defJoin, myBuilder, tagType, tokens);
+ myPredicates.add(myBuilder.or(toArray(orPredicates)));
- Predicate notDeletedPredicatePrediate = builder.isNull(defJoin2.get("myDeleted"));
- andPredicates.add(notDeletedPredicatePrediate);
-
- List orPredicates = createPredicateTagList(defJoin, builder, tagType, tokens);
- andPredicates.add(builder.or(toArray(orPredicates)));
-
- if (theLastUpdated != null) {
- andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin2));
- }
-
- createPredicateResourceId(builder, cq, andPredicates, from.get("myResourceId").as(Long.class));
- Predicate masterCodePredicate = builder.and(toArray(andPredicates));
-
- cq.select(from.get("myResourceId").as(Long.class));
- cq.where(masterCodePredicate);
-
- TypedQuery q = myEntityManager.createQuery(cq);
- Set