More work on performance

This commit is contained in:
James Agnew 2017-04-06 12:50:36 -04:00
parent 1581cdf9a8
commit f48d0d677b
11 changed files with 462 additions and 205 deletions

View File

@ -40,6 +40,8 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
@Configuration
@EnableScheduling
@ -64,11 +66,6 @@ public class BaseConfig implements SchedulingConfigurer {
return retVal;
}
@Bean(autowire=Autowire.BY_TYPE)
public IStaleSearchDeletingSvc staleSearchDeletingSvc() {
return new StaleSearchDeletingSvcImpl();
}
@Bean()
public ScheduledExecutorFactoryBean scheduledExecutorService() {
ScheduledExecutorFactoryBean b = new ScheduledExecutorFactoryBean();
@ -76,6 +73,16 @@ public class BaseConfig implements SchedulingConfigurer {
return b;
}
@Bean
public ISearchParamPresenceSvc searchParamPresenceSvc() {
return new SearchParamPresenceSvcImpl();
}
@Bean(autowire=Autowire.BY_TYPE)
public IStaleSearchDeletingSvc staleSearchDeletingSvc() {
return new StaleSearchDeletingSvcImpl();
}
@Bean
public TaskScheduler taskScheduler() {
ConcurrentTaskScheduler retVal = new ConcurrentTaskScheduler();

View File

@ -85,28 +85,9 @@ 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.*;
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.ForcedId;
import ca.uhn.fhir.jpa.entity.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTag;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
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.SearchTypeEnum;
import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
@ -197,7 +178,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
protected EntityManager myEntityManager;
@Autowired
protected IForcedIdDao myForcedIdDao;
@Autowired(required = false)
protected IFulltextSearchSvc myFulltextSearchSvc;
@Autowired
@ -220,6 +200,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
@Autowired
private ISearchParamExtractor mySearchParamExtractor;
@Autowired
private ISearchParamPresenceSvc mySearchParamPresenceSvc;
@Autowired
private ISearchParamRegistry mySearchParamRegistry;
@ -258,14 +241,19 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return InstantDt.withCurrentTime();
}
/**
* @return Returns a set containing all of the parameter names that
* were found to have a value
*/
@SuppressWarnings("unchecked")
protected void extractResourceLinks(ResourceTable theEntity, IBaseResource theResource, Set<ResourceLink> theLinks, Date theUpdateTime) {
protected Set<String> extractResourceLinks(ResourceTable theEntity, IBaseResource theResource, Set<ResourceLink> theLinks, Date theUpdateTime) {
HashSet<String> retVal = new HashSet<String>();
/*
* For now we don't try to load any of the links in a bundle if it's the actual bundle we're storing..
*/
if (theResource instanceof IBaseBundle) {
return;
return Collections.emptySet();
}
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
@ -318,6 +306,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
}
retVal.add(nextSpDef.getName());
if (isLogicalReference(nextId)) {
ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
if (theLinks.add(resourceLink)) {
@ -401,6 +391,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theEntity.setHasLinks(theLinks.size() > 0);
return retVal;
}
protected Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
@ -1343,7 +1334,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
links = new HashSet<ResourceLink>();
extractResourceLinks(theEntity, theResource, links, theUpdateTime);
Set<String> populatedResourceLinkParameters = extractResourceLinks(theEntity, theResource, links, theUpdateTime);
/*
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
@ -1383,6 +1374,29 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theEntity.setIndexStatus(INDEX_STATUS_INDEXED);
populateFullTextFields(theResource, theEntity);
/*
* Update the "search param present" table which is used for the
* ?foo:missing=true queries
*/
Map<String, Boolean> presentSearchParams = new HashMap<String, Boolean>();
for (String nextKey : populatedResourceLinkParameters) {
presentSearchParams.put(nextKey, Boolean.TRUE);
}
updateSearchParamPresent(presentSearchParams, stringParams);
updateSearchParamPresent(presentSearchParams, tokenParams);
updateSearchParamPresent(presentSearchParams, numberParams);
updateSearchParamPresent(presentSearchParams, quantityParams);
updateSearchParamPresent(presentSearchParams, dateParams);
updateSearchParamPresent(presentSearchParams, uriParams);
updateSearchParamPresent(presentSearchParams, coordsParams);
Set<String> activeSearchParamNames = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType()).keySet();
for (String nextSpName : activeSearchParamNames) {
if (!presentSearchParams.containsKey(nextSpName)) {
presentSearchParams.put(nextSpName, Boolean.FALSE);
}
}
mySearchParamPresenceSvc.updatePresence(theEntity, presentSearchParams);
} else {
populateResourceIntoEntity(theResource, theEntity);
@ -1502,6 +1516,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return theEntity;
}
private void updateSearchParamPresent(Map<String, Boolean> presentSearchParams, Set<? extends BaseResourceIndexedSearchParam> params) {
for (BaseResourceIndexedSearchParam nextSearchParam : params) {
presentSearchParams.put(nextSearchParam.getParamName(), Boolean.TRUE);
}
}
protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable entity, Date theDeletedTimestampOrNull, Date theUpdateTime) {
return updateEntity(theResource, entity, theDeletedTimestampOrNull, true, true, theUpdateTime);
}

