Allow forced IDs to be reused between resource types
This commit is contained in:
parent
9631160942
commit
d45a9b67dc
|
@ -113,7 +113,7 @@ public interface IQuery<T> extends IClientExecutable<IQuery<T>, T>, IBaseQuery<I
|
|||
* Request that the client return the specified bundle type, e.g. <code>org.hl7.fhir.instance.model.Bundle.class</code>
|
||||
* or <code>ca.uhn.fhir.model.dstu2.resource.Bundle.class</code>
|
||||
*/
|
||||
<B extends IBaseBundle> IQuery<B> returnBundle(Class<B> theClass);
|
||||
<B extends IBaseBundle> IClientExecutable<IQuery<B>, B> returnBundle(Class<B> theClass);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
|
|
@ -45,6 +45,7 @@ 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.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.xml.stream.events.Characters;
|
||||
|
@ -52,6 +53,7 @@ import javax.xml.stream.events.XMLEvent;
|
|||
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
||||
|
@ -195,7 +197,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
protected EntityManager myEntityManager;
|
||||
|
||||
@Autowired
|
||||
private IForcedIdDao myForcedIdDao;
|
||||
protected IForcedIdDao myForcedIdDao;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager myPlatformTransactionManager;
|
||||
|
@ -216,15 +218,17 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
@Autowired
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
|
||||
protected void createForcedIdIfNeeded(ResourceTable entity, IIdType id) {
|
||||
if (id.isEmpty() == false && id.hasIdPart()) {
|
||||
if (isValidPid(id)) {
|
||||
protected void createForcedIdIfNeeded(ResourceTable theEntity, IIdType theId) {
|
||||
if (theId.isEmpty() == false && theId.hasIdPart()) {
|
||||
if (isValidPid(theId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ForcedId fid = new ForcedId();
|
||||
fid.setForcedId(id.getIdPart());
|
||||
fid.setResource(entity);
|
||||
entity.setForcedId(fid);
|
||||
fid.setResourceType(theEntity.getResourceType());
|
||||
fid.setForcedId(theId.getIdPart());
|
||||
fid.setResource(theEntity);
|
||||
theEntity.setForcedId(fid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,7 +313,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
Long valueOf;
|
||||
try {
|
||||
valueOf = translateForcedIdToPid(nextValue.getReferenceElement());
|
||||
valueOf = translateForcedIdToPid(typeString, id);
|
||||
} catch (ResourceNotFoundException e) {
|
||||
String resName = getContext().getResourceDefinition(type).getName();
|
||||
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
|
||||
|
@ -496,7 +500,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
if (theResourceName != null) {
|
||||
Predicate typePredicate = builder.equal(from.get("myResourceType"), theResourceName);
|
||||
if (theResourceId != null) {
|
||||
cq.where(typePredicate, builder.equal(from.get("myResourceId"), translateForcedIdToPid(theResourceId)));
|
||||
cq.where(typePredicate, builder.equal(from.get("myResourceId"), translateForcedIdToPid(theResourceName, theResourceId.getIdPart())));
|
||||
} else {
|
||||
cq.where(typePredicate);
|
||||
}
|
||||
|
@ -509,6 +513,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
protected DaoConfig getConfig() {
|
||||
return myConfig;
|
||||
}
|
||||
|
@ -1083,16 +1088,24 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return myContext.getResourceDefinition(theResource).getName();
|
||||
}
|
||||
|
||||
protected Long translateForcedIdToPid(IIdType theId) {
|
||||
return translateForcedIdToPid(theId, myEntityManager);
|
||||
protected List<Long> translateForcedIdToPids(IIdType theId) {
|
||||
return translateForcedIdToPids(theId, myForcedIdDao);
|
||||
}
|
||||
|
||||
protected String translatePidIdToForcedId(Long theId) {
|
||||
protected static Long translateForcedIdToPid(String theResourceName, String theResourceId, IForcedIdDao theForcedIdDao) {
|
||||
return translateForcedIdToPids(new IdDt(theResourceName, theResourceId), theForcedIdDao).get(0);
|
||||
}
|
||||
|
||||
protected Long translateForcedIdToPid(String theResourceName, String theResourceId) {
|
||||
return translateForcedIdToPids(new IdDt(theResourceName, theResourceId), myForcedIdDao).get(0);
|
||||
}
|
||||
|
||||
protected String translatePidIdToForcedId(String theResourceType, Long theId) {
|
||||
ForcedId forcedId = myForcedIdDao.findByResourcePid(theId);
|
||||
if (forcedId != null) {
|
||||
return forcedId.getForcedId();
|
||||
return forcedId.getResourceType() + '/' + forcedId.getForcedId();
|
||||
} else {
|
||||
return theId.toString();
|
||||
return theResourceType + '/' + theId.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1247,7 +1260,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
Long next = matches.iterator().next();
|
||||
String newId = resourceTypeString + '/' + translatePidIdToForcedId(next);
|
||||
String newId = translatePidIdToForcedId(resourceTypeString, next);
|
||||
ourLog.info("Replacing inline match URL[{}] with ID[{}}", nextId.getValue(), newId);
|
||||
nextRef.setReference(newId);
|
||||
}
|
||||
|
@ -1557,15 +1570,26 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
static Long translateForcedIdToPid(IIdType theId, EntityManager entityManager) {
|
||||
static List<Long> translateForcedIdToPids(IIdType theId, IForcedIdDao theForcedIdDao) {
|
||||
Validate.isTrue(theId.hasIdPart());
|
||||
|
||||
if (isValidPid(theId)) {
|
||||
return theId.getIdPartAsLong();
|
||||
return Collections.singletonList(theId.getIdPartAsLong());
|
||||
} else {
|
||||
TypedQuery<ForcedId> q = entityManager.createNamedQuery("Q_GET_FORCED_ID", ForcedId.class);
|
||||
q.setParameter("ID", theId.getIdPart());
|
||||
try {
|
||||
return q.getSingleResult().getResourcePid();
|
||||
} catch (NoResultException e) {
|
||||
List<ForcedId> forcedId;
|
||||
if (theId.hasResourceType()) {
|
||||
forcedId = theForcedIdDao.findByTypeAndForcedId(theId.getResourceType(), theId.getIdPart());
|
||||
} else {
|
||||
forcedId = theForcedIdDao.findByForcedId(theId.getIdPart());
|
||||
}
|
||||
|
||||
if (forcedId.isEmpty() == false) {
|
||||
List<Long> retVal = new ArrayList<Long>(forcedId.size());
|
||||
for (ForcedId next : forcedId) {
|
||||
retVal.add(next.getResourcePid());
|
||||
}
|
||||
return retVal;
|
||||
} else {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -305,7 +305,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
if (entity.getForcedId() != null) {
|
||||
try {
|
||||
translateForcedIdToPid(theResource.getIdElement());
|
||||
translateForcedIdToPid(getResourceName(), theResource.getIdElement().getIdPart());
|
||||
throw new UnprocessableEntityException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "duplicateCreateForcedId", theResource.getIdElement().getIdPart()));
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// good, this ID doesn't exist so we can create it
|
||||
|
@ -750,12 +750,13 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BaseHasResource readEntity(IIdType theId, boolean theCheckForForcedId) {
|
||||
validateResourceTypeAndThrowIllegalArgumentException(theId);
|
||||
|
||||
Long pid = translateForcedIdToPid(theId);
|
||||
Long pid = translateForcedIdToPid(getResourceName(), theId.getIdPart());
|
||||
BaseHasResource entity = myEntityManager.find(ResourceTable.class, pid);
|
||||
|
||||
if (entity == null) {
|
||||
|
@ -794,7 +795,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
protected ResourceTable readEntityLatestVersion(IIdType theId) {
|
||||
ResourceTable entity = myEntityManager.find(ResourceTable.class, translateForcedIdToPid(theId));
|
||||
ResourceTable entity = myEntityManager.find(ResourceTable.class, translateForcedIdToPid(getResourceName(), theId.getIdPart()));
|
||||
if (entity == null) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
@ -854,7 +855,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theParams.getRequestDetails());
|
||||
notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
|
||||
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao);
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao);
|
||||
builder.setType(getResourceType(), getResourceName());
|
||||
return builder.search(theParams);
|
||||
}
|
||||
|
@ -880,7 +881,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
@Override
|
||||
public Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams, DateRangeParam theLastUpdated) {
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao);
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao);
|
||||
builder.setType(getResourceType(), getResourceName());
|
||||
builder.searchForIdsWithAndOr(theParams, theLastUpdated);
|
||||
return builder.doGetPids();
|
||||
|
|
|
@ -42,6 +42,8 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.entity.ForcedId;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.util.ReindexFailureException;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
|
@ -60,6 +62,9 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
|
||||
@Autowired
|
||||
private PlatformTransactionManager myTxManager;
|
||||
|
||||
@Autowired
|
||||
private IForcedIdDao myForcedIdDao;
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@Override
|
||||
|
@ -98,6 +103,18 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
|
||||
for (ResourceTable resourceTable : resources) {
|
||||
try {
|
||||
/*
|
||||
* This part is because from HAPI 1.5 - 1.6 we changed the format of
|
||||
* forced ID to be "type/id" instead of just "id"
|
||||
*/
|
||||
ForcedId forcedId = resourceTable.getForcedId();
|
||||
if (forcedId != null) {
|
||||
if (forcedId.getResourceType() == null) {
|
||||
forcedId.setResourceType(resourceTable.getResourceType());
|
||||
myForcedIdDao.save(forcedId);
|
||||
}
|
||||
}
|
||||
|
||||
final IBaseResource resource = toResource(resourceTable, false);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
@ -212,7 +229,7 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
protected ResourceTable tryToLoadEntity(IdDt nextId) {
|
||||
ResourceTable entity;
|
||||
try {
|
||||
Long pid = translateForcedIdToPid(nextId);
|
||||
Long pid = translateForcedIdToPid(nextId.getResourceType(), nextId.getIdPart());
|
||||
entity = myEntityManager.find(ResourceTable.class, pid);
|
||||
} catch (ResourceNotFoundException e) {
|
||||
entity = null;
|
||||
|
|
|
@ -61,7 +61,7 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>im
|
|||
paramMap.add("_id", new StringParam(theId.getIdPart()));
|
||||
}
|
||||
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao);
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao);
|
||||
builder.setType(getResourceType(), getResourceName());
|
||||
return builder.search(paramMap);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ import com.google.common.collect.Sets;
|
|||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.dstu.resource.BaseResource;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -193,7 +192,7 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
Long pid = null;
|
||||
if (theParams.get(BaseResource.SP_RES_ID) != null) {
|
||||
StringParam idParm = (StringParam) theParams.get(BaseResource.SP_RES_ID).get(0).get(0);
|
||||
pid = BaseHapiFhirDao.translateForcedIdToPid(new IdDt(idParm.getValue()), myEntityManager);
|
||||
pid = BaseHapiFhirDao.translateForcedIdToPid(theResourceName, idParm.getValue(), myForcedIdDao);
|
||||
}
|
||||
|
||||
Long referencingPid = pid;
|
||||
|
@ -222,8 +221,7 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
if (contextParts.length != 3 || "Patient".equals(contextParts[0]) == false || "$everything".equals(contextParts[2]) == false) {
|
||||
throw new InvalidRequestException("Invalid context: " + theContext);
|
||||
}
|
||||
IdDt contextId = new IdDt(contextParts[0], contextParts[1]);
|
||||
Long pid = BaseHapiFhirDao.translateForcedIdToPid(contextId, myEntityManager);
|
||||
Long pid = BaseHapiFhirDao.translateForcedIdToPid(contextParts[0], contextParts[1], myForcedIdDao);
|
||||
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ 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.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
|
@ -77,6 +78,7 @@ 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.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
|
@ -138,6 +140,7 @@ public class SearchBuilder {
|
|||
private BaseHapiFhirDao<?> myCallingDao;
|
||||
private FhirContext myContext;
|
||||
private EntityManager myEntityManager;
|
||||
private IForcedIdDao myForcedIdDao;
|
||||
private SearchParameterMap myParams;
|
||||
private Collection<Long> myPids;
|
||||
private PlatformTransactionManager myPlatformTransactionManager;
|
||||
|
@ -149,7 +152,7 @@ public class SearchBuilder {
|
|||
private ISearchResultDao mySearchResultDao;
|
||||
|
||||
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, IFulltextSearchSvc theSearchDao,
|
||||
ISearchResultDao theSearchResultDao, BaseHapiFhirDao<?> theDao, IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao) {
|
||||
ISearchResultDao theSearchResultDao, BaseHapiFhirDao<?> theDao, IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao, IForcedIdDao theForcedIdDao) {
|
||||
myContext = theFhirContext;
|
||||
myEntityManager = theEntityManager;
|
||||
myPlatformTransactionManager = thePlatformTransactionManager;
|
||||
|
@ -157,6 +160,7 @@ public class SearchBuilder {
|
|||
mySearchResultDao = theSearchResultDao;
|
||||
myCallingDao = theDao;
|
||||
myResourceIndexedSearchParamUriDao = theResourceIndexedSearchParamUriDao;
|
||||
myForcedIdDao = theForcedIdDao;
|
||||
}
|
||||
|
||||
private void addPredicateComposite(RuntimeSearchParam theParamDef, List<? extends IQueryParameterType> theNextAnd) {
|
||||
|
@ -548,16 +552,13 @@ public class SearchBuilder {
|
|||
|
||||
if (isBlank(ref.getChain())) {
|
||||
String resourceId = ref.getValueAsQueryToken(myContext);
|
||||
if (resourceId.contains("/")) {
|
||||
IIdType dt = new IdDt(resourceId);
|
||||
resourceId = dt.getIdPart();
|
||||
IIdType dt = new IdDt(resourceId);
|
||||
List<Long> targetPid = myCallingDao.translateForcedIdToPids(dt);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
codePredicates.add(eq);
|
||||
|
||||
} else {
|
||||
|
||||
String paramPath = myContext.getResourceDefinition(myResourceType).getSearchParam(theParamName).getPath();
|
||||
|
@ -889,22 +890,6 @@ public class SearchBuilder {
|
|||
|
||||
}
|
||||
|
||||
private List<Predicate> createPredicateTagList(Path<TagDefinition> theDefJoin, CriteriaBuilder theBuilder, TagTypeEnum theTagType, List<Pair<String, String>> theTokens) {
|
||||
Predicate typePrediate = theBuilder.equal(theDefJoin.get("myTagType"), theTagType);
|
||||
|
||||
List<Predicate> orPredicates = Lists.newArrayList();
|
||||
for (Pair<String, String> next : theTokens) {
|
||||
Predicate codePrediate = theBuilder.equal(theDefJoin.get("myCode"), next.getRight());
|
||||
if (isNotBlank(next.getLeft())) {
|
||||
Predicate systemPrediate = theBuilder.equal(theDefJoin.get("mySystem"), next.getLeft());
|
||||
orPredicates.add(theBuilder.and(typePrediate, systemPrediate, codePrediate));
|
||||
} else {
|
||||
orPredicates.add(theBuilder.and(typePrediate, codePrediate));
|
||||
}
|
||||
}
|
||||
return orPredicates;
|
||||
}
|
||||
|
||||
private void addPredicateToken(String theParamName, List<? extends IQueryParameterType> theList) {
|
||||
|
||||
if (Boolean.TRUE.equals(theList.get(0).getMissing())) {
|
||||
|
@ -1238,6 +1223,22 @@ public class SearchBuilder {
|
|||
return singleCode;
|
||||
}
|
||||
|
||||
private List<Predicate> createPredicateTagList(Path<TagDefinition> theDefJoin, CriteriaBuilder theBuilder, TagTypeEnum theTagType, List<Pair<String, String>> theTokens) {
|
||||
Predicate typePrediate = theBuilder.equal(theDefJoin.get("myTagType"), theTagType);
|
||||
|
||||
List<Predicate> orPredicates = Lists.newArrayList();
|
||||
for (Pair<String, String> next : theTokens) {
|
||||
Predicate codePrediate = theBuilder.equal(theDefJoin.get("myCode"), next.getRight());
|
||||
if (isNotBlank(next.getLeft())) {
|
||||
Predicate systemPrediate = theBuilder.equal(theDefJoin.get("mySystem"), next.getLeft());
|
||||
orPredicates.add(theBuilder.and(typePrediate, systemPrediate, codePrediate));
|
||||
} else {
|
||||
orPredicates.add(theBuilder.and(typePrediate, codePrediate));
|
||||
}
|
||||
}
|
||||
return orPredicates;
|
||||
}
|
||||
|
||||
private Predicate createPredicateToken(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
|
||||
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theFrom) {
|
||||
String code;
|
||||
|
@ -1441,30 +1442,6 @@ public class SearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private void reinitializeSearch() {
|
||||
mySearchEntity = new Search();
|
||||
mySearchEntity.setUuid(UUID.randomUUID().toString());
|
||||
mySearchEntity.setCreated(new Date());
|
||||
mySearchEntity.setTotalCount(-1);
|
||||
mySearchEntity.setPreferredPageSize(myParams.getCount());
|
||||
mySearchEntity.setSearchType(myParams.getEverythingMode() != null ? SearchTypeEnum.EVERYTHING : SearchTypeEnum.SEARCH);
|
||||
mySearchEntity.setLastUpdated(myParams.getLastUpdated());
|
||||
|
||||
for (Include next : myParams.getIncludes()) {
|
||||
mySearchEntity.getIncludes().add(new SearchInclude(mySearchEntity, next.getValue(), false, next.isRecurse()));
|
||||
}
|
||||
for (Include next : myParams.getRevIncludes()) {
|
||||
mySearchEntity.getIncludes().add(new SearchInclude(mySearchEntity, next.getValue(), true, next.isRecurse()));
|
||||
}
|
||||
|
||||
if (myParams.isPersistResults()) {
|
||||
myEntityManager.persist(mySearchEntity);
|
||||
for (SearchInclude next : mySearchEntity.getIncludes()) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IBundleProvider doReturnProvider() {
|
||||
if (myParams.isPersistResults()) {
|
||||
return new PersistedJpaBundleProvider(mySearchEntity.getUuid(), myCallingDao);
|
||||
|
@ -1575,6 +1552,30 @@ public class SearchBuilder {
|
|||
|
||||
}
|
||||
|
||||
private void reinitializeSearch() {
|
||||
mySearchEntity = new Search();
|
||||
mySearchEntity.setUuid(UUID.randomUUID().toString());
|
||||
mySearchEntity.setCreated(new Date());
|
||||
mySearchEntity.setTotalCount(-1);
|
||||
mySearchEntity.setPreferredPageSize(myParams.getCount());
|
||||
mySearchEntity.setSearchType(myParams.getEverythingMode() != null ? SearchTypeEnum.EVERYTHING : SearchTypeEnum.SEARCH);
|
||||
mySearchEntity.setLastUpdated(myParams.getLastUpdated());
|
||||
|
||||
for (Include next : myParams.getIncludes()) {
|
||||
mySearchEntity.getIncludes().add(new SearchInclude(mySearchEntity, next.getValue(), false, next.isRecurse()));
|
||||
}
|
||||
for (Include next : myParams.getRevIncludes()) {
|
||||
mySearchEntity.getIncludes().add(new SearchInclude(mySearchEntity, next.getValue(), true, next.isRecurse()));
|
||||
}
|
||||
|
||||
if (myParams.isPersistResults()) {
|
||||
myEntityManager.persist(mySearchEntity);
|
||||
for (SearchInclude next : mySearchEntity.getIncludes()) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IBundleProvider search(final SearchParameterMap theParams) {
|
||||
myParams = theParams;
|
||||
StopWatch w = new StopWatch();
|
||||
|
@ -1589,7 +1590,7 @@ public class SearchBuilder {
|
|||
Long pid = null;
|
||||
if (theParams.get(BaseResource.SP_RES_ID) != null) {
|
||||
StringParam idParm = (StringParam) theParams.get(BaseResource.SP_RES_ID).get(0).get(0);
|
||||
pid = BaseHapiFhirDao.translateForcedIdToPid(new IdDt(idParm.getValue()), myEntityManager);
|
||||
pid = BaseHapiFhirDao.translateForcedIdToPid(myResourceName, idParm.getValue(), myForcedIdDao);
|
||||
}
|
||||
|
||||
if (theParams.containsKey(Constants.PARAM_CONTENT) || theParams.containsKey(Constants.PARAM_TEXT)) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -28,6 +30,12 @@ import ca.uhn.fhir.jpa.entity.ForcedId;
|
|||
|
||||
public interface IForcedIdDao extends JpaRepository<ForcedId, Long> {
|
||||
|
||||
@Query("SELECT f FROM ForcedId f WHERE myForcedId = :forced_id")
|
||||
public List<ForcedId> findByForcedId(@Param("forced_id") String theForcedId);
|
||||
|
||||
@Query("SELECT f FROM ForcedId f WHERE myResourceType = :resource_type AND myForcedId = :forced_id")
|
||||
public List<ForcedId> findByTypeAndForcedId(@Param("resource_type") String theResourceType, @Param("forced_id") String theForcedId);
|
||||
|
||||
@Query("SELECT f FROM ForcedId f WHERE f.myResourcePid = :resource_pid")
|
||||
public ForcedId findByResourcePid(@Param("resource_pid") Long theResourcePid);
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ public class FhirResourceDaoPatientDstu3 extends FhirResourceDaoDstu3<Patient>im
|
|||
paramMap.add("_id", new StringParam(theId.getIdPart()));
|
||||
}
|
||||
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao);
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao);
|
||||
builder.setType(getResourceType(), getResourceName());
|
||||
return builder.search(paramMap);
|
||||
}
|
||||
|
|
|
@ -25,21 +25,21 @@ import javax.persistence.Entity;
|
|||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
|
||||
//@formatter:off
|
||||
@Entity()
|
||||
@Table(name = "HFJ_FORCED_ID", uniqueConstraints = {
|
||||
@UniqueConstraint(name = "IDX_FORCEDID", columnNames = {"FORCED_ID"}),
|
||||
@UniqueConstraint(name = "IDX_FORCEDID_RESID", columnNames = {"RESOURCE_PID"})
|
||||
})
|
||||
@NamedQueries(value = {
|
||||
@NamedQuery(name = "Q_GET_FORCED_ID", query = "SELECT f FROM ForcedId f WHERE myForcedId = :ID")
|
||||
@UniqueConstraint(name = "IDX_FORCEDID_RESID", columnNames = {"RESOURCE_PID"}),
|
||||
@UniqueConstraint(name = "IDX_FORCEDID_TYPE_RESID", columnNames = {"RESOURCE_TYPE", "RESOURCE_PID"})
|
||||
}, indexes= {
|
||||
@Index(name = "IDX_FORCEDID", columnList = "FORCED_ID"),
|
||||
})
|
||||
//@formatter:on
|
||||
public class ForcedId {
|
||||
|
@ -61,6 +61,17 @@ public class ForcedId {
|
|||
@Column(name = "RESOURCE_PID", nullable = false, updatable = false, insertable=false)
|
||||
private Long myResourcePid;
|
||||
|
||||
@ColumnDefault("''")
|
||||
@Column(name = "RESOURCE_TYPE", nullable = true, length = 100, updatable = false)
|
||||
private String myResourceType;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ForcedId() {
|
||||
super();
|
||||
}
|
||||
|
||||
public String getForcedId() {
|
||||
return myForcedId;
|
||||
}
|
||||
|
@ -68,7 +79,7 @@ public class ForcedId {
|
|||
public ResourceTable getResource() {
|
||||
return myResource;
|
||||
}
|
||||
|
||||
|
||||
public Long getResourcePid() {
|
||||
if (myResourcePid==null) {
|
||||
return myResource.getId();
|
||||
|
@ -76,6 +87,10 @@ public class ForcedId {
|
|||
return myResourcePid;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
public void setForcedId(String theForcedId) {
|
||||
myForcedId = theForcedId;
|
||||
}
|
||||
|
@ -92,4 +107,8 @@ public class ForcedId {
|
|||
myResource = theResourcePid;
|
||||
}
|
||||
|
||||
public void setResourceType(String theResourceType) {
|
||||
myResourceType = theResourceType;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -101,8 +101,12 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
|||
|
||||
@Override
|
||||
public IdDt getIdDt() {
|
||||
Object id = getForcedId() == null ? getResourceId() : getForcedId().getForcedId();
|
||||
return new IdDt(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
|
||||
if (getForcedId() == null) {
|
||||
Long id = myResourceId;
|
||||
return new IdDt(myResourceType + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
|
||||
} else {
|
||||
return new IdDt(getForcedId().getResourceType() + '/' + getForcedId().getForcedId() + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
public Long getResourceId() {
|
||||
|
|
|
@ -246,8 +246,12 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
|
||||
@Override
|
||||
public IdDt getIdDt() {
|
||||
Object id = getForcedId() == null ? myId : getForcedId().getForcedId();
|
||||
return new IdDt(myResourceType + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + myVersion);
|
||||
if (getForcedId() == null) {
|
||||
Long id = myId;
|
||||
return new IdDt(myResourceType + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + myVersion);
|
||||
} else {
|
||||
return new IdDt(getForcedId().getResourceType() + '/' + getForcedId().getForcedId() + '/' + Constants.PARAM_HISTORY + '/' + myVersion);
|
||||
}
|
||||
}
|
||||
|
||||
public Long getIndexStatus() {
|
||||
|
|
|
@ -569,8 +569,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
p.getManagingOrganization().setReference(new IdDt("Organization", id1.getIdPart()));
|
||||
myPatientDao.create(p, mySrd);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertEquals("Resource contains reference to Organization/testCreateWithIllegalReference but resource with ID testCreateWithIllegalReference is actually of type Observation", e.getMessage());
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Resource Organization/testCreateWithIllegalReference not found, specified in path: Patient.managingOrganization", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -255,6 +255,25 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDifferentTypesWithSameForcedId() {
|
||||
String idName = "forcedId";
|
||||
|
||||
Patient pat = new Patient();
|
||||
pat.setId(idName);
|
||||
pat.addName().addFamily("FAM");
|
||||
IIdType patId = myPatientDao.update(pat, mySrd).getId();
|
||||
assertEquals("Patient/" + idName, patId.toUnqualifiedVersionless().getValue());
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.setId(idName);
|
||||
obs.getCode().addCoding().setSystem("foo").setCode("testCreateDifferentTypesWithSameForcedId");
|
||||
IIdType obsId = myObservationDao.update(obs, mySrd).getId();
|
||||
assertEquals("Observation/" + idName, obsId.toUnqualifiedVersionless().getValue());
|
||||
|
||||
pat = myPatientDao.read(patId.toUnqualifiedVersionless(), mySrd);
|
||||
obs = myObservationDao.read(obsId.toUnqualifiedVersionless(), mySrd);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChoiceParamDateAlt() {
|
||||
|
@ -672,8 +691,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
p.getManagingOrganization().setReferenceElement(new IdType("Organization", id1.getIdPart()));
|
||||
myPatientDao.create(p, mySrd);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertEquals("Resource contains reference to Organization/testCreateWithIllegalReference but resource with ID testCreateWithIllegalReference is actually of type Observation", e.getMessage());
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Resource Organization/testCreateWithIllegalReference not found, specified in path: Patient.managingOrganization", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.hl7.fhir.dstu3.model.Patient;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.rest.gclient.IClientExecutable;
|
||||
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
@ -47,7 +48,7 @@ public class StaleSearchDeletingSvcDstu3Test extends BaseResourceProviderDstu3Te
|
|||
}
|
||||
|
||||
//@formatter:off
|
||||
IQuery<Bundle> search = ourClient
|
||||
IClientExecutable<IQuery<Bundle>, Bundle> search = ourClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.NAME.matches().value("Everything"))
|
||||
|
|
|
@ -27,10 +27,12 @@ import org.junit.Test;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
|
@ -41,16 +43,36 @@ public class SearchReturningProfiledResourceDstu2Test {
|
|||
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
||||
private static String ourLastMethod;
|
||||
private static StringParam ourLastRef;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchReturningProfiledResourceDstu2Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourLastMethod = null;
|
||||
ourLastRef = null;
|
||||
@Test
|
||||
public void testClientTypedRequest() throws Exception {
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/");
|
||||
Bundle bundle = client.search().forResource(PatientProfileDstu2.class).returnBundle(Bundle.class).execute();
|
||||
|
||||
assertEquals(PatientProfileDstu2.class, bundle.getEntry().get(0).getResource().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientUntypedRequestWithHint() throws Exception {
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
ourCtx.setDefaultTypeForProfile("http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient", PatientProfileDstu2.class);
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/");
|
||||
Bundle bundle = client.search().forResource(Patient.class).returnBundle(Bundle.class).execute();
|
||||
|
||||
assertEquals(PatientProfileDstu2.class, bundle.getEntry().get(0).getResource().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientUntypedRequestWithoutHint() throws Exception {
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/");
|
||||
Bundle bundle = client.search().forResource(Patient.class).returnBundle(Bundle.class).execute();
|
||||
|
||||
assertEquals(Patient.class, bundle.getEntry().get(0).getResource().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -60,12 +82,11 @@ public class SearchReturningProfiledResourceDstu2Test {
|
|||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
|
||||
|
||||
assertThat(responseContent, containsString("<profile value=\"http://foo\"/>"));
|
||||
assertThat(responseContent, containsString("<profile value=\"http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient\"/>"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
|
@ -96,6 +117,11 @@ public class SearchReturningProfiledResourceDstu2Test {
|
|||
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic search, available for every resource. Allows search per id (use of read is better, though), fulltext
|
||||
* content
|
||||
|
@ -118,24 +144,19 @@ public class SearchReturningProfiledResourceDstu2Test {
|
|||
//@formatter:on
|
||||
|
||||
List<Patient> result = new ArrayList<Patient>();
|
||||
|
||||
|
||||
PatientProfileDstu2 pp = new PatientProfileDstu2();
|
||||
|
||||
|
||||
ResourceMetadataKeyEnum.PROFILES.put(pp, Collections.singletonList(new IdDt("http://foo")));
|
||||
|
||||
|
||||
pp.setId("123");
|
||||
pp.getOwningOrganization().setReference("Organization/456");
|
||||
result.add(pp);
|
||||
|
||||
|
||||
ourLog.info("Search: Everything ok. Going to return results!");
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,12 @@
|
|||
</action>
|
||||
<action type="add">
|
||||
Deprecate fluent client search operations without an explicit declaration of the
|
||||
bundle type being used
|
||||
bundle type being used. This also means that in a client
|
||||
<![CDATA[<code>.search()</code>]]>
|
||||
operation, the
|
||||
<![CDATA[<code>.returnBundle(Bundle.class)</code>]]>
|
||||
needs to be the last statement before
|
||||
<![CDATA[<code>.execute()</code>]]>
|
||||
</action>
|
||||
<action type="add" issue="346">
|
||||
Server now respects the parameter <![CDATA[<code>_format=application/xml+fhir"</code>]]>
|
||||
|
@ -39,6 +44,13 @@
|
|||
(the correct one is "id"). Previously _id was allowed because some early FHIR examples
|
||||
used that form, but this was never actually valid so it is now being removed.
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA server now allows "forced IDs" (ids containing non-numeric, client assigned IDs)
|
||||
to use the same logical ID part on different resource types. E.g. A server may now have
|
||||
both Patient/foo and Obervation/foo on the same server.<![CDATA[<br/><br/>]]>
|
||||
Note that existing databases will need to modify index "IDX_FORCEDID" as
|
||||
it is no longer unique, and perform a reindexing pass.
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.5" date="2016-04-20">
|
||||
<action type="fix" issue="339">
|
||||
|
|
Loading…
Reference in New Issue