diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 05a07bc4b9d..ee93f087537 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -48,6 +48,7 @@ import javax.persistence.criteria.Root; import javax.xml.stream.events.Characters; import javax.xml.stream.events.XMLEvent; +import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; @@ -134,11 +135,11 @@ public abstract class BaseHapiFhirDao implements IDao { /** * These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(Map)} */ - protected static final Map> RESOURCE_META_PARAMS; + static final Map> RESOURCE_META_PARAMS; /** * These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(Map)} */ - protected static final Map>> RESOURCE_META_AND_PARAMS; + static final Map>> RESOURCE_META_AND_PARAMS; static { Map> resourceMetaParams = new HashMap>(); @@ -1535,4 +1536,8 @@ public abstract class BaseHapiFhirDao implements IDao { return b.toString(); } + public BaseHasResource readEntity(IIdType theValueId) { + throw new NotImplementedException(""); + } + } 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 d9a613854eb..4fb6ca8fc03 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 @@ -22,20 +22,15 @@ package ca.uhn.fhir.jpa.dao; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.UUID; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; @@ -43,94 +38,45 @@ import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.persistence.TemporalType; -import javax.persistence.Tuple; import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.From; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.persistence.criteria.Subquery; -import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Required; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionTemplate; -import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.context.RuntimeChildResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; -import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; import ca.uhn.fhir.jpa.entity.BaseHasResource; -import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.entity.BaseTag; import ca.uhn.fhir.jpa.entity.ResourceHistoryTable; -import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate; -import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber; -import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity; -import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; -import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken; -import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri; import ca.uhn.fhir.jpa.entity.ResourceLink; import ca.uhn.fhir.jpa.entity.ResourceTable; -import ca.uhn.fhir.jpa.entity.ResourceTag; -import ca.uhn.fhir.jpa.entity.Search; -import ca.uhn.fhir.jpa.entity.SearchResult; import ca.uhn.fhir.jpa.entity.TagDefinition; import ca.uhn.fhir.jpa.entity.TagTypeEnum; import ca.uhn.fhir.jpa.interceptor.IJpaServerInterceptor; import ca.uhn.fhir.jpa.util.StopWatch; -import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.TagList; -import ca.uhn.fhir.model.base.composite.BaseCodingDt; -import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; -import ca.uhn.fhir.model.base.composite.BaseQuantityDt; -import ca.uhn.fhir.model.dstu.resource.BaseResource; -import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.UriDt; -import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; -import ca.uhn.fhir.rest.api.SortOrderEnum; -import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum; -import ca.uhn.fhir.rest.param.CompositeParam; -import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.NumberParam; -import ca.uhn.fhir.rest.param.QuantityParam; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.param.UriParam; -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.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; @@ -143,7 +89,7 @@ import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.ObjectUtil; @Transactional(propagation = Propagation.REQUIRED) -public abstract class BaseHapiFhirResourceDao extends BaseHapiFhirDaoimplements IFhirResourceDao { +public abstract class BaseHapiFhirResourceDao extends BaseHapiFhirDao implements IFhirResourceDao { static final String OO_SEVERITY_ERROR = "error"; static final String OO_SEVERITY_INFO = "information"; @@ -160,9 +106,9 @@ public abstract class BaseHapiFhirResourceDao extends BaseH @Autowired private DaoConfig myDaoConfig; - @Autowired(required=false) + @Autowired(required = false) private ISearchDao mySearchDao; - + private String myResourceName; private Class myResourceType; private String mySecondaryPrimaryKeyParamName; @@ -170,7 +116,6 @@ public abstract class BaseHapiFhirResourceDao extends BaseH @Autowired() private ISearchResultDao mySearchResultDao; - @Override public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) { StopWatch w = new StopWatch(); @@ -227,8 +172,6 @@ public abstract class BaseHapiFhirResourceDao extends BaseH return doCreate(theResource, theIfNoneExist, thePerformIndexing, new Date()); } - - protected IBaseOperationOutcome createErrorOperationOutcome(String theMessage) { return createOperationOutcome(OO_SEVERITY_ERROR, theMessage); } @@ -239,7 +182,6 @@ public abstract class BaseHapiFhirResourceDao extends BaseH protected abstract IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage); - @Override public DaoMethodOutcome delete(IIdType theId) { if (theId == null || !theId.hasIdPart()) { @@ -343,8 +285,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH if (isNotBlank(theResource.getId().getIdPart())) { if (isValidPid(theResource.getId())) { - throw new UnprocessableEntityException( - "This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID"); + throw new UnprocessableEntityException("This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID"); } createForcedIdIfNeeded(entity, theResource.getId()); @@ -372,7 +313,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH ((IJpaServerInterceptor) next).resourceCreated(requestDetails, entity); } } - + DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true); String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart()); @@ -550,240 +491,87 @@ public abstract class BaseHapiFhirResourceDao extends BaseH return retVal; } - private void loadResourcesByPid(Collection theIncludePids, List theResourceListToPopulate, Set theRevIncludedPids, boolean theForHistoryOperation) { - if (theIncludePids.isEmpty()) { - return; - } - - Map position = new HashMap(); - for (Long next : theIncludePids) { - position.put(next, theResourceListToPopulate.size()); - theResourceListToPopulate.add(null); - } - - CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); - CriteriaQuery cq = builder.createQuery(ResourceTable.class); - Root from = cq.from(ResourceTable.class); - cq.where(from.get("myId").in(theIncludePids)); - TypedQuery q = myEntityManager.createQuery(cq); - - for (ResourceTable next : q.getResultList()) { - Class resourceType = getContext().getResourceDefinition(next.getResourceType()).getImplementingClass(); - IResource resource = (IResource) toResource(resourceType, next, theForHistoryOperation); - Integer index = position.get(next.getId()); - if (index == null) { - ourLog.warn("Got back unexpected resource PID {}", next.getId()); - continue; - } - - if (theRevIncludedPids.contains(next.getId())) { - ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(resource, BundleEntrySearchModeEnum.INCLUDE); - } else { - ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(resource, BundleEntrySearchModeEnum.MATCH); - } - - theResourceListToPopulate.set(index, resource); - } - } - -// @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; -// } -// }; -// } - - /** - * THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet()) - * @param theLastUpdated - */ - private HashSet loadReverseIncludes(Collection theMatches, Set theRevIncludes, boolean theReverseMode, EverythingModeEnum theEverythingModeEnum, DateRangeParam theLastUpdated) { - if (theMatches.size() == 0) { - return new HashSet(); - } - if (theRevIncludes == null || theRevIncludes.isEmpty()) { - return new HashSet(); - } - String searchFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid"; - - Collection nextRoundMatches = theMatches; - HashSet allAdded = new HashSet(); - HashSet original = new HashSet(theMatches); - ArrayList includes = new ArrayList(theRevIncludes); - - int roundCounts = 0; - StopWatch w = new StopWatch(); - - boolean addedSomeThisRound; - do { - roundCounts++; - - HashSet pidsToInclude = new HashSet(); - Set nextRoundOmit = new HashSet(); - - for (Iterator iter = includes.iterator(); iter.hasNext();) { - Include nextInclude = iter.next(); - if (nextInclude.isRecurse() == false) { - iter.remove(); - } - - boolean matchAll = "*".equals(nextInclude.getValue()); - if (matchAll) { - String sql; - sql = "SELECT r FROM ResourceLink r WHERE r." + searchFieldName + " IN (:target_pids)"; - TypedQuery q = myEntityManager.createQuery(sql, ResourceLink.class); - q.setParameter("target_pids", nextRoundMatches); - List results = q.getResultList(); - for (ResourceLink resourceLink : results) { - if (theReverseMode) { -// if (theEverythingModeEnum.isEncounter()) { -// if (resourceLink.getSourcePath().equals("Encounter.subject") || resourceLink.getSourcePath().equals("Encounter.patient")) { -// nextRoundOmit.add(resourceLink.getSourceResourcePid()); -// } -// } - pidsToInclude.add(resourceLink.getSourceResourcePid()); - } else { - pidsToInclude.add(resourceLink.getTargetResourcePid()); - } - } - } else { - - List paths; - if (getContext().getVersion().getVersion() == FhirVersionEnum.DSTU1) { - paths = Collections.singletonList(nextInclude.getValue()); - } else { - int colonIdx = nextInclude.getValue().indexOf(':'); - if (colonIdx < 2) { - continue; - } - String resType = nextInclude.getValue().substring(0, colonIdx); - RuntimeResourceDefinition def = getContext().getResourceDefinition(resType); - if (def == null) { - ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue()); - continue; - } - - String paramName = nextInclude.getValue().substring(colonIdx + 1); - RuntimeSearchParam param = def.getSearchParam(paramName); - if (param == null) { - ourLog.warn("Unknown param name in include/revinclude=" + nextInclude.getValue()); - continue; - } - - paths = param.getPathsSplit(); - } - - for (String nextPath : paths) { - String sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids)"; - TypedQuery q = myEntityManager.createQuery(sql, ResourceLink.class); - q.setParameter("src_path", nextPath); - q.setParameter("target_pids", nextRoundMatches); - List results = q.getResultList(); - for (ResourceLink resourceLink : results) { - if (theReverseMode) { - pidsToInclude.add(resourceLink.getSourceResourcePid()); - } else { - pidsToInclude.add(resourceLink.getTargetResourcePid()); - } - } - } - } - } - - if (theLastUpdated != null && (theLastUpdated.getLowerBoundAsInstant() != null || theLastUpdated.getUpperBoundAsInstant() != null)) { - pidsToInclude = new HashSet(filterResourceIdsByLastUpdated(pidsToInclude, theLastUpdated)); - } - for (Long next : pidsToInclude) { - if (original.contains(next) == false && allAdded.contains(next) == false) { - theMatches.add(next); - } - } - - pidsToInclude.removeAll(nextRoundOmit); - - addedSomeThisRound = allAdded.addAll(pidsToInclude); - 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() }); - - return allAdded; - } + // @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 MetaDt metaAddOperation(IIdType theResourceId, MetaDt theMetaAdd) { @@ -993,8 +781,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH if (entity == null) { if (theId.hasVersionIdPart()) { - TypedQuery q = myEntityManager - .createQuery("SELECT t from ResourceHistoryTable t WHERE t.myResourceId = :RID AND t.myResourceType = :RTYP AND t.myResourceVersion = :RVER", ResourceHistoryTable.class); + TypedQuery q = myEntityManager.createQuery("SELECT t from ResourceHistoryTable t WHERE t.myResourceId = :RID AND t.myResourceType = :RTYP AND t.myResourceVersion = :RVER", ResourceHistoryTable.class); q.setParameter("RID", pid); q.setParameter("RTYP", myResourceName); q.setParameter("RVER", theId.getVersionIdPartAsLong()); @@ -1070,202 +857,9 @@ public abstract class BaseHapiFhirResourceDao extends BaseH ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName()); notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails); - StopWatch w = new StopWatch(); - final InstantDt now = InstantDt.withCurrentTime(); - - DateRangeParam lu = theParams.getLastUpdated(); - if (lu != null && lu.isEmpty()) { - lu = null; - } - - Collection loadPids; - if (theParams.getEverythingMode() != null) { - - CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); - CriteriaQuery cq = builder.createTupleQuery(); - Root from = cq.from(ResourceTable.class); - List predicates = new ArrayList(); - if (theParams.get(BaseResource.SP_RES_ID) != null) { - StringParam idParm = (StringParam) theParams.get(BaseResource.SP_RES_ID).get(0).get(0); - predicates.add(builder.equal(from.get("myId"), idParm.getValue())); - } - predicates.add(builder.equal(from.get("myResourceType"), myResourceName)); - predicates.add(builder.isNull(from.get("myDeleted"))); - cq.where(builder.and(SearchBuilder.toArray(predicates))); - - Join join = from.join("myIncomingResourceLinks", JoinType.LEFT); - cq.multiselect(from.get("myId").as(Long.class), join.get("mySourceResourcePid").as(Long.class)); - - TypedQuery query = myEntityManager.createQuery(cq); - loadPids = new HashSet(); - for (Tuple next : query.getResultList()) { - loadPids.add(next.get(0, Long.class)); - Long nextLong = next.get(1, Long.class); - if(nextLong != null) { - loadPids.add(nextLong); - } - } - - } else if (theParams.isEmpty()) { - - loadPids = new HashSet(); - TypedQuery query = createSearchAllByTypeQuery(lu); - lu = null; - for (Tuple next : query.getResultList()) { - loadPids.add(next.get(0, Long.class)); - } - if (loadPids.isEmpty()) { - return new SimpleBundleProvider(); - } - - } else { - - List searchResultPids; - if (mySearchDao == null) { - if (theParams.containsKey(Constants.PARAM_TEXT)) { - throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_TEXT); - } else if (theParams.containsKey(Constants.PARAM_CONTENT)) { - throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_CONTENT); - } - searchResultPids = null; - } else { - searchResultPids = mySearchDao.search(getResourceName(), theParams); - } - if (theParams.isEmpty()) { - loadPids = searchResultPids; - } else { - loadPids = searchForIdsWithAndOr(theParams, searchResultPids, lu); - } - if (loadPids.isEmpty()) { - return new SimpleBundleProvider(); - } - - } - - // // Load _include and _revinclude before filter and sort in everything mode - // if (theParams.getEverythingMode() != null) { - // if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) { - // loadPids.addAll(loadReverseIncludes(loadPids, theParams.getRevIncludes(), true, theParams.getEverythingMode())); - // loadPids.addAll(loadReverseIncludes(loadPids, theParams.getIncludes(), false, theParams.getEverythingMode())); - // } - // } - - // Handle _lastUpdated - if (lu != null) { - List resultList = filterResourceIdsByLastUpdated(loadPids, lu); - loadPids.clear(); - for (Long next : resultList) { - loadPids.add(next); - } - - if (loadPids.isEmpty()) { - return new SimpleBundleProvider(); - } - } - - // Handle sorting if any was provided - final List pids = processSort(theParams, loadPids); - - // Load _revinclude resources - final Set revIncludedPids; - if (theParams.getEverythingMode() == null) { - if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) { - revIncludedPids = loadReverseIncludes(pids, theParams.getRevIncludes(), true, null, lu); - } else { - revIncludedPids = new HashSet(); - } - } else { - revIncludedPids = new HashSet(); - } - - ourLog.debug("Search returned PIDs: {}", pids); - - final int totalCount = pids.size(); - - IBundleProvider retVal = new IBundleProvider() { - @Override - public InstantDt getPublished() { - return now; - } - - @Override - public List getResources(final int theFromIndex, final int theToIndex) { - TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager); - return template.execute(new TransactionCallback>() { - @Override - public List doInTransaction(TransactionStatus theStatus) { - List pidsSubList = pids.subList(theFromIndex, theToIndex); - - // Load includes - pidsSubList = new ArrayList(pidsSubList); - revIncludedPids.addAll(loadReverseIncludes(pidsSubList, theParams.getIncludes(), false, null, theParams.getLastUpdated())); - - // Execute the query and make sure we return distinct results - List resources = new ArrayList(); - loadResourcesByPid(pidsSubList, resources, revIncludedPids, false); - - return resources; - } - - }); - } - - @Override - public Integer preferredPageSize() { - return theParams.getCount(); - } - - @Override - public int size() { - return totalCount; - } - }; - - ourLog.info(" {} on {} in {}ms", new Object[] { myResourceName, theParams, w.getMillisAndRestart() }); - - return retVal; - } - - private List filterResourceIdsByLastUpdated(Collection thePids, final DateRangeParam theLastUpdated) { - 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 lastUpdatedPredicates = createLastUpdatedPredicates(theLastUpdated, builder, from); - lastUpdatedPredicates.add(0, from.get("myId").in(thePids)); - - cq.where(toArray(lastUpdatedPredicates)); - TypedQuery query = myEntityManager.createQuery(cq); - List resultList = query.getResultList(); - return resultList; - } - - private List createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From from) { - List lastUpdatedPredicates = new ArrayList(); - if (theLastUpdated != null) { - if (theLastUpdated.getLowerBoundAsInstant() != null) { - Predicate predicateLower = builder.greaterThanOrEqualTo(from. get("myUpdated"), theLastUpdated.getLowerBoundAsInstant()); - lastUpdatedPredicates.add(predicateLower); - } - if (theLastUpdated.getUpperBoundAsInstant() != null) { - Predicate predicateUpper = builder.lessThanOrEqualTo(from. get("myUpdated"), theLastUpdated.getUpperBoundAsInstant()); - lastUpdatedPredicates.add(predicateUpper); - } - } - return lastUpdatedPredicates; - } - - - - private List toList(Collection theLoadPids) { - final List pids; - if (theLoadPids instanceof List) { - pids = (List) theLoadPids; - } else { - pids = new ArrayList(theLoadPids); - } - return pids; + SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this); + builder.setType(getResourceType(), getResourceName()); + return builder.search(theParams); } @Override @@ -1289,145 +883,9 @@ public abstract class BaseHapiFhirResourceDao extends BaseH @Override public Set searchForIdsWithAndOr(SearchParameterMap theParams, Collection theInitialPids, DateRangeParam theLastUpdated) { - SearchParameterMap params = theParams; - if (params == null) { - params = new SearchParameterMap(); - } - - RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(myResourceType); - - Set pids = new HashSet(); - if (theInitialPids != null) { - pids.addAll(theInitialPids); - } - - for (Entry>> nextParamEntry : params.entrySet()) { - String nextParamName = nextParamEntry.getKey(); - if (nextParamName.equals(BaseResource.SP_RES_ID)) { - - if (nextParamEntry.getValue().isEmpty()) { - continue; - } else { - for (List nextValue : nextParamEntry.getValue()) { - Set joinPids = new HashSet(); - if (nextValue == null || nextValue.size() == 0) { - continue; - } else { - for (IQueryParameterType next : nextValue) { - String value = next.getValueAsQueryToken(); - IIdType valueId = new IdDt(value); - - try { - BaseHasResource entity = readEntity(valueId); - if (entity.getDeleted() != null) { - continue; - } - joinPids.add(entity.getId()); - } catch (ResourceNotFoundException e) { - // This isn't an error, just means no result found - } - } - if (joinPids.isEmpty()) { - return new HashSet(); - } - } - - pids = addPredicateId(pids, joinPids, theLastUpdated); - if (pids.isEmpty()) { - return new HashSet(); - } - - if (pids.isEmpty()) { - pids.addAll(joinPids); - } else { - pids.retainAll(joinPids); - } - } - } - - } else if (nextParamName.equals(BaseResource.SP_RES_LANGUAGE)) { - - pids = addPredicateLanguage(pids, nextParamEntry.getValue(), theLastUpdated); - - } else if (nextParamName.equals(Constants.PARAM_TAG) || nextParamName.equals(Constants.PARAM_PROFILE) || nextParamName.equals(Constants.PARAM_SECURITY)) { - - pids = addPredicateTag(pids, nextParamEntry.getValue(), nextParamName, theLastUpdated); - - } else { - - RuntimeSearchParam nextParamDef = resourceDef.getSearchParam(nextParamName); - if (nextParamDef != null) { - switch (nextParamDef.getParamType()) { - case DATE: - for (List nextAnd : nextParamEntry.getValue()) { - pids = addPredicateDate(nextParamName, pids, nextAnd); - if (pids.isEmpty()) { - return new HashSet(); - } - } - break; - case QUANTITY: - for (List nextAnd : nextParamEntry.getValue()) { - pids = addPredicateQuantity(nextParamName, pids, nextAnd); - if (pids.isEmpty()) { - return new HashSet(); - } - } - break; - case REFERENCE: - for (List nextAnd : nextParamEntry.getValue()) { - pids = addPredicateReference(nextParamName, pids, nextAnd); - if (pids.isEmpty()) { - return new HashSet(); - } - } - break; - case STRING: - for (List nextAnd : nextParamEntry.getValue()) { - pids = addPredicateString(nextParamName, pids, nextAnd); - if (pids.isEmpty()) { - return new HashSet(); - } - } - break; - case TOKEN: - for (List nextAnd : nextParamEntry.getValue()) { - pids = addPredicateToken(nextParamName, pids, nextAnd); - if (pids.isEmpty()) { - return new HashSet(); - } - } - break; - case NUMBER: - for (List nextAnd : nextParamEntry.getValue()) { - pids = addPredicateNumber(nextParamName, pids, nextAnd); - if (pids.isEmpty()) { - return new HashSet(); - } - } - break; - case COMPOSITE: - for (List nextAnd : nextParamEntry.getValue()) { - pids = addPredicateComposite(nextParamDef, pids, nextAnd); - if (pids.isEmpty()) { - return new HashSet(); - } - } - break; - case URI: - for (List nextAnd : nextParamEntry.getValue()) { - pids = addPredicateUri(nextParamName, pids, nextAnd); - if (pids.isEmpty()) { - return new HashSet(); - } - } - break; - } - } - } - } - - return pids; + SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this); + builder.setType(getResourceType(), getResourceName()); + return builder.searchForIdsWithAndOr(theParams, theInitialPids, theLastUpdated); } @SuppressWarnings("unchecked") @@ -1437,7 +895,8 @@ public abstract class BaseHapiFhirResourceDao extends BaseH } /** - * If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to share the same value. + * If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to + * share the same value. */ public void setSecondaryPrimaryKeyParamName(String theSecondaryPrimaryKeyParamName) { mySecondaryPrimaryKeyParamName = theSecondaryPrimaryKeyParamName; @@ -1460,48 +919,8 @@ public abstract class BaseHapiFhirResourceDao extends BaseH return retVal; } - private IQueryParameterType toParameterType(RuntimeSearchParam theParam) { - IQueryParameterType qp; - switch (theParam.getParamType()) { - case DATE: - qp = new DateParam(); - break; - case NUMBER: - qp = new NumberParam(); - break; - case QUANTITY: - qp = new QuantityParam(); - break; - case STRING: - qp = new StringParam(); - break; - case TOKEN: - qp = new TokenParam(); - break; - case COMPOSITE: - List compositeOf = theParam.getCompositeOf(); - if (compositeOf.size() != 2) { - throw new InternalErrorException("Parameter " + theParam.getName() + " has " + compositeOf.size() + " composite parts. Don't know how handlt this."); - } - IQueryParameterType leftParam = toParameterType(compositeOf.get(0)); - IQueryParameterType rightParam = toParameterType(compositeOf.get(1)); - qp = new CompositeParam(leftParam, rightParam); - break; - case REFERENCE: - qp = new ReferenceParam(); - break; - default: - throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType()); - } - return qp; - } - private IQueryParameterType toParameterType(RuntimeSearchParam theParam, String theQualifier, String theValueAsQueryToken) { - IQueryParameterType qp = toParameterType(theParam); - qp.setValueAsQueryToken(theQualifier, theValueAsQueryToken); // aaaa - return qp; - } private ArrayList toTagList(MetaDt theMeta) { ArrayList retVal = new ArrayList(); @@ -1570,8 +989,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH } if (resourceId.hasResourceType() && !resourceId.getResourceType().equals(getResourceName())) { - throw new UnprocessableEntityException( - "Invalid resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] of type[" + entity.getResourceType() + "] - Does not match expected [" + getResourceName() + "]"); + throw new UnprocessableEntityException("Invalid resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] of type[" + entity.getResourceType() + "] - Does not match expected [" + getResourceName() + "]"); } // Notify interceptors @@ -1623,14 +1041,12 @@ public abstract class BaseHapiFhirResourceDao extends BaseH String sourceId = link.getSourceResource().getIdDt().toUnqualifiedVersionless().getValue(); String sourcePath = link.getSourcePath(); - throw new ResourceVersionConflictException( - "Unable to delete " + targetId + " because at least one resource has a reference to this resource. First reference found was resource " + sourceId + " in path " + sourcePath); + throw new ResourceVersionConflictException("Unable to delete " + targetId + " because at least one resource has a reference to this resource. First reference found was resource " + sourceId + " in path " + sourcePath); } private void validateResourceType(BaseHasResource entity) { if (!myResourceName.equals(entity.getResourceType())) { - throw new ResourceNotFoundException( - "Resource with ID " + entity.getIdDt().getIdPart() + " exists but it is not of type " + myResourceName + ", found resource of type " + entity.getResourceType()); + throw new ResourceNotFoundException("Resource with ID " + entity.getIdDt().getIdPart() + " exists but it is not of type " + myResourceName + ", found resource of type " + entity.getResourceType()); } } 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 e46f66e3120..37138dbe796 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 @@ -40,4 +40,5 @@ public interface IDao { } }; + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java index 9e5c05a783a..8a6487e73b0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java @@ -45,8 +45,6 @@ public interface IFhirSystemDao extends IDao { IBundleProvider history(Date theDate); - int performReindexingPass(Integer theCount); - /** * Marks all indexes as needing fresh indexing * @@ -59,6 +57,8 @@ public interface IFhirSystemDao extends IDao { */ MetaDt metaGetOperation(); + int performReindexingPass(Integer theCount); + T transaction(RequestDetails theRequestDetails, T theResources); } 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 dabbb8718a3..00aa5797c74 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 @@ -8,9 +8,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import javax.persistence.EntityManager; @@ -20,6 +24,7 @@ import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; import javax.persistence.criteria.From; +import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Order; import javax.persistence.criteria.Path; @@ -30,13 +35,21 @@ import javax.persistence.criteria.Subquery; import org.apache.commons.lang3.StringUtils; 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 ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeChildResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; +import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum; +import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; +import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber; @@ -49,14 +62,20 @@ import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTag; import ca.uhn.fhir.jpa.entity.TagDefinition; import ca.uhn.fhir.jpa.entity.TagTypeEnum; +import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseQuantityDt; import ca.uhn.fhir.model.dstu.resource.BaseResource; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.model.primitive.InstantDt; +import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum; @@ -70,18 +89,31 @@ import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.UriParam; 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; public class SearchBuilder { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class); private EntityManager myEntityManager; private FhirContext myContext; + private Class myResourceType; + private String myResourceName; + private PlatformTransactionManager myPlatformTransactionManager; + private ISearchDao mySearchDao; + private ISearchResultDao mySearchResultDao; + private BaseHapiFhirDao myCallingDao; - public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager) { + public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, ISearchDao theSearchDao, ISearchResultDao theSearchResultDao, BaseHapiFhirDao theDao) { myContext = theFhirContext; myEntityManager = theEntityManager; + myPlatformTransactionManager = thePlatformTransactionManager; + mySearchDao = theSearchDao; + mySearchResultDao = theSearchResultDao; + myCallingDao = theDao; } private Predicate createCompositeParamPart(CriteriaBuilder builder, Root from, RuntimeSearchParam left, IQueryParameterType leftValue) { @@ -205,6 +237,149 @@ public class SearchBuilder { } return singleCode; } + + public Set searchForIdsWithAndOr(SearchParameterMap theParams, Collection theInitialPids, DateRangeParam theLastUpdated) { + SearchParameterMap params = theParams; + if (params == null) { + params = new SearchParameterMap(); + } + + RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceType); + + Set pids = new HashSet(); + if (theInitialPids != null) { + pids.addAll(theInitialPids); + } + + for (Entry>> nextParamEntry : params.entrySet()) { + String nextParamName = nextParamEntry.getKey(); + if (nextParamName.equals(BaseResource.SP_RES_ID)) { + + if (nextParamEntry.getValue().isEmpty()) { + continue; + } else { + for (List nextValue : nextParamEntry.getValue()) { + Set joinPids = new HashSet(); + if (nextValue == null || nextValue.size() == 0) { + continue; + } else { + for (IQueryParameterType next : nextValue) { + String value = next.getValueAsQueryToken(); + IIdType valueId = new IdDt(value); + + try { + BaseHasResource entity = myCallingDao.readEntity(valueId); + if (entity.getDeleted() != null) { + continue; + } + joinPids.add(entity.getId()); + } catch (ResourceNotFoundException e) { + // This isn't an error, just means no result found + } + } + if (joinPids.isEmpty()) { + return new HashSet(); + } + } + + pids = addPredicateId(pids, joinPids, theLastUpdated); + if (pids.isEmpty()) { + return new HashSet(); + } + + if (pids.isEmpty()) { + pids.addAll(joinPids); + } else { + pids.retainAll(joinPids); + } + } + } + + } else if (nextParamName.equals(BaseResource.SP_RES_LANGUAGE)) { + + pids = addPredicateLanguage(pids, nextParamEntry.getValue(), theLastUpdated); + + } else if (nextParamName.equals(Constants.PARAM_TAG) || nextParamName.equals(Constants.PARAM_PROFILE) || nextParamName.equals(Constants.PARAM_SECURITY)) { + + pids = addPredicateTag(pids, nextParamEntry.getValue(), nextParamName, theLastUpdated); + + } else { + + RuntimeSearchParam nextParamDef = resourceDef.getSearchParam(nextParamName); + if (nextParamDef != null) { + switch (nextParamDef.getParamType()) { + case DATE: + for (List nextAnd : nextParamEntry.getValue()) { + pids = addPredicateDate(nextParamName, pids, nextAnd); + if (pids.isEmpty()) { + return new HashSet(); + } + } + break; + case QUANTITY: + for (List nextAnd : nextParamEntry.getValue()) { + pids = addPredicateQuantity(nextParamName, pids, nextAnd); + if (pids.isEmpty()) { + return new HashSet(); + } + } + break; + case REFERENCE: + for (List nextAnd : nextParamEntry.getValue()) { + pids = addPredicateReference(nextParamName, pids, nextAnd); + if (pids.isEmpty()) { + return new HashSet(); + } + } + break; + case STRING: + for (List nextAnd : nextParamEntry.getValue()) { + pids = addPredicateString(nextParamName, pids, nextAnd); + if (pids.isEmpty()) { + return new HashSet(); + } + } + break; + case TOKEN: + for (List nextAnd : nextParamEntry.getValue()) { + pids = addPredicateToken(nextParamName, pids, nextAnd); + if (pids.isEmpty()) { + return new HashSet(); + } + } + break; + case NUMBER: + for (List nextAnd : nextParamEntry.getValue()) { + pids = addPredicateNumber(nextParamName, pids, nextAnd); + if (pids.isEmpty()) { + return new HashSet(); + } + } + break; + case COMPOSITE: + for (List nextAnd : nextParamEntry.getValue()) { + pids = addPredicateComposite(nextParamDef, pids, nextAnd); + if (pids.isEmpty()) { + return new HashSet(); + } + } + break; + case URI: + for (List nextAnd : nextParamEntry.getValue()) { + pids = addPredicateUri(nextParamName, pids, nextAnd); + if (pids.isEmpty()) { + return new HashSet(); + } + } + break; + } + } + } + } + + return pids; + } + private TypedQuery createSearchAllByTypeQuery(DateRangeParam theLastUpdated) { CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); @@ -225,6 +400,200 @@ public class SearchBuilder { return query; } + private List createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From from) { + List lastUpdatedPredicates = new ArrayList(); + if (theLastUpdated != null) { + if (theLastUpdated.getLowerBoundAsInstant() != null) { + Predicate predicateLower = builder.greaterThanOrEqualTo(from. get("myUpdated"), theLastUpdated.getLowerBoundAsInstant()); + lastUpdatedPredicates.add(predicateLower); + } + if (theLastUpdated.getUpperBoundAsInstant() != null) { + Predicate predicateUpper = builder.lessThanOrEqualTo(from. get("myUpdated"), theLastUpdated.getUpperBoundAsInstant()); + lastUpdatedPredicates.add(predicateUpper); + } + } + return lastUpdatedPredicates; + } + private List filterResourceIdsByLastUpdated(Collection thePids, final DateRangeParam theLastUpdated) { + 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 lastUpdatedPredicates = createLastUpdatedPredicates(theLastUpdated, builder, from); + lastUpdatedPredicates.add(0, from.get("myId").in(thePids)); + + cq.where(SearchBuilder.toArray(lastUpdatedPredicates)); + TypedQuery query = myEntityManager.createQuery(cq); + List resultList = query.getResultList(); + return resultList; + } + + + private void loadResourcesByPid(Collection theIncludePids, List theResourceListToPopulate, Set theRevIncludedPids, boolean theForHistoryOperation) { + if (theIncludePids.isEmpty()) { + return; + } + + Map position = new HashMap(); + for (Long next : theIncludePids) { + position.put(next, theResourceListToPopulate.size()); + theResourceListToPopulate.add(null); + } + + CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery cq = builder.createQuery(ResourceTable.class); + Root from = cq.from(ResourceTable.class); + cq.where(from.get("myId").in(theIncludePids)); + TypedQuery q = myEntityManager.createQuery(cq); + + for (ResourceTable next : q.getResultList()) { + Class resourceType = myContext.getResourceDefinition(next.getResourceType()).getImplementingClass(); + IResource resource = (IResource) myCallingDao.toResource(resourceType, next, theForHistoryOperation); + Integer index = position.get(next.getId()); + if (index == null) { + ourLog.warn("Got back unexpected resource PID {}", next.getId()); + continue; + } + + if (theRevIncludedPids.contains(next.getId())) { + ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(resource, BundleEntrySearchModeEnum.INCLUDE); + } else { + ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(resource, BundleEntrySearchModeEnum.MATCH); + } + + theResourceListToPopulate.set(index, resource); + } + } + + /** + * THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet()) + * @param theLastUpdated + */ + private HashSet loadReverseIncludes(Collection theMatches, Set theRevIncludes, boolean theReverseMode, EverythingModeEnum theEverythingModeEnum, DateRangeParam theLastUpdated) { + if (theMatches.size() == 0) { + return new HashSet(); + } + if (theRevIncludes == null || theRevIncludes.isEmpty()) { + return new HashSet(); + } + String searchFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid"; + + Collection nextRoundMatches = theMatches; + HashSet allAdded = new HashSet(); + HashSet original = new HashSet(theMatches); + ArrayList includes = new ArrayList(theRevIncludes); + + int roundCounts = 0; + StopWatch w = new StopWatch(); + + boolean addedSomeThisRound; + do { + roundCounts++; + + HashSet pidsToInclude = new HashSet(); + Set nextRoundOmit = new HashSet(); + + for (Iterator iter = includes.iterator(); iter.hasNext();) { + Include nextInclude = iter.next(); + if (nextInclude.isRecurse() == false) { + iter.remove(); + } + + boolean matchAll = "*".equals(nextInclude.getValue()); + if (matchAll) { + String sql; + sql = "SELECT r FROM ResourceLink r WHERE r." + searchFieldName + " IN (:target_pids)"; + TypedQuery q = myEntityManager.createQuery(sql, ResourceLink.class); + q.setParameter("target_pids", nextRoundMatches); + List results = q.getResultList(); + for (ResourceLink resourceLink : results) { + if (theReverseMode) { +// if (theEverythingModeEnum.isEncounter()) { +// if (resourceLink.getSourcePath().equals("Encounter.subject") || resourceLink.getSourcePath().equals("Encounter.patient")) { +// nextRoundOmit.add(resourceLink.getSourceResourcePid()); +// } +// } + pidsToInclude.add(resourceLink.getSourceResourcePid()); + } else { + pidsToInclude.add(resourceLink.getTargetResourcePid()); + } + } + } else { + + List paths; + if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { + paths = Collections.singletonList(nextInclude.getValue()); + } else { + int colonIdx = nextInclude.getValue().indexOf(':'); + if (colonIdx < 2) { + continue; + } + String resType = nextInclude.getValue().substring(0, colonIdx); + RuntimeResourceDefinition def = myContext.getResourceDefinition(resType); + if (def == null) { + ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue()); + continue; + } + + String paramName = nextInclude.getValue().substring(colonIdx + 1); + RuntimeSearchParam param = def.getSearchParam(paramName); + if (param == null) { + ourLog.warn("Unknown param name in include/revinclude=" + nextInclude.getValue()); + continue; + } + + paths = param.getPathsSplit(); + } + + for (String nextPath : paths) { + String sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids)"; + TypedQuery q = myEntityManager.createQuery(sql, ResourceLink.class); + q.setParameter("src_path", nextPath); + q.setParameter("target_pids", nextRoundMatches); + List results = q.getResultList(); + for (ResourceLink resourceLink : results) { + if (theReverseMode) { + pidsToInclude.add(resourceLink.getSourceResourcePid()); + } else { + pidsToInclude.add(resourceLink.getTargetResourcePid()); + } + } + } + } + } + + if (theLastUpdated != null && (theLastUpdated.getLowerBoundAsInstant() != null || theLastUpdated.getUpperBoundAsInstant() != null)) { + pidsToInclude = new HashSet(filterResourceIdsByLastUpdated(pidsToInclude, theLastUpdated)); + } + for (Long next : pidsToInclude) { + if (original.contains(next) == false && allAdded.contains(next) == false) { + theMatches.add(next); + } + } + + pidsToInclude.removeAll(nextRoundOmit); + + addedSomeThisRound = allAdded.addAll(pidsToInclude); + 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() }); + + return allAdded; + } + + + private List toList(Collection theLoadPids) { + final List pids; + if (theLoadPids instanceof List) { + pids = (List) theLoadPids; + } else { + pids = new ArrayList(theLoadPids); + } + return pids; + } + private List processSort(final SearchParameterMap theParams, Collection theLoadPids) { final List pids; // Set loadPids = theLoadPids; @@ -316,7 +685,7 @@ public class SearchBuilder { } private Predicate createResourceLinkPathPredicate(String theParamName, CriteriaBuilder builder, Root from) { - RuntimeSearchParam param = myContext.getResourceDefinition(getResourceType()).getSearchParam(theParamName); + RuntimeSearchParam param = myContext.getResourceDefinition(myResourceType).getSearchParam(theParamName); List path = param.getPathsSplit(); Predicate type = from.get("mySourcePath").in(path); return type; @@ -682,8 +1051,6 @@ public class SearchBuilder { } private Set addPredicateParamMissing(Set thePids, String joinName, String theParamName, Class theParamTable) { - String resourceType = myContext.getResourceDefinition(getResourceType()).getName(); - CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery cq = builder.createQuery(Long.class); Root from = cq.from(ResourceTable.class); @@ -693,11 +1060,11 @@ public class SearchBuilder { Root 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"), resourceType); + Predicate subQtype = builder.equal(subQfrom.get("myResourceType"), myResourceName); subQ.where(builder.and(subQtype, subQname)); Predicate joinPredicate = builder.not(builder.in(from.get("myId")).value(subQ)); - Predicate typePredicate = builder.equal(from.get("myResourceType"), resourceType); + Predicate typePredicate = builder.equal(from.get("myResourceType"), myResourceName); Predicate notDeletedPredicate = builder.isNull(from.get("myDeleted")); if (thePids.size() > 0) { @@ -895,7 +1262,7 @@ public class SearchBuilder { IIdType dt = new IdDt(resourceId); resourceId = dt.getIdPart(); } - Long targetPid = translateForcedIdToPid(new IdDt(resourceId)); + Long targetPid = myCallingDao.translateForcedIdToPid(new IdDt(resourceId)); ourLog.debug("Searching for resource link with target PID: {}", targetPid); Predicate eq = builder.equal(from.get("myTargetResourcePid"), targetPid); @@ -935,7 +1302,7 @@ public class SearchBuilder { for (Class nextType : resourceTypes) { RuntimeResourceDefinition typeDef = myContext.getResourceDefinition(nextType); - IFhirResourceDao dao = getDao(nextType); + IFhirResourceDao dao = myCallingDao.getDao(nextType); if (dao == null) { ourLog.debug("Don't have a DAO for type {}", nextType.getSimpleName()); continue; @@ -948,7 +1315,7 @@ public class SearchBuilder { chain = chain.substring(0, qualifierIndex); } - boolean isMeta = RESOURCE_META_PARAMS.containsKey(chain); + boolean isMeta = BaseHapiFhirDao.RESOURCE_META_PARAMS.containsKey(chain); RuntimeSearchParam param = null; if (!isMeta) { param = typeDef.getSearchParam(chain); @@ -969,7 +1336,7 @@ public class SearchBuilder { chainValue.setValueAsQueryToken(qualifier, resourceId); ((ReferenceParam) chainValue).setChain(remainingChain); } else if (isMeta) { - IQueryParameterType type = newInstanceType(chain); + IQueryParameterType type = BaseHapiFhirDao.newInstanceType(chain); type.setValueAsQueryToken(qualifier, resourceId); chainValue = type; } else { @@ -1012,7 +1379,52 @@ public class SearchBuilder { TypedQuery q = myEntityManager.createQuery(cq); return new HashSet(q.getResultList()); } + + private IQueryParameterType toParameterType(RuntimeSearchParam theParam, String theQualifier, String theValueAsQueryToken) { + IQueryParameterType qp = toParameterType(theParam); + qp.setValueAsQueryToken(theQualifier, theValueAsQueryToken); // aaaa + return qp; + } + + + private IQueryParameterType toParameterType(RuntimeSearchParam theParam) { + IQueryParameterType qp; + switch (theParam.getParamType()) { + case DATE: + qp = new DateParam(); + break; + case NUMBER: + qp = new NumberParam(); + break; + case QUANTITY: + qp = new QuantityParam(); + break; + case STRING: + qp = new StringParam(); + break; + case TOKEN: + qp = new TokenParam(); + break; + case COMPOSITE: + List compositeOf = theParam.getCompositeOf(); + if (compositeOf.size() != 2) { + throw new InternalErrorException("Parameter " + theParam.getName() + " has " + compositeOf.size() + " composite parts. Don't know how handlt this."); + } + IQueryParameterType leftParam = toParameterType(compositeOf.get(0)); + IQueryParameterType rightParam = toParameterType(compositeOf.get(1)); + qp = new CompositeParam(leftParam, rightParam); + break; + case REFERENCE: + qp = new ReferenceParam(); + break; + default: + throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType()); + } + return qp; + } + + private Set addPredicateString(String theParamName, Set thePids, List theList) { if (theList == null || theList.isEmpty()) { return thePids; @@ -1256,5 +1668,166 @@ public class SearchBuilder { return new HashSet(q.getResultList()); } + public void setType(Class theResourceType, String theResourceName) { + myResourceType = theResourceType; + myResourceName = theResourceName; + } + + public IBundleProvider search(final SearchParameterMap theParams) { + StopWatch w = new StopWatch(); + final InstantDt now = InstantDt.withCurrentTime(); + + DateRangeParam lu = theParams.getLastUpdated(); + if (lu != null && lu.isEmpty()) { + lu = null; + } + + Collection loadPids; + if (theParams.getEverythingMode() != null) { + + CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery cq = builder.createTupleQuery(); + Root from = cq.from(ResourceTable.class); + List predicates = new ArrayList(); + if (theParams.get(BaseResource.SP_RES_ID) != null) { + StringParam idParm = (StringParam) theParams.get(BaseResource.SP_RES_ID).get(0).get(0); + predicates.add(builder.equal(from.get("myId"), idParm.getValue())); + } + predicates.add(builder.equal(from.get("myResourceType"), myResourceName)); + predicates.add(builder.isNull(from.get("myDeleted"))); + cq.where(builder.and(SearchBuilder.toArray(predicates))); + + Join join = from.join("myIncomingResourceLinks", JoinType.LEFT); + cq.multiselect(from.get("myId").as(Long.class), join.get("mySourceResourcePid").as(Long.class)); + + TypedQuery query = myEntityManager.createQuery(cq); + loadPids = new HashSet(); + for (Tuple next : query.getResultList()) { + loadPids.add(next.get(0, Long.class)); + Long nextLong = next.get(1, Long.class); + if(nextLong != null) { + loadPids.add(nextLong); + } + } + + } else if (theParams.isEmpty()) { + + loadPids = new HashSet(); + TypedQuery query = createSearchAllByTypeQuery(lu); + lu = null; + for (Tuple next : query.getResultList()) { + loadPids.add(next.get(0, Long.class)); + } + if (loadPids.isEmpty()) { + return new SimpleBundleProvider(); + } + + } else { + + List searchResultPids; + if (mySearchDao == null) { + if (theParams.containsKey(Constants.PARAM_TEXT)) { + throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_TEXT); + } else if (theParams.containsKey(Constants.PARAM_CONTENT)) { + throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_CONTENT); + } + searchResultPids = null; + } else { + searchResultPids = mySearchDao.search(myResourceName, theParams); + } + if (theParams.isEmpty()) { + loadPids = searchResultPids; + } else { + loadPids = searchForIdsWithAndOr(theParams, searchResultPids, lu); + } + if (loadPids.isEmpty()) { + return new SimpleBundleProvider(); + } + + } + + // // Load _include and _revinclude before filter and sort in everything mode + // if (theParams.getEverythingMode() != null) { + // if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) { + // loadPids.addAll(loadReverseIncludes(loadPids, theParams.getRevIncludes(), true, theParams.getEverythingMode())); + // loadPids.addAll(loadReverseIncludes(loadPids, theParams.getIncludes(), false, theParams.getEverythingMode())); + // } + // } + + // Handle _lastUpdated + if (lu != null) { + List resultList = filterResourceIdsByLastUpdated(loadPids, lu); + loadPids.clear(); + for (Long next : resultList) { + loadPids.add(next); + } + + if (loadPids.isEmpty()) { + return new SimpleBundleProvider(); + } + } + + // Handle sorting if any was provided + final List pids = processSort(theParams, loadPids); + + // Load _revinclude resources + final Set revIncludedPids; + if (theParams.getEverythingMode() == null) { + if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) { + revIncludedPids = loadReverseIncludes(pids, theParams.getRevIncludes(), true, null, lu); + } else { + revIncludedPids = new HashSet(); + } + } else { + revIncludedPids = new HashSet(); + } + + ourLog.debug("Search returned PIDs: {}", pids); + + final int totalCount = pids.size(); + + IBundleProvider retVal = new IBundleProvider() { + @Override + public InstantDt getPublished() { + return now; + } + + @Override + public List getResources(final int theFromIndex, final int theToIndex) { + TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager); + return template.execute(new TransactionCallback>() { + @Override + public List doInTransaction(TransactionStatus theStatus) { + List pidsSubList = pids.subList(theFromIndex, theToIndex); + + // Load includes + pidsSubList = new ArrayList(pidsSubList); + revIncludedPids.addAll(loadReverseIncludes(pidsSubList, theParams.getIncludes(), false, null, theParams.getLastUpdated())); + + // Execute the query and make sure we return distinct results + List resources = new ArrayList(); + loadResourcesByPid(pidsSubList, resources, revIncludedPids, false); + + return resources; + } + + }); + } + + @Override + public Integer preferredPageSize() { + return theParams.getCount(); + } + + @Override + public int size() { + return totalCount; + } + }; + + ourLog.info(" {} on {} in {}ms", new Object[] { myResourceName, theParams, w.getMillisAndRestart() }); + return retVal; + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaPlainProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaPlainProvider.java new file mode 100644 index 00000000000..57841135718 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaPlainProvider.java @@ -0,0 +1,24 @@ +package ca.uhn.fhir.jpa.provider; + +import org.springframework.beans.factory.annotation.Required; + +import ca.uhn.fhir.jpa.dao.IFhirSystemDao; + +public class BaseJpaPlainProvider extends BaseJpaProvider { + + private IFhirSystemDao myDao; + + public BaseJpaPlainProvider() { + // nothing + } + + @Required + public void setDao(IFhirSystemDao theDao) { + myDao = theDao; + } + + public IFhirSystemDao getDao() { + return myDao; + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java index 22efc907338..47d8f1f84be 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java @@ -29,15 +29,17 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.jboss.logging.MDC; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.method.RequestDetails; public class BaseJpaProvider { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaProvider.class); public static final String REMOTE_ADDR = "req.remoteAddr"; - public static final String REMOTE_UA = "req.userAgent"; + private FhirContext myContext; + public void endRequest(HttpServletRequest theRequest) { MDC.remove(REMOTE_ADDR); MDC.remove(REMOTE_UA); @@ -47,6 +49,14 @@ public class BaseJpaProvider { endRequest(theRequest.getServletRequest()); } + public FhirContext getContext() { + return myContext; + } + + public void setContext(FhirContext theContext) { + myContext = theContext; + } + public void startRequest(HttpServletRequest theRequest) { if (theRequest == null) { return; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java index b40139e8c99..9a0ebd36fb0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java @@ -43,7 +43,6 @@ import ca.uhn.fhir.rest.server.IResourceProvider; public abstract class BaseJpaResourceProvider extends BaseJpaProvider implements IResourceProvider { - private FhirContext myContext; private IFhirResourceDao myDao; public BaseJpaResourceProvider() { @@ -55,10 +54,6 @@ public abstract class BaseJpaResourceProvider extends BaseJ myDao = theDao; } - public FhirContext getContext() { - return myContext; - } - public IFhirResourceDao getDao() { return myDao; } @@ -118,10 +113,6 @@ public abstract class BaseJpaResourceProvider extends BaseJ } } - public void setContext(FhirContext theContext) { - myContext = theContext; - } - @Required public void setDao(IFhirResourceDao theDao) { myDao = theDao; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2SearchTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2SearchTest.java new file mode 100644 index 00000000000..878db8d0c25 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2SearchTest.java @@ -0,0 +1,36 @@ +package ca.uhn.fhir.jpa.dao; + +public class FhirSystemDaoDstu2SearchTest { + + /*//@formatter:off + * [ERROR] Search parameter action has conflicting types token and reference + * [ERROR] Search parameter source has conflicting types token and reference + * [ERROR] Search parameter plan has conflicting types reference and token + * [ERROR] Search parameter version has conflicting types token and string + * [ERROR] Search parameter source has conflicting types reference and uri + * [ERROR] Search parameter location has conflicting types reference and uri + * [ERROR] Search parameter title has conflicting types string and token + * [ERROR] Search parameter manufacturer has conflicting types string and reference + * [ERROR] Search parameter address has conflicting types token and string + * [ERROR] Search parameter source has conflicting types reference and string + * [ERROR] Search parameter destination has conflicting types reference and string + * [ERROR] Search parameter responsible has conflicting types reference and string + * [ERROR] Search parameter value has conflicting types token and string + * [ERROR] Search parameter address has conflicting types token and string + * [ERROR] Search parameter address has conflicting types token and string + * [ERROR] Search parameter address has conflicting types token and string + * [ERROR] Search parameter address has conflicting types token and string + * [ERROR] Search parameter action has conflicting types reference and token + * [ERROR] Search parameter version has conflicting types token and string + * [ERROR] Search parameter address has conflicting types token and string + * [ERROR] Search parameter base has conflicting types reference and token + * [ERROR] Search parameter target has conflicting types reference and token + * [ERROR] Search parameter base has conflicting types reference and uri + * [ERROR] Search parameter contact has conflicting types string and token + * [ERROR] Search parameter substance has conflicting types token and reference + * [ERROR] Search parameter provider has conflicting types reference and token + * [ERROR] Search parameter system has conflicting types token and uri + * [ERROR] Search parameter reference has conflicting types reference and uri + * //@formatter:off + */ +} diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java index 07baa86c01a..4ea9c7f71c6 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java @@ -54,7 +54,27 @@ public class ServerSearchDstu2Test { assertEquals("searchParam1", ourLastMethod); assertEquals("param1value", ourLastRef.getValue()); } - + + @Test + public void testSearchParam2() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param2=param2value&foo=bar"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals("searchParam2", ourLastMethod); + assertEquals("param2value", ourLastRef.getValue()); + } + + @Test + public void testUnknownSearchParam() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?foo=bar"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(null, ourLastMethod); + } @AfterClass public static void afterClass() throws Exception { @@ -86,9 +106,8 @@ public class ServerSearchDstu2Test { public static class DummyPatientResourceProvider { - //@formatter:off - @Search() + @Search(allowUnknownParams=true) public List searchParam1( @RequiredParam(name = "param1") StringParam theParam) { ourLastMethod = "searchParam1"; @@ -104,7 +123,7 @@ public class ServerSearchDstu2Test { //@formatter:on //@formatter:off - @Search() + @Search(allowUnknownParams=true) public List searchParam2( @RequiredParam(name = "param2") StringParam theParam) { ourLastMethod = "searchParam2"; diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java index 9d237a47a7f..27efd0f44ea 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java @@ -210,9 +210,14 @@ public class TinderJpaRestServerMojo extends AbstractMojo { // Conformance conformance = new FhirContext(Conformance.class).newXmlParser().parseResource(Conformance.class, metadataString); TinderJpaRestServerMojo mojo = new TinderJpaRestServerMojo(); + mojo.myProject = new MavenProject(); + mojo.version = "dstu2"; mojo.packageBase = "ca.uhn.test"; - mojo.baseResourceNames = java.util.Collections.singletonList("observation"); + mojo.configPackageBase = "ca.uhn.test"; +// mojo.baseResourceNames = new ArrayList(Collections.singletonList("observation")); mojo.targetDirectory = new File("target/generated/valuesets"); + mojo.targetResourceDirectory = new File("target/generated/valuesets"); + mojo.targetResourceSpringBeansFile = "tmp_beans.xml"; mojo.execute(); } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureParser.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureParser.java index 5bbaf59c116..e2f67b83058 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureParser.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureParser.java @@ -60,7 +60,7 @@ public abstract class BaseStructureParser { private TreeMap myNameToDatatypeClass = new TreeMap(); private TreeMap myNameToResourceClass = new TreeMap(); private String myPackageBase; - private List myResources = new ArrayList(); + protected List myResources = new ArrayList(); private String myVersion; public BaseStructureParser(String theVersion, String theBaseDir) { @@ -555,7 +555,7 @@ public abstract class BaseStructureParser { } } - private FhirVersionEnum determineVersionEnum() throws MojoFailureException { + protected FhirVersionEnum determineVersionEnum() throws MojoFailureException { FhirVersionEnum versionEnum = null; if ("dstu".equals(myVersion)) { versionEnum = FhirVersionEnum.DSTU1; diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/ResourceGeneratorUsingSpreadsheet.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/ResourceGeneratorUsingSpreadsheet.java index 459cf568290..1788eb3ba7f 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/ResourceGeneratorUsingSpreadsheet.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/ResourceGeneratorUsingSpreadsheet.java @@ -16,7 +16,7 @@ public class ResourceGeneratorUsingSpreadsheet extends BaseStructureSpreadsheetP private List myInputStreamNames; private ArrayList myInputStreams; private String myTemplate = null; - private String myVersion; + protected String myVersion; public ResourceGeneratorUsingSpreadsheet(String theVersion, String theBaseDir) { super(theVersion, theBaseDir); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 439a16be2f5..98743c2ec42 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -217,6 +217,11 @@ which are part of a bundle when the resource has a UUID/OID ID. + + Add ability for a server REST resource provider @Search method + to declare that it should allow even parameters it doesn't + understand. +