View File

@ -106,6 +106,8 @@ public class SearchBuilder {
private CriteriaBuilder myBuilder;
private CriteriaQuery<Tuple> myResourceTableQuery;
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, IFulltextSearchSvc theFulltextSearchSvc,
ISearchResultDao theSearchResultDao, BaseHapiFhirDao<?> theDao,
IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao, IForcedIdDao theForcedIdDao, IHapiTerminologySvc theTerminologySvc, ISearchParamRegistry theSearchParamRegistry) {
@ -317,7 +319,7 @@ public class SearchBuilder {
return missingFalse;
}
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates,
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, From<?,? extends ResourceLink> from, List<Predicate> codePredicates,
IQueryParameterType nextOr) {
boolean missingFalse = false;
if (nextOr.getMissing() != null) {
@ -456,17 +458,14 @@ public class SearchBuilder {
return;
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceLink> from = cq.from(ResourceLink.class);
cq.select(from.get("mySourceResourcePid").as(Long.class));
Join<ResourceTable, ResourceLink> join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresentForResourceLink(builder, theParamName, from, codePredicates, nextOr)) {
if (addPredicateMissingFalseIfPresentForResourceLink(myBuilder, theParamName, join, codePredicates, nextOr)) {
continue;
}
@ -481,7 +480,7 @@ public class SearchBuilder {
dt = dt.toUnqualified();
} else {
ourLog.debug("Searching for resource link with target URL: {}", dt.getValue());
Predicate eq = builder.equal(from.get("myTargetResourceUrl"), dt.getValue());
Predicate eq = myBuilder.equal(join.get("myTargetResourceUrl"), dt.getValue());
codePredicates.add(eq);
continue;
}
@ -491,12 +490,12 @@ public class SearchBuilder {
try {
targetPid = myCallingDao.translateForcedIdToPids(dt);
} catch (ResourceNotFoundException e) {
doSetPids(new ArrayList<Long>());
return;
// Use a PID that will never exist
targetPid = Collections.singletonList(-1L);
}
for (Long next : targetPid) {
ourLog.debug("Searching for resource link with target PID: {}", next);
Predicate eq = builder.equal(from.get("myTargetResourcePid"), next);
Predicate eq = myBuilder.equal(join.get("myTargetResourcePid"), next);
codePredicates.add(eq);
}
} else {
@ -586,13 +585,35 @@ public class SearchBuilder {
}
foundChainMatch = true;
// Set<Long> pids = dao.searchForIds(chain, chainValue);
Set<Long> pids = dao.searchForIds(chain, chainValue);
if (pids.isEmpty()) {
continue;
}
Subquery<Long> subQ = myResourceTableQuery.subquery(Long.class);
Root<ResourceTable> subQfrom = subQ.from(ResourceTable.class);
subQ.select(subQfrom.get("myId").as(Long.class));
Predicate eq = from.get("myTargetResourcePid").in(pids);
List<List<? extends IQueryParameterType>> andOrParams = new ArrayList<List<? extends IQueryParameterType>>();
andOrParams.add(Collections.singletonList(chainValue));
/*
* We're doing a chain call, so push the current query root
* and predicate list down and put new ones at the top of the
* stack and run a subuery
*/
Root<ResourceTable> stackRoot = myResourceTableRoot;
ArrayList<Predicate> stackPredicates = myPredicates;
myResourceTableRoot = subQfrom;
myPredicates = new ArrayList<Predicate>();
searchForIdsWithAndOr(chain, andOrParams);
subQ.where(toArray(myPredicates));
/*
* Pop the old query root and predicate list back
*/
myResourceTableRoot = stackRoot;
myPredicates = stackPredicates;
Predicate eq = join.get("myTargetResourcePid").in(subQ);
codePredicates.add(eq);
}
@ -608,16 +629,7 @@ public class SearchBuilder {
}
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(createResourceLinkPathPredicate(theParamName, from));
predicates.add(builder.or(toArray(codePredicates)));
createPredicateResourceId(builder, cq, predicates, from.get("mySourceResourcePid").as(Long.class));
createPredicateLastUpdatedForResourceLink(builder, from, predicates);
cq.where(builder.and(toArray(predicates)));
TypedQuery<Long> q = myEntityManager.createQuery(cq);
doSetPids(new HashSet<Long>(q.getResultList()));
myPredicates.add(myBuilder.or(toArray(codePredicates)));
}
private void addPredicateString(String theParamName, List<? extends IQueryParameterType> theList) {
@ -713,8 +725,6 @@ public class SearchBuilder {
continue;
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
boolean paramInverted = false;
List<Pair<String, String>> tokens = Lists.newArrayList();
for (IQueryParameterType nextOrParams : nextAndParams) {
@ -745,19 +755,11 @@ public class SearchBuilder {
if (paramInverted) {
ourLog.debug("Searching for _tag:not");
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> newFrom = cq.from(ResourceTable.class);
Subquery<Long> subQ = cq.subquery(Long.class);
Subquery<Long> subQ = myResourceTableQuery.subquery(Long.class);
Root<ResourceTag> subQfrom = subQ.from(ResourceTag.class);
subQ.select(subQfrom.get("myResourceId").as(Long.class));
cq.select(newFrom.get("myId").as(Long.class));
List<Predicate> andPredicates = new ArrayList<Predicate>();
andPredicates = new ArrayList<Predicate>();
andPredicates.add(builder.equal(newFrom.get("myResourceType"), myResourceName));
andPredicates.add(builder.not(builder.in(newFrom.get("myId")).value(subQ)));
myPredicates.add(myBuilder.not(myBuilder.in(myResourceTableRoot.get("myId")).value(subQ)));
Subquery<Long> defJoin = subQ.subquery(Long.class);
Root<TagDefinition> defJoinFrom = defJoin.from(TagDefinition.class);
@ -765,44 +767,18 @@ public class SearchBuilder {
subQ.where(subQfrom.get("myTagId").as(Long.class).in(defJoin));
List<Predicate> orPredicates = createPredicateTagList(defJoinFrom, builder, tagType, tokens);
List<Predicate> orPredicates = createPredicateTagList(defJoinFrom, myBuilder, tagType, tokens);
defJoin.where(toArray(orPredicates));
cq.where(toArray(andPredicates));
TypedQuery<Long> q = myEntityManager.createQuery(cq);
Set<Long> pids = new HashSet<Long>(q.getResultList());
doSetPids(pids);
continue;
}
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTag> from = cq.from(ResourceTag.class);
List<Predicate> andPredicates = new ArrayList<Predicate>();
andPredicates.add(builder.equal(from.get("myResourceType"), myResourceName));
From<ResourceTag, TagDefinition> defJoin = from.join("myTag");
Join<ResourceTable, ResourceTag> tagJoin = myResourceTableRoot.join("myTags", JoinType.LEFT);
From<ResourceTag, TagDefinition> defJoin = tagJoin.join("myTag");
Join<?, ResourceTable> defJoin2 = from.join("myResource");
List<Predicate> orPredicates = createPredicateTagList(defJoin, myBuilder, tagType, tokens);
myPredicates.add(myBuilder.or(toArray(orPredicates)));
Predicate notDeletedPredicatePrediate = builder.isNull(defJoin2.get("myDeleted"));
andPredicates.add(notDeletedPredicatePrediate);
List<Predicate> orPredicates = createPredicateTagList(defJoin, builder, tagType, tokens);
andPredicates.add(builder.or(toArray(orPredicates)));
if (theLastUpdated != null) {
andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin2));
}
createPredicateResourceId(builder, cq, andPredicates, from.get("myResourceId").as(Long.class));
Predicate masterCodePredicate = builder.and(toArray(andPredicates));
cq.select(from.get("myResourceId").as(Long.class));
cq.where(masterCodePredicate);
TypedQuery<Long> q = myEntityManager.createQuery(cq);
Set<Long> pids = new HashSet<Long>(q.getResultList());
doSetPids(pids);
}
}
@ -851,16 +827,13 @@ public class SearchBuilder {
return;
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceIndexedSearchParamUri> from = cq.from(ResourceIndexedSearchParamUri.class);
cq.select(from.get("myResourcePid").as(Long.class));
Join<ResourceTable, ResourceIndexedSearchParamUri> join = myResourceTableRoot.join("myParamsUri", JoinType.LEFT);
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
if (addPredicateMissingFalseIfPresent(myBuilder, theParamName, join, codePredicates, nextOr)) {
continue;
}
@ -872,7 +845,7 @@ public class SearchBuilder {
continue;
}
Path<Object> fromObj = from.get("myUri");
Path<Object> fromObj = join.get("myUri");
Predicate predicate;
if (param.getQualifier() == UriParamQualifierEnum.ABOVE) {
@ -905,9 +878,9 @@ public class SearchBuilder {
predicate = fromObj.as(String.class).in(toFind);
} else if (param.getQualifier() == UriParamQualifierEnum.BELOW) {
predicate = builder.like(fromObj.as(String.class), createLeftMatchLikeExpression(value));
predicate = myBuilder.like(fromObj.as(String.class), createLeftMatchLikeExpression(value));
} else {
predicate = builder.equal(fromObj.as(String.class), value);
predicate = myBuilder.equal(fromObj.as(String.class), value);
}
codePredicates.add(predicate);
} else {
@ -921,16 +894,8 @@ public class SearchBuilder {
return;
}
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
predicates.add(builder.equal(from.get("myParamName"), theParamName));
predicates.add(builder.or(toArray(codePredicates)));
createPredicateResourceId(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
cq.where(builder.and(toArray(predicates)));
TypedQuery<Long> q = myEntityManager.createQuery(cq);
doSetPids(new HashSet<Long>(q.getResultList()));
Predicate orPredicate = myBuilder.or(toArray(codePredicates));
myPredicates.add(orPredicate);
}
private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) {
@ -1300,7 +1265,7 @@ public class SearchBuilder {
return singleCode;
}
private Predicate createResourceLinkPathPredicate(String theParamName, Root<? extends ResourceLink> from) {
private Predicate createResourceLinkPathPredicate(String theParamName, From<?,? extends ResourceLink> from) {
return createResourceLinkPathPredicate(myCallingDao, myContext, theParamName, from, myResourceType);
}
@ -1642,9 +1607,9 @@ public class SearchBuilder {
}
myBuilder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = myBuilder.createTupleQuery();
cq.distinct(true);
myResourceTableRoot = cq.from(ResourceTable.class);
myResourceTableQuery = myBuilder.createTupleQuery();
myResourceTableQuery.distinct(true);
myResourceTableRoot = myResourceTableQuery.from(ResourceTable.class);
myPredicates = new ArrayList<Predicate>();
myPredicates.add(myBuilder.equal(myResourceTableRoot.get("myResourceType"), myResourceName));
myPredicates.add(myBuilder.isNull(myResourceTableRoot.get("myDeleted")));
@ -1655,10 +1620,10 @@ public class SearchBuilder {
searchForIdsWithAndOr(theParams);
cq.where(myBuilder.and(SearchBuilder.toArray(myPredicates)));
myResourceTableQuery.where(myBuilder.and(SearchBuilder.toArray(myPredicates)));
cq.multiselect(myResourceTableRoot.get("myId").as(Long.class));
TypedQuery<Tuple> query = myEntityManager.createQuery(cq);
myResourceTableQuery.multiselect(myResourceTableRoot.get("myId").as(Long.class));
TypedQuery<Tuple> query = myEntityManager.createQuery(myResourceTableQuery);
query.setFirstResult(theFromIndex);
query.setMaxResults(theToIndex - theFromIndex);
@ -1774,23 +1739,31 @@ public class SearchBuilder {
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamEntry : params.entrySet()) {
String nextParamName = nextParamEntry.getKey();
List<List<? extends IQueryParameterType>> andOrParams = nextParamEntry.getValue();
searchForIdsWithAndOr(nextParamName, andOrParams);
}
}
private void searchForIdsWithAndOr(String nextParamName, List<List<? extends IQueryParameterType>> andOrParams) {
if (nextParamName.equals(BaseResource.SP_RES_ID)) {
addPredicateResourceId(nextParamEntry.getValue());
addPredicateResourceId(andOrParams);
} else if (nextParamName.equals(BaseResource.SP_RES_LANGUAGE)) {
addPredicateLanguage(nextParamEntry.getValue());
addPredicateLanguage(andOrParams);
} else if (nextParamName.equals(Constants.PARAM_HAS)) {
// FIXME
addPredicateHas(nextParamEntry.getValue(), null);
addPredicateHas(andOrParams, null);
} else if (nextParamName.equals(Constants.PARAM_TAG) || nextParamName.equals(Constants.PARAM_PROFILE) || nextParamName.equals(Constants.PARAM_SECURITY)) {
// FIXME
addPredicateTag(nextParamEntry.getValue(), nextParamName, null);
addPredicateTag(andOrParams, nextParamName, null);
} else {
@ -1798,42 +1771,42 @@ public class SearchBuilder {
if (nextParamDef != null) {
switch (nextParamDef.getParamType()) {
case DATE:
for (List<? extends IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
addPredicateDate(nextParamName, nextAnd);
}
break;
case QUANTITY:
for (List<? extends IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
addPredicateQuantity(nextParamName, nextAnd);
}
break;
case REFERENCE:
for (List<? extends IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
addPredicateReference(nextParamName, nextAnd);
}
break;
case STRING:
for (List<? extends IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
addPredicateString(nextParamName, nextAnd);
}
break;
case TOKEN:
for (List<? extends IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
addPredicateToken(nextParamName, nextAnd);
}
break;
case NUMBER:
for (List<? extends IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
addPredicateNumber(nextParamName, nextAnd);
}
break;
case COMPOSITE:
for (List<? extends IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
addPredicateComposite(nextParamDef, nextAnd);
}
break;
case URI:
for (List<? extends IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
addPredicateUri(nextParamName, nextAnd);
}
break;
@ -1843,9 +1816,6 @@ public class SearchBuilder {
}
}
}
}
}
private void addPredicateResourceId(List<List<? extends IQueryParameterType>> theValues) {
@ -1972,7 +1942,7 @@ public class SearchBuilder {
return likeExpression.replace("%", "[%]") + "%";
}
private static Predicate createResourceLinkPathPredicate(IDao theCallingDao, FhirContext theContext, String theParamName, Root<? extends ResourceLink> from,
private static Predicate createResourceLinkPathPredicate(IDao theCallingDao, FhirContext theContext, String theParamName, From<?,? extends ResourceLink> from,
Class<? extends IBaseResource> resourceType) {
RuntimeResourceDefinition resourceDef = theContext.getResourceDefinition(resourceType);
RuntimeSearchParam param = theCallingDao.getSearchParamByName(resourceDef, theParamName);

View File

@ -0,0 +1,34 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ca.uhn.fhir.jpa.entity.SearchParam;
public interface ISearchParamDao extends JpaRepository<SearchParam, Long> {
@Query("SELECT s FROM SearchParam s WHERE s.myResourceName = :resname AND s.myParamName = :parmname")
public SearchParam findForResource(@Param("resname") String theResourceType, @Param("parmname") String theParamName);
}

View File

@ -0,0 +1,38 @@
package ca.uhn.fhir.jpa.dao.data;
import java.util.Collection;
import java.util.Date;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.SearchParamPresent;
public interface ISearchParamPresentDao extends JpaRepository<SearchParamPresent, Long> {
@Query("SELECT s FROM SearchParamPresent s WHERE s.myResourceTable = :res")
public Collection<SearchParamPresent> findAllForResource(@Param("res") ResourceTable theResource);
}

View File

@ -42,7 +42,6 @@ import org.hibernate.search.annotations.Field;
import ca.uhn.fhir.model.primitive.InstantDt;
//@formatter:off
@Embeddable
@Entity
@Table(name = "HFJ_SPIDX_DATE", indexes= {
@ -50,7 +49,6 @@ import ca.uhn.fhir.model.primitive.InstantDt;
@Index(name = "IDX_SP_DATE_UPDATED", columnList = "SP_UPDATED"),
@Index(name = "IDX_SP_DATE_RESID", columnList = "RES_ID")
})
//@formatter:on
public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchParam {
private static final long serialVersionUID = 1L;

View File

@ -0,0 +1,36 @@
package ca.uhn.fhir.jpa.entity;
import javax.persistence.*;
@Entity
@Table(name = "HFJ_SEARCH_PARM", uniqueConstraints= {
@UniqueConstraint(name="IDX_SEARCHPARM_RESTYPE_SPNAME", columnNames= {"RES_TYPE", "PARAM_NAME"})
})
public class SearchParam {
@Id
@SequenceGenerator(name = "SEQ_SEARCHPARM_ID", sequenceName = "SEQ_SEARCHPARM_ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SEARCHPARM_ID")
@Column(name = "RES_ID")
private Long myId;
@Column(name="PARAM_NAME", length=BaseResourceIndexedSearchParam.MAX_SP_NAME, nullable=false, updatable=false)
private String myParamName;
@Column(name="RES_TYPE", length=ResourceTable.RESTYPE_LEN, nullable=false, updatable=false)
private String myResourceName;
public String getParamName() {
return myParamName;
}
public void setParamName(String theParamName) {
myParamName = theParamName;
}
public void setResourceName(String theResourceName) {
myResourceName = theResourceName;
}
}

View File

@ -0,0 +1,58 @@
package ca.uhn.fhir.jpa.entity;
import java.io.Serializable;
import javax.persistence.*;
@Entity
@Table(name = "HFJ_RES_PARAM_PRESENT", indexes = {
@Index(name = "IDX_RESPARMPRESENT_RESID", columnList = "RES_ID")
}, uniqueConstraints = {
@UniqueConstraint(name = "IDX_RESPARMPRESENT_SPID_RESID", columnNames = { "SP_ID", "RES_ID" })
})
public class SearchParamPresent implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name = "SEQ_RESPARMPRESENT_ID", sequenceName = "SEQ_RESPARMPRESENT_ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESPARMPRESENT_ID")
@Column(name = "PID")
private Long myId;
@Column(name = "SP_PRESENT", nullable = false)
private boolean myPresent;
@ManyToOne()
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, foreignKey = @ForeignKey(name = "FK_RESPARMPRES_RESID"))
private ResourceTable myResourceTable;
@ManyToOne()
@JoinColumn(name = "SP_ID", referencedColumnName = "PID", nullable = false, foreignKey = @ForeignKey(name = "FK_RESPARMPRES_SPID"))
private SearchParam mySearchParam;
public ResourceTable getResourceTable() {
return myResourceTable;
}
public SearchParam getSearchParam() {
return mySearchParam;
}
public boolean isPresent() {
return myPresent;
}
public void setPresent(boolean thePresent) {
myPresent = thePresent;
}
public void setResourceTable(ResourceTable theResourceTable) {
myResourceTable = theResourceTable;
}
public void setSearchParam(SearchParam theSearchParam) {
mySearchParam = theSearchParam;
}
}

View File

@ -0,0 +1,11 @@
package ca.uhn.fhir.jpa.sp;
import java.util.Map;
import ca.uhn.fhir.jpa.entity.ResourceTable;
public interface ISearchParamPresenceSvc {
void updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence);
}

View File

@ -0,0 +1,81 @@
package ca.uhn.fhir.jpa.sp;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.data.ISearchParamDao;
import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.SearchParam;
import ca.uhn.fhir.jpa.entity.SearchParamPresent;
public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamPresenceSvcImpl.class);
private Map<Pair<String, String>, SearchParam> myResourceTypeToSearchParamToEntity = new ConcurrentHashMap<Pair<String,String>, SearchParam>();
@Autowired
private ISearchParamDao mySearchParamDao;
@Autowired
private ISearchParamPresentDao mySearchParamPresentDao;
@Override
public void updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence) {
Map<String, Boolean> presenceMap = new HashMap<String, Boolean>(theParamNameToPresence);
List<SearchParamPresent> entitiesToSave = new ArrayList<SearchParamPresent>();
List<SearchParamPresent> entitiesToDelete = new ArrayList<SearchParamPresent>();
Collection<SearchParamPresent> existing = mySearchParamPresentDao.findAllForResource(theResource);
for (SearchParamPresent nextExistingEntity : existing) {
String nextSearchParamName = nextExistingEntity.getSearchParam().getParamName();
Boolean existingValue = presenceMap.remove(nextSearchParamName);
if (existingValue == null) {
entitiesToDelete.add(nextExistingEntity);
} else if (existingValue.booleanValue() == nextExistingEntity.isPresent()) {
ourLog.trace("No change for search param {}", nextSearchParamName);
} else {
nextExistingEntity.setPresent(existingValue);
entitiesToSave.add(nextExistingEntity);
}
}
for (Entry<String, Boolean> next : presenceMap.entrySet()) {
String resourceType = theResource.getResourceType();
String paramName = next.getKey();
Pair<String, String> key = Pair.of(resourceType, paramName);
SearchParam searchParam = myResourceTypeToSearchParamToEntity.get(key);
if (searchParam == null) {
searchParam = mySearchParamDao.findForResource(resourceType, paramName);
if (searchParam != null) {
myResourceTypeToSearchParamToEntity.put(key, searchParam);
} else {
searchParam = new SearchParam();
searchParam.setResourceName(resourceType);
searchParam.setParamName(paramName);
mySearchParamDao.save(searchParam);
// Don't add the newly saved entity to the map in case the save fails
}
SearchParamPresent present = new SearchParamPresent();
present.setResourceTable(theResource);
present.setSearchParam(searchParam);
present.setPresent(next.getValue());
entitiesToSave.add(present);
}
mySearchParamPresentDao.deleteInBatch(entitiesToDelete);
mySearchParamPresentDao.save(entitiesToSave);
}
}
}

View File

@ -2620,6 +2620,10 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
result = myValueSetDao.search(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/").setQualifier(UriParamQualifierEnum.BELOW));
assertThat(toUnqualifiedVersionlessIds(result), contains(id1));
result = myValueSetDao.search(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/FOOOOOO"));
assertThat(toUnqualifiedVersionlessIds(result), empty());
}
@Test