Handle Versioned Canonical Refs (#6003)
* Work on versioned canonidal refs * Merge * Tests working * Working without version support * CLean refactor * Targets * Remove redundant parameter * Fix up tests * Remove fixme * Add changelog * Improve changelog * Update version * Test fixes
This commit is contained in:
parent
72b50a4e6d
commit
8947706af3
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-bom</artifactId>
|
<artifactId>hapi-fhir-bom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>HAPI FHIR BOM</name>
|
<name>HAPI FHIR BOM</name>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-cli</artifactId>
|
<artifactId>hapi-fhir-cli</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 6003
|
||||||
|
title: "When performing a search for a resource which uses versioned canonical
|
||||||
|
references to another resource (e.g. a QuestionnaireResponse with a
|
||||||
|
questionnaire reference of `http://example.org/my-questionnaire|1.0`),
|
||||||
|
the server failed to process `_include` directives which should bring in
|
||||||
|
these references. Includes will now fetch all versions of a canonical
|
||||||
|
reference, which is still not perfect but is an improvement over the
|
||||||
|
current behaviour. A future update may add more targeted behaviour to
|
||||||
|
address including versioned reference targets, but this will require a
|
||||||
|
significant re-architecture of the loading module."
|
|
@ -11,7 +11,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -42,6 +42,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries {
|
||||||
* is an object array, where the order matters (the array represents columns returned by the query).
|
* is an object array, where the order matters (the array represents columns returned by the query).
|
||||||
* Deleted resources are not filtered.
|
* Deleted resources are not filtered.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Collection<Object[]> findAndResolveByForcedIdWithNoTypeIncludeDeleted(
|
public Collection<Object[]> findAndResolveByForcedIdWithNoTypeIncludeDeleted(
|
||||||
String theResourceType, Collection<String> theForcedIds) {
|
String theResourceType, Collection<String> theForcedIds) {
|
||||||
return findAndResolveByForcedIdWithNoType(theResourceType, theForcedIds, false);
|
return findAndResolveByForcedIdWithNoType(theResourceType, theForcedIds, false);
|
||||||
|
@ -52,6 +53,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries {
|
||||||
* is an object array, where the order matters (the array represents columns returned by the query).
|
* is an object array, where the order matters (the array represents columns returned by the query).
|
||||||
* Deleted resources are optionally filtered. Be careful if you change this query in any way.
|
* Deleted resources are optionally filtered. Be careful if you change this query in any way.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Collection<Object[]> findAndResolveByForcedIdWithNoType(
|
public Collection<Object[]> findAndResolveByForcedIdWithNoType(
|
||||||
String theResourceType, Collection<String> theForcedIds, boolean theExcludeDeleted) {
|
String theResourceType, Collection<String> theForcedIds, boolean theExcludeDeleted) {
|
||||||
String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted "
|
String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted "
|
||||||
|
@ -74,6 +76,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries {
|
||||||
* is an object array, where the order matters (the array represents columns returned by the query).
|
* is an object array, where the order matters (the array represents columns returned by the query).
|
||||||
* Deleted resources are optionally filtered. Be careful if you change this query in any way.
|
* Deleted resources are optionally filtered. Be careful if you change this query in any way.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartition(
|
public Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartition(
|
||||||
String theResourceType,
|
String theResourceType,
|
||||||
Collection<String> theForcedIds,
|
Collection<String> theForcedIds,
|
||||||
|
@ -100,6 +103,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries {
|
||||||
* is an object array, where the order matters (the array represents columns returned by the query).
|
* is an object array, where the order matters (the array represents columns returned by the query).
|
||||||
* Deleted resources are optionally filtered. Be careful if you change this query in any way.
|
* Deleted resources are optionally filtered. Be careful if you change this query in any way.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartitionNull(
|
public Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartitionNull(
|
||||||
String theResourceType, Collection<String> theForcedIds, boolean theExcludeDeleted) {
|
String theResourceType, Collection<String> theForcedIds, boolean theExcludeDeleted) {
|
||||||
String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted "
|
String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted "
|
||||||
|
@ -122,6 +126,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries {
|
||||||
* is an object array, where the order matters (the array represents columns returned by the query).
|
* is an object array, where the order matters (the array represents columns returned by the query).
|
||||||
* Deleted resources are optionally filtered. Be careful if you change this query in any way.
|
* Deleted resources are optionally filtered. Be careful if you change this query in any way.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId(
|
public Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId(
|
||||||
String theResourceType,
|
String theResourceType,
|
||||||
Collection<String> theForcedIds,
|
Collection<String> theForcedIds,
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.jpa.search.builder;
|
package ca.uhn.fhir.jpa.search.builder;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||||
|
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||||
import ca.uhn.fhir.context.ComboSearchParamType;
|
import ca.uhn.fhir.context.ComboSearchParamType;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
@ -163,7 +165,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
||||||
public static boolean myUseMaxPageSize50ForTest = false;
|
public static boolean myUseMaxPageSize50ForTest = false;
|
||||||
protected final IInterceptorBroadcaster myInterceptorBroadcaster;
|
protected final IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||||
protected final IResourceTagDao myResourceTagDao;
|
protected final IResourceTagDao myResourceTagDao;
|
||||||
private final String myResourceName;
|
String myResourceName;
|
||||||
private final Class<? extends IBaseResource> myResourceType;
|
private final Class<? extends IBaseResource> myResourceType;
|
||||||
private final HapiFhirLocalContainerEntityManagerFactoryBean myEntityManagerFactory;
|
private final HapiFhirLocalContainerEntityManagerFactoryBean myEntityManagerFactory;
|
||||||
private final SqlObjectFactory mySqlBuilderFactory;
|
private final SqlObjectFactory mySqlBuilderFactory;
|
||||||
|
@ -195,6 +197,9 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private IElasticsearchSvc myIElasticsearchSvc;
|
private IElasticsearchSvc myIElasticsearchSvc;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FhirContext myCtx;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||||
|
|
||||||
|
@ -1309,7 +1314,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
||||||
Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, theParameters.getRequestDetails())) {
|
Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, theParameters.getRequestDetails())) {
|
||||||
CurrentThreadCaptureQueriesListener.startCapturing();
|
CurrentThreadCaptureQueriesListener.startCapturing();
|
||||||
}
|
}
|
||||||
if (matches.size() == 0) {
|
if (matches.isEmpty()) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
}
|
}
|
||||||
if (currentIncludes == null || currentIncludes.isEmpty()) {
|
if (currentIncludes == null || currentIncludes.isEmpty()) {
|
||||||
|
@ -1356,196 +1361,32 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchAll) {
|
if (matchAll) {
|
||||||
StringBuilder sqlBuilder = new StringBuilder();
|
loadIncludesMatchAll(
|
||||||
sqlBuilder.append("SELECT r.").append(findPidFieldName);
|
findPidFieldName,
|
||||||
sqlBuilder.append(", r.").append(findResourceTypeFieldName);
|
findResourceTypeFieldName,
|
||||||
if (findVersionFieldName != null) {
|
findVersionFieldName,
|
||||||
sqlBuilder.append(", r.").append(findVersionFieldName);
|
searchPidFieldName,
|
||||||
}
|
wantResourceType,
|
||||||
sqlBuilder.append(" FROM ResourceLink r WHERE ");
|
reverseMode,
|
||||||
|
hasDesiredResourceTypes,
|
||||||
sqlBuilder.append("r.");
|
nextRoundMatches,
|
||||||
sqlBuilder.append(searchPidFieldName); // (rev mode) target_resource_id | source_resource_id
|
entityManager,
|
||||||
sqlBuilder.append(" IN (:target_pids)");
|
maxCount,
|
||||||
|
desiredResourceTypes,
|
||||||
/*
|
pidsToInclude,
|
||||||
* We need to set the resource type in 2 cases only:
|
request);
|
||||||
* 1) we are in $everything mode
|
|
||||||
* (where we only want to fetch specific resource types, regardless of what is
|
|
||||||
* available to fetch)
|
|
||||||
* 2) we are doing revincludes
|
|
||||||
*
|
|
||||||
* Technically if the request is a qualified star (e.g. _include=Observation:*) we
|
|
||||||
* should always be checking the source resource type on the resource link. We don't
|
|
||||||
* actually index that column though by default, so in order to try and be efficient
|
|
||||||
* we don't actually include it for includes (but we do for revincludes). This is
|
|
||||||
* because for an include, it doesn't really make sense to include a different
|
|
||||||
* resource type than the one you are searching on.
|
|
||||||
*/
|
|
||||||
if (wantResourceType != null
|
|
||||||
&& (reverseMode || (myParams != null && myParams.getEverythingMode() != null))) {
|
|
||||||
// because mySourceResourceType is not part of the HFJ_RES_LINK
|
|
||||||
// index, this might not be the most optimal performance.
|
|
||||||
// but it is for an $everything operation (and maybe we should update the index)
|
|
||||||
sqlBuilder.append(" AND r.mySourceResourceType = :want_resource_type");
|
|
||||||
} else {
|
} else {
|
||||||
wantResourceType = null;
|
loadIncludesMatchSpecific(
|
||||||
}
|
nextInclude,
|
||||||
|
fhirContext,
|
||||||
// When calling $everything on a Patient instance, we don't want to recurse into new Patient
|
findPidFieldName,
|
||||||
// resources
|
findVersionFieldName,
|
||||||
// (e.g. via Provenance, List, or Group) when in an $everything operation
|
searchPidFieldName,
|
||||||
if (myParams != null
|
reverseMode,
|
||||||
&& myParams.getEverythingMode() == SearchParameterMap.EverythingModeEnum.PATIENT_INSTANCE) {
|
nextRoundMatches,
|
||||||
sqlBuilder.append(" AND r.myTargetResourceType != 'Patient'");
|
entityManager,
|
||||||
sqlBuilder.append(UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE.stream()
|
maxCount,
|
||||||
.collect(Collectors.joining("', '", " AND r.mySourceResourceType NOT IN ('", "')")));
|
pidsToInclude);
|
||||||
}
|
|
||||||
if (hasDesiredResourceTypes) {
|
|
||||||
sqlBuilder.append(" AND r.myTargetResourceType IN (:desired_target_resource_types)");
|
|
||||||
}
|
|
||||||
|
|
||||||
String sql = sqlBuilder.toString();
|
|
||||||
List<Collection<JpaPid>> partitions = partition(nextRoundMatches, getMaximumPageSize());
|
|
||||||
for (Collection<JpaPid> nextPartition : partitions) {
|
|
||||||
TypedQuery<?> q = entityManager.createQuery(sql, Object[].class);
|
|
||||||
q.setParameter("target_pids", JpaPid.toLongList(nextPartition));
|
|
||||||
if (wantResourceType != null) {
|
|
||||||
q.setParameter("want_resource_type", wantResourceType);
|
|
||||||
}
|
|
||||||
if (maxCount != null) {
|
|
||||||
q.setMaxResults(maxCount);
|
|
||||||
}
|
|
||||||
if (hasDesiredResourceTypes) {
|
|
||||||
q.setParameter("desired_target_resource_types", desiredResourceTypes);
|
|
||||||
}
|
|
||||||
List<?> results = q.getResultList();
|
|
||||||
for (Object nextRow : results) {
|
|
||||||
if (nextRow == null) {
|
|
||||||
// This can happen if there are outgoing references which are canonical or point to
|
|
||||||
// other servers
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Long version = null;
|
|
||||||
Long resourceLink = (Long) ((Object[]) nextRow)[0];
|
|
||||||
String resourceType = (String) ((Object[]) nextRow)[1];
|
|
||||||
if (findVersionFieldName != null) {
|
|
||||||
version = (Long) ((Object[]) nextRow)[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resourceLink != null) {
|
|
||||||
JpaPid pid =
|
|
||||||
JpaPid.fromIdAndVersionAndResourceType(resourceLink, version, resourceType);
|
|
||||||
pidsToInclude.add(pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
List<String> paths;
|
|
||||||
|
|
||||||
// Start replace
|
|
||||||
RuntimeSearchParam param;
|
|
||||||
String resType = nextInclude.getParamType();
|
|
||||||
if (isBlank(resType)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
RuntimeResourceDefinition def = fhirContext.getResourceDefinition(resType);
|
|
||||||
if (def == null) {
|
|
||||||
ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String paramName = nextInclude.getParamName();
|
|
||||||
if (isNotBlank(paramName)) {
|
|
||||||
param = mySearchParamRegistry.getActiveSearchParam(resType, paramName);
|
|
||||||
} else {
|
|
||||||
param = null;
|
|
||||||
}
|
|
||||||
if (param == null) {
|
|
||||||
ourLog.warn("Unknown param name in include/revinclude=" + nextInclude.getValue());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
paths = param.getPathsSplitForResourceType(resType);
|
|
||||||
// end replace
|
|
||||||
|
|
||||||
Set<String> targetResourceTypes = computeTargetResourceTypes(nextInclude, param);
|
|
||||||
|
|
||||||
for (String nextPath : paths) {
|
|
||||||
String findPidFieldSqlColumn = findPidFieldName.equals(MY_SOURCE_RESOURCE_PID)
|
|
||||||
? "src_resource_id"
|
|
||||||
: "target_resource_id";
|
|
||||||
String fieldsToLoad = "r." + findPidFieldSqlColumn + " AS " + RESOURCE_ID_ALIAS;
|
|
||||||
if (findVersionFieldName != null) {
|
|
||||||
fieldsToLoad += ", r.target_resource_version AS " + RESOURCE_VERSION_ALIAS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query for includes lookup has 2 cases
|
|
||||||
// Case 1: Where target_resource_id is available in hfj_res_link table for local references
|
|
||||||
// Case 2: Where target_resource_id is null in hfj_res_link table and referred by a canonical
|
|
||||||
// url in target_resource_url
|
|
||||||
|
|
||||||
// Case 1:
|
|
||||||
Map<String, Object> localReferenceQueryParams = new HashMap<>();
|
|
||||||
|
|
||||||
String searchPidFieldSqlColumn = searchPidFieldName.equals(MY_TARGET_RESOURCE_PID)
|
|
||||||
? "target_resource_id"
|
|
||||||
: "src_resource_id";
|
|
||||||
StringBuilder localReferenceQuery =
|
|
||||||
new StringBuilder("SELECT " + fieldsToLoad + " FROM hfj_res_link r "
|
|
||||||
+ " WHERE r.src_path = :src_path AND "
|
|
||||||
+ " r.target_resource_id IS NOT NULL AND "
|
|
||||||
+ " r."
|
|
||||||
+ searchPidFieldSqlColumn + " IN (:target_pids) ");
|
|
||||||
localReferenceQueryParams.put("src_path", nextPath);
|
|
||||||
// we loop over target_pids later.
|
|
||||||
if (targetResourceTypes != null) {
|
|
||||||
if (targetResourceTypes.size() == 1) {
|
|
||||||
localReferenceQuery.append(" AND r.target_resource_type = :target_resource_type ");
|
|
||||||
localReferenceQueryParams.put(
|
|
||||||
"target_resource_type",
|
|
||||||
targetResourceTypes.iterator().next());
|
|
||||||
} else {
|
|
||||||
localReferenceQuery.append(" AND r.target_resource_type in (:target_resource_types) ");
|
|
||||||
localReferenceQueryParams.put("target_resource_types", targetResourceTypes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case 2:
|
|
||||||
Pair<String, Map<String, Object>> canonicalQuery = buildCanonicalUrlQuery(
|
|
||||||
findVersionFieldName, searchPidFieldSqlColumn, targetResourceTypes);
|
|
||||||
|
|
||||||
// @formatter:on
|
|
||||||
|
|
||||||
String sql = localReferenceQuery + " UNION " + canonicalQuery.getLeft();
|
|
||||||
|
|
||||||
List<Collection<JpaPid>> partitions = partition(nextRoundMatches, getMaximumPageSize());
|
|
||||||
for (Collection<JpaPid> nextPartition : partitions) {
|
|
||||||
Query q = entityManager.createNativeQuery(sql, Tuple.class);
|
|
||||||
q.setParameter("target_pids", JpaPid.toLongList(nextPartition));
|
|
||||||
localReferenceQueryParams.forEach(q::setParameter);
|
|
||||||
canonicalQuery.getRight().forEach(q::setParameter);
|
|
||||||
|
|
||||||
if (maxCount != null) {
|
|
||||||
q.setMaxResults(maxCount);
|
|
||||||
}
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<Tuple> results = q.getResultList();
|
|
||||||
for (Tuple result : results) {
|
|
||||||
if (result != null) {
|
|
||||||
Long resourceId =
|
|
||||||
NumberUtils.createLong(String.valueOf(result.get(RESOURCE_ID_ALIAS)));
|
|
||||||
Long resourceVersion = null;
|
|
||||||
if (findVersionFieldName != null && result.get(RESOURCE_VERSION_ALIAS) != null) {
|
|
||||||
resourceVersion = NumberUtils.createLong(
|
|
||||||
String.valueOf(result.get(RESOURCE_VERSION_ALIAS)));
|
|
||||||
}
|
|
||||||
pidsToInclude.add(JpaPid.fromIdAndVersion(resourceId, resourceVersion));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1609,9 +1450,264 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
||||||
return allAdded;
|
return allAdded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadIncludesMatchSpecific(
|
||||||
|
Include nextInclude,
|
||||||
|
FhirContext fhirContext,
|
||||||
|
String findPidFieldName,
|
||||||
|
String findVersionFieldName,
|
||||||
|
String searchPidFieldName,
|
||||||
|
boolean reverseMode,
|
||||||
|
List<JpaPid> nextRoundMatches,
|
||||||
|
EntityManager entityManager,
|
||||||
|
Integer maxCount,
|
||||||
|
HashSet<JpaPid> pidsToInclude) {
|
||||||
|
List<String> paths;
|
||||||
|
|
||||||
|
// Start replace
|
||||||
|
RuntimeSearchParam param;
|
||||||
|
String resType = nextInclude.getParamType();
|
||||||
|
if (isBlank(resType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RuntimeResourceDefinition def = fhirContext.getResourceDefinition(resType);
|
||||||
|
if (def == null) {
|
||||||
|
ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String paramName = nextInclude.getParamName();
|
||||||
|
if (isNotBlank(paramName)) {
|
||||||
|
param = mySearchParamRegistry.getActiveSearchParam(resType, paramName);
|
||||||
|
} else {
|
||||||
|
param = null;
|
||||||
|
}
|
||||||
|
if (param == null) {
|
||||||
|
ourLog.warn("Unknown param name in include/revinclude=" + nextInclude.getValue());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paths = param.getPathsSplitForResourceType(resType);
|
||||||
|
// end replace
|
||||||
|
|
||||||
|
Set<String> targetResourceTypes = computeTargetResourceTypes(nextInclude, param);
|
||||||
|
|
||||||
|
for (String nextPath : paths) {
|
||||||
|
String findPidFieldSqlColumn =
|
||||||
|
findPidFieldName.equals(MY_SOURCE_RESOURCE_PID) ? "src_resource_id" : "target_resource_id";
|
||||||
|
String fieldsToLoad = "r." + findPidFieldSqlColumn + " AS " + RESOURCE_ID_ALIAS;
|
||||||
|
if (findVersionFieldName != null) {
|
||||||
|
fieldsToLoad += ", r.target_resource_version AS " + RESOURCE_VERSION_ALIAS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query for includes lookup has 2 cases
|
||||||
|
// Case 1: Where target_resource_id is available in hfj_res_link table for local references
|
||||||
|
// Case 2: Where target_resource_id is null in hfj_res_link table and referred by a canonical
|
||||||
|
// url in target_resource_url
|
||||||
|
|
||||||
|
// Case 1:
|
||||||
|
Map<String, Object> localReferenceQueryParams = new HashMap<>();
|
||||||
|
|
||||||
|
String searchPidFieldSqlColumn =
|
||||||
|
searchPidFieldName.equals(MY_TARGET_RESOURCE_PID) ? "target_resource_id" : "src_resource_id";
|
||||||
|
StringBuilder localReferenceQuery = new StringBuilder("SELECT " + fieldsToLoad + " FROM hfj_res_link r "
|
||||||
|
+ " WHERE r.src_path = :src_path AND "
|
||||||
|
+ " r.target_resource_id IS NOT NULL AND "
|
||||||
|
+ " r."
|
||||||
|
+ searchPidFieldSqlColumn + " IN (:target_pids) ");
|
||||||
|
localReferenceQueryParams.put("src_path", nextPath);
|
||||||
|
// we loop over target_pids later.
|
||||||
|
if (targetResourceTypes != null) {
|
||||||
|
if (targetResourceTypes.size() == 1) {
|
||||||
|
localReferenceQuery.append(" AND r.target_resource_type = :target_resource_type ");
|
||||||
|
localReferenceQueryParams.put(
|
||||||
|
"target_resource_type",
|
||||||
|
targetResourceTypes.iterator().next());
|
||||||
|
} else {
|
||||||
|
localReferenceQuery.append(" AND r.target_resource_type in (:target_resource_types) ");
|
||||||
|
localReferenceQueryParams.put("target_resource_types", targetResourceTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 2:
|
||||||
|
Pair<String, Map<String, Object>> canonicalQuery =
|
||||||
|
buildCanonicalUrlQuery(findVersionFieldName, targetResourceTypes, reverseMode);
|
||||||
|
|
||||||
|
String sql = localReferenceQuery + " UNION " + canonicalQuery.getLeft();
|
||||||
|
|
||||||
|
List<Collection<JpaPid>> partitions = partition(nextRoundMatches, getMaximumPageSize());
|
||||||
|
for (Collection<JpaPid> nextPartition : partitions) {
|
||||||
|
Query q = entityManager.createNativeQuery(sql, Tuple.class);
|
||||||
|
q.setParameter("target_pids", JpaPid.toLongList(nextPartition));
|
||||||
|
localReferenceQueryParams.forEach(q::setParameter);
|
||||||
|
canonicalQuery.getRight().forEach(q::setParameter);
|
||||||
|
|
||||||
|
if (maxCount != null) {
|
||||||
|
q.setMaxResults(maxCount);
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Tuple> results = q.getResultList();
|
||||||
|
for (Tuple result : results) {
|
||||||
|
if (result != null) {
|
||||||
|
Long resourceId = NumberUtils.createLong(String.valueOf(result.get(RESOURCE_ID_ALIAS)));
|
||||||
|
Long resourceVersion = null;
|
||||||
|
if (findVersionFieldName != null && result.get(RESOURCE_VERSION_ALIAS) != null) {
|
||||||
|
resourceVersion =
|
||||||
|
NumberUtils.createLong(String.valueOf(result.get(RESOURCE_VERSION_ALIAS)));
|
||||||
|
}
|
||||||
|
pidsToInclude.add(JpaPid.fromIdAndVersion(resourceId, resourceVersion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadIncludesMatchAll(
|
||||||
|
String findPidFieldName,
|
||||||
|
String findResourceTypeFieldName,
|
||||||
|
String findVersionFieldName,
|
||||||
|
String searchPidFieldName,
|
||||||
|
String wantResourceType,
|
||||||
|
boolean reverseMode,
|
||||||
|
boolean hasDesiredResourceTypes,
|
||||||
|
List<JpaPid> nextRoundMatches,
|
||||||
|
EntityManager entityManager,
|
||||||
|
Integer maxCount,
|
||||||
|
List<String> desiredResourceTypes,
|
||||||
|
HashSet<JpaPid> pidsToInclude,
|
||||||
|
RequestDetails request) {
|
||||||
|
StringBuilder sqlBuilder = new StringBuilder();
|
||||||
|
sqlBuilder.append("SELECT r.").append(findPidFieldName);
|
||||||
|
sqlBuilder.append(", r.").append(findResourceTypeFieldName);
|
||||||
|
sqlBuilder.append(", r.myTargetResourceUrl");
|
||||||
|
if (findVersionFieldName != null) {
|
||||||
|
sqlBuilder.append(", r.").append(findVersionFieldName);
|
||||||
|
}
|
||||||
|
sqlBuilder.append(" FROM ResourceLink r WHERE ");
|
||||||
|
|
||||||
|
sqlBuilder.append("r.");
|
||||||
|
sqlBuilder.append(searchPidFieldName); // (rev mode) target_resource_id | source_resource_id
|
||||||
|
sqlBuilder.append(" IN (:target_pids)");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to set the resource type in 2 cases only:
|
||||||
|
* 1) we are in $everything mode
|
||||||
|
* (where we only want to fetch specific resource types, regardless of what is
|
||||||
|
* available to fetch)
|
||||||
|
* 2) we are doing revincludes
|
||||||
|
*
|
||||||
|
* Technically if the request is a qualified star (e.g. _include=Observation:*) we
|
||||||
|
* should always be checking the source resource type on the resource link. We don't
|
||||||
|
* actually index that column though by default, so in order to try and be efficient
|
||||||
|
* we don't actually include it for includes (but we do for revincludes). This is
|
||||||
|
* because for an include, it doesn't really make sense to include a different
|
||||||
|
* resource type than the one you are searching on.
|
||||||
|
*/
|
||||||
|
if (wantResourceType != null && (reverseMode || (myParams != null && myParams.getEverythingMode() != null))) {
|
||||||
|
// because mySourceResourceType is not part of the HFJ_RES_LINK
|
||||||
|
// index, this might not be the most optimal performance.
|
||||||
|
// but it is for an $everything operation (and maybe we should update the index)
|
||||||
|
sqlBuilder.append(" AND r.mySourceResourceType = :want_resource_type");
|
||||||
|
} else {
|
||||||
|
wantResourceType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When calling $everything on a Patient instance, we don't want to recurse into new Patient
|
||||||
|
// resources
|
||||||
|
// (e.g. via Provenance, List, or Group) when in an $everything operation
|
||||||
|
if (myParams != null
|
||||||
|
&& myParams.getEverythingMode() == SearchParameterMap.EverythingModeEnum.PATIENT_INSTANCE) {
|
||||||
|
sqlBuilder.append(" AND r.myTargetResourceType != 'Patient'");
|
||||||
|
sqlBuilder.append(UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE.stream()
|
||||||
|
.collect(Collectors.joining("', '", " AND r.mySourceResourceType NOT IN ('", "')")));
|
||||||
|
}
|
||||||
|
if (hasDesiredResourceTypes) {
|
||||||
|
sqlBuilder.append(" AND r.myTargetResourceType IN (:desired_target_resource_types)");
|
||||||
|
}
|
||||||
|
|
||||||
|
String sql = sqlBuilder.toString();
|
||||||
|
List<Collection<JpaPid>> partitions = partition(nextRoundMatches, getMaximumPageSize());
|
||||||
|
for (Collection<JpaPid> nextPartition : partitions) {
|
||||||
|
TypedQuery<?> q = entityManager.createQuery(sql, Object[].class);
|
||||||
|
q.setParameter("target_pids", JpaPid.toLongList(nextPartition));
|
||||||
|
if (wantResourceType != null) {
|
||||||
|
q.setParameter("want_resource_type", wantResourceType);
|
||||||
|
}
|
||||||
|
if (maxCount != null) {
|
||||||
|
q.setMaxResults(maxCount);
|
||||||
|
}
|
||||||
|
if (hasDesiredResourceTypes) {
|
||||||
|
q.setParameter("desired_target_resource_types", desiredResourceTypes);
|
||||||
|
}
|
||||||
|
List<?> results = q.getResultList();
|
||||||
|
Set<String> canonicalUrls = null;
|
||||||
|
for (Object nextRow : results) {
|
||||||
|
if (nextRow == null) {
|
||||||
|
// This can happen if there are outgoing references which are canonical or point to
|
||||||
|
// other servers
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long version = null;
|
||||||
|
Long resourceId = (Long) ((Object[]) nextRow)[0];
|
||||||
|
String resourceType = (String) ((Object[]) nextRow)[1];
|
||||||
|
String resourceCanonicalUrl = (String) ((Object[]) nextRow)[2];
|
||||||
|
if (findVersionFieldName != null) {
|
||||||
|
version = (Long) ((Object[]) nextRow)[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceId != null) {
|
||||||
|
JpaPid pid = JpaPid.fromIdAndVersionAndResourceType(resourceId, version, resourceType);
|
||||||
|
pidsToInclude.add(pid);
|
||||||
|
} else if (resourceCanonicalUrl != null) {
|
||||||
|
if (canonicalUrls == null) {
|
||||||
|
canonicalUrls = new HashSet<>();
|
||||||
|
}
|
||||||
|
canonicalUrls.add(resourceCanonicalUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canonicalUrls != null) {
|
||||||
|
String message =
|
||||||
|
"Search with _include=* can be inefficient when references using canonical URLs are detected. Use more specific _include values instead.";
|
||||||
|
firePerformanceWarning(request, message);
|
||||||
|
loadCanonicalUrls(canonicalUrls, entityManager, pidsToInclude, reverseMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCanonicalUrls(
|
||||||
|
Set<String> theCanonicalUrls,
|
||||||
|
EntityManager theEntityManager,
|
||||||
|
HashSet<JpaPid> thePidsToInclude,
|
||||||
|
boolean theReverse) {
|
||||||
|
StringBuilder sqlBuilder;
|
||||||
|
Set<Long> identityHashesForTypes = calculateIndexUriIdentityHashesForResourceTypes(null, theReverse);
|
||||||
|
List<Collection<String>> canonicalUrlPartitions =
|
||||||
|
partition(theCanonicalUrls, getMaximumPageSize() - identityHashesForTypes.size());
|
||||||
|
|
||||||
|
sqlBuilder = new StringBuilder();
|
||||||
|
sqlBuilder.append("SELECT i.myResourcePid ");
|
||||||
|
sqlBuilder.append("FROM ResourceIndexedSearchParamUri i ");
|
||||||
|
sqlBuilder.append("WHERE i.myHashIdentity IN (:hash_identity) ");
|
||||||
|
sqlBuilder.append("AND i.myUri IN (:uris)");
|
||||||
|
|
||||||
|
String canonicalResSql = sqlBuilder.toString();
|
||||||
|
|
||||||
|
for (Collection<String> nextCanonicalUrlList : canonicalUrlPartitions) {
|
||||||
|
TypedQuery<Long> canonicalResIdQuery = theEntityManager.createQuery(canonicalResSql, Long.class);
|
||||||
|
canonicalResIdQuery.setParameter("hash_identity", identityHashesForTypes);
|
||||||
|
canonicalResIdQuery.setParameter("uris", nextCanonicalUrlList);
|
||||||
|
List<Long> resIds = canonicalResIdQuery.getResultList();
|
||||||
|
for (var next : resIds) {
|
||||||
|
if (next != null) {
|
||||||
|
thePidsToInclude.add(JpaPid.fromId(next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a
|
* Sends a raw SQL query to the Pointcut for raw SQL queries.
|
||||||
* @param request
|
|
||||||
*/
|
*/
|
||||||
private void callRawSqlHookWithCurrentThreadQueries(RequestDetails request) {
|
private void callRawSqlHookWithCurrentThreadQueries(RequestDetails request) {
|
||||||
SqlQueryList capturedQueries = CurrentThreadCaptureQueriesListener.getCurrentQueueAndStopCapturing();
|
SqlQueryList capturedQueries = CurrentThreadCaptureQueriesListener.getCurrentQueueAndStopCapturing();
|
||||||
|
@ -1641,30 +1737,22 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private Pair<String, Map<String, Object>> buildCanonicalUrlQuery(
|
private Pair<String, Map<String, Object>> buildCanonicalUrlQuery(
|
||||||
String theVersionFieldName, String thePidFieldSqlColumn, Set<String> theTargetResourceTypes) {
|
String theVersionFieldName, Set<String> theTargetResourceTypes, boolean theReverse) {
|
||||||
String fieldsToLoadFromSpidxUriTable = "rUri.res_id";
|
String fieldsToLoadFromSpidxUriTable = theReverse ? "r.src_resource_id" : "rUri.res_id";
|
||||||
if (theVersionFieldName != null) {
|
if (theVersionFieldName != null) {
|
||||||
// canonical-uri references aren't versioned, but we need to match the column count for the UNION
|
// canonical-uri references aren't versioned, but we need to match the column count for the UNION
|
||||||
fieldsToLoadFromSpidxUriTable += ", NULL";
|
fieldsToLoadFromSpidxUriTable += ", NULL";
|
||||||
}
|
}
|
||||||
// The logical join will be by hfj_spidx_uri on sp_name='uri' and sp_uri=target_resource_url.
|
// The logical join will be by hfj_spidx_uri on sp_name='uri' and sp_uri=target_resource_url.
|
||||||
// But sp_name isn't indexed, so we use hash_identity instead.
|
// But sp_name isn't indexed, so we use hash_identity instead.
|
||||||
if (theTargetResourceTypes == null) {
|
Set<Long> identityHashesForTypes =
|
||||||
// hash_identity includes the resource type. So a null wildcard must be replaced with a list of all types.
|
calculateIndexUriIdentityHashesForResourceTypes(theTargetResourceTypes, theReverse);
|
||||||
theTargetResourceTypes = myDaoRegistry.getRegisteredDaoTypes();
|
|
||||||
}
|
|
||||||
assert !theTargetResourceTypes.isEmpty();
|
|
||||||
|
|
||||||
Set<Long> identityHashesForTypes = theTargetResourceTypes.stream()
|
|
||||||
.map(type -> BaseResourceIndexedSearchParam.calculateHashIdentity(
|
|
||||||
myPartitionSettings, myRequestPartitionId, type, "url"))
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
Map<String, Object> canonicalUriQueryParams = new HashMap<>();
|
Map<String, Object> canonicalUriQueryParams = new HashMap<>();
|
||||||
StringBuilder canonicalUrlQuery = new StringBuilder(
|
StringBuilder canonicalUrlQuery = new StringBuilder(
|
||||||
"SELECT " + fieldsToLoadFromSpidxUriTable + " FROM hfj_res_link r " + " JOIN hfj_spidx_uri rUri ON ( ");
|
"SELECT " + fieldsToLoadFromSpidxUriTable + " FROM hfj_res_link r " + " JOIN hfj_spidx_uri rUri ON ( ");
|
||||||
// join on hash_identity and sp_uri - indexed in IDX_SP_URI_HASH_IDENTITY_V2
|
// join on hash_identity and sp_uri - indexed in IDX_SP_URI_HASH_IDENTITY_V2
|
||||||
if (theTargetResourceTypes.size() == 1) {
|
if (theTargetResourceTypes != null && theTargetResourceTypes.size() == 1) {
|
||||||
canonicalUrlQuery.append(" rUri.hash_identity = :uri_identity_hash ");
|
canonicalUrlQuery.append(" rUri.hash_identity = :uri_identity_hash ");
|
||||||
canonicalUriQueryParams.put(
|
canonicalUriQueryParams.put(
|
||||||
"uri_identity_hash", identityHashesForTypes.iterator().next());
|
"uri_identity_hash", identityHashesForTypes.iterator().next());
|
||||||
|
@ -1673,21 +1761,102 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
||||||
canonicalUriQueryParams.put("uri_identity_hashes", identityHashesForTypes);
|
canonicalUriQueryParams.put("uri_identity_hashes", identityHashesForTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
canonicalUrlQuery.append(" AND r.target_resource_url = rUri.sp_uri )" + " WHERE r.src_path = :src_path AND "
|
canonicalUrlQuery.append(" AND r.target_resource_url = rUri.sp_uri )");
|
||||||
+ " r.target_resource_id IS NULL AND "
|
canonicalUrlQuery.append(" WHERE r.src_path = :src_path AND ");
|
||||||
+ " r."
|
canonicalUrlQuery.append(" r.target_resource_id IS NULL ");
|
||||||
+ thePidFieldSqlColumn + " IN (:target_pids) ");
|
canonicalUrlQuery.append(" AND ");
|
||||||
|
if (theReverse) {
|
||||||
|
canonicalUrlQuery.append("rUri.res_id");
|
||||||
|
} else {
|
||||||
|
canonicalUrlQuery.append("r.src_resource_id");
|
||||||
|
}
|
||||||
|
canonicalUrlQuery.append(" IN (:target_pids) ");
|
||||||
|
|
||||||
return Pair.of(canonicalUrlQuery.toString(), canonicalUriQueryParams);
|
return Pair.of(canonicalUrlQuery.toString(), canonicalUriQueryParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Collection<JpaPid>> partition(Collection<JpaPid> theNextRoundMatches, int theMaxLoad) {
|
@Nonnull
|
||||||
|
Set<Long> calculateIndexUriIdentityHashesForResourceTypes(Set<String> theTargetResourceTypes, boolean theReverse) {
|
||||||
|
Set<String> targetResourceTypes = theTargetResourceTypes;
|
||||||
|
if (targetResourceTypes == null) {
|
||||||
|
/*
|
||||||
|
* If we don't have a list of valid target types, we need to figure out a list of all
|
||||||
|
* possible target types in order to perform the search of the URI index table. This is
|
||||||
|
* because the hash_identity column encodes the resource type, so we'll need a hash
|
||||||
|
* value for each possible target type.
|
||||||
|
*/
|
||||||
|
targetResourceTypes = new HashSet<>();
|
||||||
|
Set<String> possibleTypes = myDaoRegistry.getRegisteredDaoTypes();
|
||||||
|
if (theReverse) {
|
||||||
|
// For reverse includes, it is really hard to figure out what types
|
||||||
|
// are actually potentially pointing to the type we're searching for
|
||||||
|
// in this context, so let's just assume it could be anything.
|
||||||
|
targetResourceTypes = possibleTypes;
|
||||||
|
} else {
|
||||||
|
for (var next : mySearchParamRegistry.getActiveSearchParams(myResourceName).values().stream()
|
||||||
|
.filter(t -> t.getParamType().equals(RestSearchParameterTypeEnum.REFERENCE))
|
||||||
|
.collect(Collectors.toList())) {
|
||||||
|
|
||||||
|
// If the reference points to a Reference (ie not a canonical or CanonicalReference)
|
||||||
|
// then it doesn't matter here anyhow. The logic here only works for elements at the
|
||||||
|
// root level of the document (e.g. QuestionnaireResponse.subject or
|
||||||
|
// QuestionnaireResponse.subject.where(...)) but this is just an optimization
|
||||||
|
// anyhow.
|
||||||
|
if (next.getPath().startsWith(myResourceName + ".")) {
|
||||||
|
String elementName =
|
||||||
|
next.getPath().substring(next.getPath().indexOf('.') + 1);
|
||||||
|
int secondDotIndex = elementName.indexOf('.');
|
||||||
|
if (secondDotIndex != -1) {
|
||||||
|
elementName = elementName.substring(0, secondDotIndex);
|
||||||
|
}
|
||||||
|
BaseRuntimeChildDefinition child =
|
||||||
|
myContext.getResourceDefinition(myResourceName).getChildByName(elementName);
|
||||||
|
if (child != null) {
|
||||||
|
BaseRuntimeElementDefinition<?> childDef = child.getChildByName(elementName);
|
||||||
|
if (childDef != null) {
|
||||||
|
if (childDef.getName().equals("Reference")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!next.getTargets().isEmpty()) {
|
||||||
|
// For each reference parameter on the resource type we're searching for,
|
||||||
|
// add all the potential target types to the list of possible target
|
||||||
|
// resource types we can look up.
|
||||||
|
for (var nextTarget : next.getTargets()) {
|
||||||
|
if (possibleTypes.contains(nextTarget)) {
|
||||||
|
targetResourceTypes.add(nextTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we have any references that don't define any target types, then
|
||||||
|
// we need to assume that all enabled resource types are possible target
|
||||||
|
// types
|
||||||
|
targetResourceTypes.addAll(possibleTypes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert !targetResourceTypes.isEmpty();
|
||||||
|
|
||||||
|
Set<Long> identityHashesForTypes = targetResourceTypes.stream()
|
||||||
|
.map(type -> BaseResourceIndexedSearchParam.calculateHashIdentity(
|
||||||
|
myPartitionSettings, myRequestPartitionId, type, "url"))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
return identityHashesForTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> List<Collection<T>> partition(Collection<T> theNextRoundMatches, int theMaxLoad) {
|
||||||
if (theNextRoundMatches.size() <= theMaxLoad) {
|
if (theNextRoundMatches.size() <= theMaxLoad) {
|
||||||
return Collections.singletonList(theNextRoundMatches);
|
return Collections.singletonList(theNextRoundMatches);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
List<Collection<JpaPid>> retVal = new ArrayList<>();
|
List<Collection<T>> retVal = new ArrayList<>();
|
||||||
Collection<JpaPid> current = null;
|
Collection<T> current = null;
|
||||||
for (JpaPid next : theNextRoundMatches) {
|
for (T next : theNextRoundMatches) {
|
||||||
if (current == null) {
|
if (current == null) {
|
||||||
current = new ArrayList<>(theMaxLoad);
|
current = new ArrayList<>(theMaxLoad);
|
||||||
retVal.add(current);
|
retVal.add(current);
|
||||||
|
@ -2121,19 +2290,11 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendProcessingMsgAndFirePerformanceHook() {
|
private void sendProcessingMsgAndFirePerformanceHook() {
|
||||||
StorageProcessingMessage message = new StorageProcessingMessage();
|
|
||||||
String msg = "Pass completed with no matching results seeking rows "
|
String msg = "Pass completed with no matching results seeking rows "
|
||||||
+ myPidSet.size() + "-" + mySkipCount
|
+ myPidSet.size() + "-" + mySkipCount
|
||||||
+ ". This indicates an inefficient query! Retrying with new max count of "
|
+ ". This indicates an inefficient query! Retrying with new max count of "
|
||||||
+ myMaxResultsToFetch;
|
+ myMaxResultsToFetch;
|
||||||
ourLog.warn(msg);
|
firePerformanceWarning(myRequest, msg);
|
||||||
message.setMessage(msg);
|
|
||||||
HookParams params = new HookParams()
|
|
||||||
.add(RequestDetails.class, myRequest)
|
|
||||||
.addIfMatchesType(ServletRequestDetails.class, myRequest)
|
|
||||||
.add(StorageProcessingMessage.class, message);
|
|
||||||
CompositeInterceptorBroadcaster.doCallHooks(
|
|
||||||
myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeIteratorQuery(Integer theOffset, Integer theMaxResultsToFetch) {
|
private void initializeIteratorQuery(Integer theOffset, Integer theMaxResultsToFetch) {
|
||||||
|
@ -2208,6 +2369,18 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void firePerformanceWarning(RequestDetails theRequest, String theMessage) {
|
||||||
|
ourLog.warn(theMessage);
|
||||||
|
StorageProcessingMessage message = new StorageProcessingMessage();
|
||||||
|
message.setMessage(theMessage);
|
||||||
|
HookParams params = new HookParams()
|
||||||
|
.add(RequestDetails.class, theRequest)
|
||||||
|
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||||
|
.add(StorageProcessingMessage.class, message);
|
||||||
|
CompositeInterceptorBroadcaster.doCallHooks(
|
||||||
|
myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
|
||||||
|
}
|
||||||
|
|
||||||
public static int getMaximumPageSize() {
|
public static int getMaximumPageSize() {
|
||||||
if (myUseMaxPageSize50ForTest) {
|
if (myUseMaxPageSize50ForTest) {
|
||||||
return MAXIMUM_PAGE_SIZE_FOR_TESTING;
|
return MAXIMUM_PAGE_SIZE_FOR_TESTING;
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package ca.uhn.fhir.jpa.search.builder;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
|
import ca.uhn.fhir.rest.server.util.FhirContextSearchParamRegistry;
|
||||||
|
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class SearchBuilderTest {
|
||||||
|
|
||||||
|
public static final FhirContext ourCtx = FhirContext.forR4Cached();
|
||||||
|
@Spy
|
||||||
|
private FhirContext myFhirContext = ourCtx;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private ISearchParamRegistry mySearchParamRegistry = new FhirContextSearchParamRegistry(myFhirContext);
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private PartitionSettings myPartitionSettings = new PartitionSettings();
|
||||||
|
|
||||||
|
@Mock(strictness = Mock.Strictness.LENIENT)
|
||||||
|
private DaoRegistry myDaoRegistry;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private SearchBuilder mySearchBuilder;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void beforeEach() {
|
||||||
|
mySearchBuilder.myResourceName = "QuestionnaireResponse";
|
||||||
|
when(myDaoRegistry.getRegisteredDaoTypes()).thenReturn(ourCtx.getResourceTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCalculateIndexUriIdentityHashesForResourceTypes_Include_Null() {
|
||||||
|
Set<Long> types = mySearchBuilder.calculateIndexUriIdentityHashesForResourceTypes(null, false);
|
||||||
|
// There are only 12 resource types that actually can be linked to by the QuestionnaireResponse
|
||||||
|
// resource via canonical references in any parameters
|
||||||
|
assertThat(types).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCalculateIndexUriIdentityHashesForResourceTypes_Include_Nonnull() {
|
||||||
|
Set<String> inputTypes = Set.of("Questionnaire");
|
||||||
|
Set<Long> types = mySearchBuilder.calculateIndexUriIdentityHashesForResourceTypes(inputTypes, false);
|
||||||
|
// Just the one that we actually specified
|
||||||
|
assertThat(types).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCalculateIndexUriIdentityHashesForResourceTypes_RevInclude_Null() {
|
||||||
|
Set<Long> types = mySearchBuilder.calculateIndexUriIdentityHashesForResourceTypes(null, true);
|
||||||
|
// Revincludes are really hard to figure out the potential resource types for, so we just need to
|
||||||
|
// use all active resource types
|
||||||
|
assertThat(types).hasSize(146);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -228,6 +228,7 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
||||||
b.append("paramName", getParamName());
|
b.append("paramName", getParamName());
|
||||||
b.append("uri", myUri);
|
b.append("uri", myUri);
|
||||||
b.append("hashUri", myHashUri);
|
b.append("hashUri", myHashUri);
|
||||||
|
b.append("hashIdentity", myHashIdentity);
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -304,11 +304,11 @@ public class ResourceLink extends BaseResourceIndex {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append("ResourceLink[");
|
b.append("ResourceLink[");
|
||||||
b.append("path=").append(mySourcePath);
|
b.append("path=").append(mySourcePath);
|
||||||
b.append(", src=").append(mySourceResourcePid);
|
b.append(", srcResId=").append(mySourceResourcePid);
|
||||||
b.append(", target=").append(myTargetResourcePid);
|
b.append(", targetResId=").append(myTargetResourcePid);
|
||||||
b.append(", targetType=").append(myTargetResourceType);
|
b.append(", targetResType=").append(myTargetResourceType);
|
||||||
b.append(", targetVersion=").append(myTargetResourceVersion);
|
b.append(", targetResVersion=").append(myTargetResourceVersion);
|
||||||
b.append(", targetUrl=").append(myTargetResourceUrl);
|
b.append(", targetResUrl=").append(myTargetResourceUrl);
|
||||||
|
|
||||||
b.append("]");
|
b.append("]");
|
||||||
return b.toString();
|
return b.toString();
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -2161,6 +2161,12 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsed.isAbsolute()) {
|
if (parsed.isAbsolute()) {
|
||||||
|
String refValue =
|
||||||
|
fakeReference.getReferenceElement().getValue();
|
||||||
|
if (refValue.contains("|")) {
|
||||||
|
fakeReference.setReference(refValue.substring(0, refValue.indexOf('|')));
|
||||||
|
}
|
||||||
|
|
||||||
myPathAndRef = new PathAndRef(theSearchParam.getName(), thePath, fakeReference, true);
|
myPathAndRef = new PathAndRef(theSearchParam.getName(), thePath, fakeReference, true);
|
||||||
theParams.add(myPathAndRef);
|
theParams.add(myPathAndRef);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.api.dao.ReindexParameters;
|
import ca.uhn.fhir.jpa.api.dao.ReindexParameters;
|
||||||
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
|
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
|
||||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||||
|
@ -72,6 +73,8 @@ import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Practitioner;
|
import org.hl7.fhir.r4.model.Practitioner;
|
||||||
import org.hl7.fhir.r4.model.Provenance;
|
import org.hl7.fhir.r4.model.Provenance;
|
||||||
import org.hl7.fhir.r4.model.Quantity;
|
import org.hl7.fhir.r4.model.Quantity;
|
||||||
|
import org.hl7.fhir.r4.model.Questionnaire;
|
||||||
|
import org.hl7.fhir.r4.model.QuestionnaireResponse;
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
import org.hl7.fhir.r4.model.ServiceRequest;
|
import org.hl7.fhir.r4.model.ServiceRequest;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
@ -1505,6 +1508,40 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWithRevInclude() {
|
||||||
|
Questionnaire q = new Questionnaire();
|
||||||
|
q.setId("q");
|
||||||
|
q.setUrl("http://foo");
|
||||||
|
q.setVersion("1.0");
|
||||||
|
myQuestionnaireDao.update(q, mySrd);
|
||||||
|
|
||||||
|
QuestionnaireResponse qr = new QuestionnaireResponse();
|
||||||
|
qr.setId("qr");
|
||||||
|
qr.setQuestionnaire("http://foo");
|
||||||
|
myQuestionnaireResponseDao.update(qr, mySrd);
|
||||||
|
|
||||||
|
logAllResourceLinks();
|
||||||
|
|
||||||
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
|
map.add("_id", new ReferenceParam("Questionnaire/q"));
|
||||||
|
map.addRevInclude(QuestionnaireResponse.INCLUDE_QUESTIONNAIRE);
|
||||||
|
IFhirResourceDao<?> dao = myQuestionnaireDao;
|
||||||
|
dao.search(map, mySrd);
|
||||||
|
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
|
IBundleProvider outcome = dao.search(map, mySrd);
|
||||||
|
toUnqualifiedVersionlessIdValues(outcome);
|
||||||
|
assertEquals(4, myCaptureQueriesListener.countSelectQueries());
|
||||||
|
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
|
outcome = dao.search(map, mySrd);
|
||||||
|
toUnqualifiedVersionlessIdValues(outcome);
|
||||||
|
assertEquals(4, myCaptureQueriesListener.countSelectQueries());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the class javadoc before changing the counts in this test!
|
* See the class javadoc before changing the counts in this test!
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||||
|
import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor;
|
||||||
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
||||||
import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider;
|
import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
|
import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.util.SqlQuery;
|
||||||
import ca.uhn.fhir.model.api.Include;
|
import ca.uhn.fhir.model.api.Include;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.DateParam;
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
|
@ -12,9 +17,7 @@ import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||||
import org.hamcrest.Matcher;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hamcrest.Matchers;
|
|
||||||
import org.hamcrest.collection.IsIterableContainingInAnyOrder;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.BodyStructure;
|
import org.hl7.fhir.r4.model.BodyStructure;
|
||||||
import org.hl7.fhir.r4.model.CarePlan;
|
import org.hl7.fhir.r4.model.CarePlan;
|
||||||
|
@ -23,34 +26,169 @@ import org.hl7.fhir.r4.model.EpisodeOfCare;
|
||||||
import org.hl7.fhir.r4.model.Organization;
|
import org.hl7.fhir.r4.model.Organization;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Procedure;
|
import org.hl7.fhir.r4.model.Procedure;
|
||||||
|
import org.hl7.fhir.r4.model.Questionnaire;
|
||||||
|
import org.hl7.fhir.r4.model.QuestionnaireResponse;
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
import org.hl7.fhir.r4.model.SearchParameter;
|
import org.hl7.fhir.r4.model.SearchParameter;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import java.sql.Date;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Collection;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "Duplicates"})
|
@SuppressWarnings({"Duplicates"})
|
||||||
public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchIncludeTest.class);
|
|
||||||
|
@Mock
|
||||||
|
private IAnonymousInterceptor myAnonymousInterceptor;
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<HookParams> myParamsCaptor;
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void afterEach() {
|
public void afterEach() {
|
||||||
myStorageSettings.setMaximumIncludesToLoadPerPage(JpaStorageSettings.DEFAULT_MAXIMUM_INCLUDES_TO_LOAD_PER_PAGE);
|
myStorageSettings.setMaximumIncludesToLoadPerPage(JpaStorageSettings.DEFAULT_MAXIMUM_INCLUDES_TO_LOAD_PER_PAGE);
|
||||||
|
myInterceptorRegistry.unregisterInterceptor(myAnonymousInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
// theQuestionnaireRespId, theReverse, theMatchAll
|
||||||
|
"QuestionnaireResponse/qr , false, false",
|
||||||
|
"QuestionnaireResponse/qr2 , false, false",
|
||||||
|
"QuestionnaireResponse/qr , true, false",
|
||||||
|
"QuestionnaireResponse/qr2 , true, false",
|
||||||
|
"QuestionnaireResponse/qr , false, true",
|
||||||
|
"QuestionnaireResponse/qr2 , false, true",
|
||||||
|
// "QuestionnaireResponse/qr , true, true", // Not yet supported
|
||||||
|
// "QuestionnaireResponse/qr2 , true, true", // Not yet supported
|
||||||
|
})
|
||||||
|
public void testIncludeCanonicalReference(String theQuestionnaireRespId, boolean theReverse, boolean theMatchAll) {
|
||||||
|
Questionnaire qWrongVersion = new Questionnaire();
|
||||||
|
qWrongVersion.setId("qWrongVersion");
|
||||||
|
qWrongVersion.setUrl("http://foo");
|
||||||
|
qWrongVersion.setVersion("99.0");
|
||||||
|
myQuestionnaireDao.update(qWrongVersion, mySrd);
|
||||||
|
|
||||||
|
Questionnaire q = new Questionnaire();
|
||||||
|
q.setId("q");
|
||||||
|
q.setUrl("http://foo");
|
||||||
|
q.setVersion("1.0");
|
||||||
|
myQuestionnaireDao.update(q, mySrd);
|
||||||
|
|
||||||
|
if (theQuestionnaireRespId.equals("QuestionnaireResponse/qr")) {
|
||||||
|
QuestionnaireResponse qr = new QuestionnaireResponse();
|
||||||
|
qr.setId("qr");
|
||||||
|
qr.setQuestionnaire("http://foo");
|
||||||
|
myQuestionnaireResponseDao.update(qr, mySrd);
|
||||||
|
} else {
|
||||||
|
QuestionnaireResponse qr2 = new QuestionnaireResponse();
|
||||||
|
qr2.setId("qr2");
|
||||||
|
qr2.setQuestionnaire("http://foo|1.0");
|
||||||
|
myQuestionnaireResponseDao.update(qr2, mySrd);
|
||||||
|
}
|
||||||
|
|
||||||
|
logAllUriIndexes();
|
||||||
|
logAllResourceLinks();
|
||||||
|
|
||||||
|
// Create a QR and Q that have other URLs and shouldn't be turned up in searches here
|
||||||
|
Questionnaire qIrrelevant = new Questionnaire();
|
||||||
|
qIrrelevant.setId("qIrrelevant");
|
||||||
|
qIrrelevant.setUrl("http://fooIrrelevant");
|
||||||
|
qIrrelevant.setVersion("1.0");
|
||||||
|
myQuestionnaireDao.update(qIrrelevant, mySrd);
|
||||||
|
|
||||||
|
QuestionnaireResponse qrIrrelevant = new QuestionnaireResponse();
|
||||||
|
qrIrrelevant.setId("qrIrrelevant");
|
||||||
|
qrIrrelevant.setQuestionnaire("http://fooIrrelevant");
|
||||||
|
myQuestionnaireResponseDao.update(qrIrrelevant, mySrd);
|
||||||
|
|
||||||
|
|
||||||
|
IBundleProvider outcome;
|
||||||
|
IFhirResourceDao<?> dao;
|
||||||
|
SearchParameterMap map;
|
||||||
|
String expectWarning = null;
|
||||||
|
if (theReverse) {
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add("_id", new TokenParam("Questionnaire/q"));
|
||||||
|
if (theMatchAll) {
|
||||||
|
map.addRevInclude(IBaseResource.INCLUDE_ALL);
|
||||||
|
} else {
|
||||||
|
map.addRevInclude(QuestionnaireResponse.INCLUDE_QUESTIONNAIRE);
|
||||||
|
}
|
||||||
|
dao = myQuestionnaireDao;
|
||||||
|
} else {
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add("_id", new TokenParam(theQuestionnaireRespId));
|
||||||
|
if (theMatchAll) {
|
||||||
|
map.addInclude(IBaseResource.INCLUDE_ALL);
|
||||||
|
} else {
|
||||||
|
map.addInclude(QuestionnaireResponse.INCLUDE_QUESTIONNAIRE);
|
||||||
|
}
|
||||||
|
dao = myQuestionnaireResponseDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theMatchAll) {
|
||||||
|
expectWarning = "Search with _include=* can be inefficient";
|
||||||
|
}
|
||||||
|
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
|
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.JPA_PERFTRACE_WARNING, myAnonymousInterceptor);
|
||||||
|
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
|
outcome = dao.search(map, mySrd);
|
||||||
|
List<String> outcomeValues = toUnqualifiedVersionlessIdValues(outcome);
|
||||||
|
myCaptureQueriesListener.logSelectQueries();
|
||||||
|
|
||||||
|
if (theReverse) {
|
||||||
|
assertThat(outcomeValues).as(outcomeValues.toString()).containsExactlyInAnyOrder(
|
||||||
|
theQuestionnaireRespId, "Questionnaire/q"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assertThat(outcomeValues).as(outcomeValues.toString()).containsExactlyInAnyOrder(
|
||||||
|
theQuestionnaireRespId, "Questionnaire/q", "Questionnaire/qWrongVersion"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectWarning == null) {
|
||||||
|
verify(myAnonymousInterceptor, never()).invoke(eq(Pointcut.JPA_PERFTRACE_WARNING), myParamsCaptor.capture());
|
||||||
|
} else {
|
||||||
|
verify(myAnonymousInterceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_WARNING), myParamsCaptor.capture());
|
||||||
|
HookParams params = myParamsCaptor.getValue();
|
||||||
|
assertThat(params.get(StorageProcessingMessage.class).getMessage()).contains(expectWarning);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!theReverse && theMatchAll) {
|
||||||
|
SqlQuery searchForCanonicalReferencesQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(3);
|
||||||
|
// Make sure we have the right query - If this ever fails, maybe we have optimized the queries
|
||||||
|
// (or somehow made things worse) and the search for the canonical target is no longer the 4th
|
||||||
|
// SQL query
|
||||||
|
assertThat(searchForCanonicalReferencesQuery.getSql(true, false)).contains("rispu1_0.HASH_IDENTITY in ('-600769180185160063')");
|
||||||
|
assertThat(searchForCanonicalReferencesQuery.getSql(true, false)).contains("rispu1_0.SP_URI in ('http://foo')");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncludesNotAppliedToIncludedResources() {
|
public void testIncludesNotAppliedToIncludedResources() {
|
||||||
createOrganizationWithReferencingEpisodesOfCare(10);
|
createOrganizationWithReferencingEpisodesOfCare(10);
|
||||||
|
@ -59,7 +197,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
.add("_id", new TokenParam("EOC-0"))
|
.add("_id", new TokenParam("EOC-0"))
|
||||||
.addInclude(new Include("*"))
|
.addInclude(new Include("*"))
|
||||||
.addRevInclude(new Include("*").setRecurse(true));
|
.addRevInclude(new Include("*").setRecurse(true));
|
||||||
IBundleProvider results = myEpisodeOfCareDao.search(map);
|
IBundleProvider results = myEpisodeOfCareDao.search(map, mySrd);
|
||||||
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "Organization/ORG-0");
|
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "Organization/ORG-0");
|
||||||
}
|
}
|
||||||
|
@ -72,7 +210,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
.setCount(10)
|
.setCount(10)
|
||||||
.addInclude(new Include("*"))
|
.addInclude(new Include("*"))
|
||||||
.addRevInclude(new Include("*").setRecurse(true));
|
.addRevInclude(new Include("*").setRecurse(true));
|
||||||
IBundleProvider results = myOrganizationDao.search(map);
|
IBundleProvider results = myOrganizationDao.search(map, mySrd);
|
||||||
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
|
|
||||||
List<String> expected = IntStream.range(0, 10)
|
List<String> expected = IntStream.range(0, 10)
|
||||||
|
@ -97,7 +235,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
.add("_id", new TokenParam("ORG-0"))
|
.add("_id", new TokenParam("ORG-0"))
|
||||||
.addInclude(new Include("*"))
|
.addInclude(new Include("*"))
|
||||||
.addRevInclude(new Include("*").setRecurse(true));
|
.addRevInclude(new Include("*").setRecurse(true));
|
||||||
IBundleProvider results = myOrganizationDao.search(map);
|
IBundleProvider results = myOrganizationDao.search(map, mySrd);
|
||||||
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "EpisodeOfCare/EOC-1", "EpisodeOfCare/EOC-2", "EpisodeOfCare/EOC-3", "EpisodeOfCare/EOC-4", "Organization/ORG-0");
|
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "EpisodeOfCare/EOC-1", "EpisodeOfCare/EOC-2", "EpisodeOfCare/EOC-3", "EpisodeOfCare/EOC-4", "Organization/ORG-0");
|
||||||
}
|
}
|
||||||
|
@ -110,7 +248,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
SearchParameterMap map = SearchParameterMap.newSynchronous()
|
SearchParameterMap map = SearchParameterMap.newSynchronous()
|
||||||
.addInclude(new Include("CarePlan.patient"));
|
.addInclude(new Include("CarePlan.patient"));
|
||||||
try {
|
try {
|
||||||
myCarePlanDao.search(map);
|
myCarePlanDao.search(map, mySrd);
|
||||||
fail();
|
fail();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// good
|
// good
|
||||||
|
@ -120,7 +258,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
SearchParameterMap map2 = SearchParameterMap.newSynchronous()
|
SearchParameterMap map2 = SearchParameterMap.newSynchronous()
|
||||||
.addInclude(new Include("CarePlan:patient"));
|
.addInclude(new Include("CarePlan:patient"));
|
||||||
try {
|
try {
|
||||||
IBundleProvider results = myCarePlanDao.search(map2);
|
IBundleProvider results = myCarePlanDao.search(map2, mySrd);
|
||||||
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("CarePlan/CP-1", "Patient/PAT-1");
|
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("CarePlan/CP-1", "Patient/PAT-1");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -161,7 +299,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
logAllResources();
|
logAllResources();
|
||||||
logAllResourceLinks();
|
logAllResourceLinks();
|
||||||
|
|
||||||
// Non synchronous
|
// Non-synchronous
|
||||||
SearchParameterMap map = new SearchParameterMap();
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
map.add("_id", new TokenParam("PRA8780542726"));
|
map.add("_id", new TokenParam("PRA8780542726"));
|
||||||
map.addRevInclude(new Include("Procedure:part-of"));
|
map.addRevInclude(new Include("Procedure:part-of"));
|
||||||
|
@ -194,7 +332,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
.setCount(10)
|
.setCount(10)
|
||||||
.addInclude(new Include("*"))
|
.addInclude(new Include("*"))
|
||||||
.addRevInclude(new Include("*").setRecurse(true));
|
.addRevInclude(new Include("*").setRecurse(true));
|
||||||
IBundleProvider results = myOrganizationDao.search(map);
|
IBundleProvider results = myOrganizationDao.search(map, mySrd);
|
||||||
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "EpisodeOfCare/EOC-1", "EpisodeOfCare/EOC-2", "EpisodeOfCare/EOC-3", "Organization/ORG-0", "Organization/ORG-P");
|
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "EpisodeOfCare/EOC-1", "EpisodeOfCare/EOC-2", "EpisodeOfCare/EOC-3", "Organization/ORG-0", "Organization/ORG-P");
|
||||||
|
|
||||||
|
@ -210,7 +348,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
.add("_id", new TokenParam("ORG-0"))
|
.add("_id", new TokenParam("ORG-0"))
|
||||||
.addRevInclude(EpisodeOfCare.INCLUDE_ORGANIZATION);
|
.addRevInclude(EpisodeOfCare.INCLUDE_ORGANIZATION);
|
||||||
myCaptureQueriesListener.clear();
|
myCaptureQueriesListener.clear();
|
||||||
IBundleProvider results = myOrganizationDao.search(map);
|
IBundleProvider results = myOrganizationDao.search(map, mySrd);
|
||||||
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
myCaptureQueriesListener.logSelectQueries();
|
myCaptureQueriesListener.logSelectQueries();
|
||||||
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "EpisodeOfCare/EOC-1", "EpisodeOfCare/EOC-2", "EpisodeOfCare/EOC-3", "EpisodeOfCare/EOC-4", "EpisodeOfCare/EOC-5", "EpisodeOfCare/EOC-6", "EpisodeOfCare/EOC-7", "EpisodeOfCare/EOC-8", "EpisodeOfCare/EOC-9", "Organization/ORG-0");
|
assertThat(ids).as(ids.toString()).containsExactlyInAnyOrder("EpisodeOfCare/EOC-0", "EpisodeOfCare/EOC-1", "EpisodeOfCare/EOC-2", "EpisodeOfCare/EOC-3", "EpisodeOfCare/EOC-4", "EpisodeOfCare/EOC-5", "EpisodeOfCare/EOC-6", "EpisodeOfCare/EOC-7", "EpisodeOfCare/EOC-8", "EpisodeOfCare/EOC-9", "Organization/ORG-0");
|
||||||
|
@ -223,38 +361,40 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setId("Organization/ORG-P");
|
org.setId("Organization/ORG-P");
|
||||||
org.setName("ORG-P");
|
org.setName("ORG-P");
|
||||||
myOrganizationDao.update(org);
|
myOrganizationDao.update(org, mySrd);
|
||||||
|
|
||||||
org = new Organization();
|
org = new Organization();
|
||||||
org.setId("Organization/ORG-0");
|
org.setId("Organization/ORG-0");
|
||||||
org.setName("ORG-0");
|
org.setName("ORG-0");
|
||||||
org.setPartOf(new Reference("Organization/ORG-P"));
|
org.setPartOf(new Reference("Organization/ORG-P"));
|
||||||
myOrganizationDao.update(org);
|
myOrganizationDao.update(org, mySrd);
|
||||||
|
|
||||||
for (int i = 0; i < theEocCount; i++) {
|
for (int i = 0; i < theEocCount; i++) {
|
||||||
EpisodeOfCare eoc = new EpisodeOfCare();
|
EpisodeOfCare eoc = new EpisodeOfCare();
|
||||||
eoc.setId("EpisodeOfCare/EOC-" + i);
|
eoc.setId("EpisodeOfCare/EOC-" + i);
|
||||||
eoc.getManagingOrganization().setReference("Organization/ORG-0");
|
eoc.getManagingOrganization().setReference("Organization/ORG-0");
|
||||||
myEpisodeOfCareDao.update(eoc);
|
myEpisodeOfCareDao.update(eoc, mySrd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
private void createPatientWithReferencingCarePlan(int theCount) {
|
private void createPatientWithReferencingCarePlan(int theCount) {
|
||||||
org.hl7.fhir.r4.model.Patient patient = new Patient();
|
org.hl7.fhir.r4.model.Patient patient = new Patient();
|
||||||
patient.setId("Patient/PAT-1");
|
patient.setId("Patient/PAT-1");
|
||||||
myPatientDao.update(patient);
|
myPatientDao.update(patient, mySrd);
|
||||||
|
|
||||||
for (int i = 1; i <= theCount; i++) {
|
for (int i = 1; i <= theCount; i++) {
|
||||||
CarePlan carePlan = new CarePlan();
|
CarePlan carePlan = new CarePlan();
|
||||||
carePlan.setId("CarePlan/CP-" + i);
|
carePlan.setId("CarePlan/CP-" + i);
|
||||||
carePlan.getSubject().setReference("Patient/PAT-1");
|
carePlan.getSubject().setReference("Patient/PAT-1");
|
||||||
myCarePlanDao.update(carePlan);
|
myCarePlanDao.update(carePlan, mySrd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://github.com/hapifhir/hapi-fhir/issues/4896
|
* <a href="https://github.com/hapifhir/hapi-fhir/issues/4896">#4896</a>
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("DataFlowIssue")
|
||||||
@Test
|
@Test
|
||||||
void testLastUpdatedDoesNotApplyToForwardOrRevIncludes() {
|
void testLastUpdatedDoesNotApplyToForwardOrRevIncludes() {
|
||||||
// given
|
// given
|
||||||
|
@ -277,7 +417,7 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
|
||||||
// when
|
// when
|
||||||
// "Patient?_lastUpdated=gt2023-01-01&_revinclude=Group:member&_revinclude=CareTeam:subject&_include=Patient:organization");
|
// "Patient?_lastUpdated=gt2023-01-01&_revinclude=Group:member&_revinclude=CareTeam:subject&_include=Patient:organization");
|
||||||
SearchParameterMap map = new SearchParameterMap();
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, Date.from(now))));
|
map.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, java.util.Date.from(now))));
|
||||||
map.addInclude(new Include("Patient:organization"));
|
map.addInclude(new Include("Patient:organization"));
|
||||||
map.addRevInclude(new Include("Group:member"));
|
map.addRevInclude(new Include("Group:member"));
|
||||||
map.addRevInclude(new Include("CareTeam:subject"));
|
map.addRevInclude(new Include("CareTeam:subject"));
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
|
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-spring-boot</artifactId>
|
<artifactId>hapi-fhir-spring-boot</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
2
pom.xml
2
pom.xml
|
@ -8,7 +8,7 @@
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>HAPI-FHIR</name>
|
<name>HAPI-FHIR</name>
|
||||||
<description>An open-source implementation of the FHIR specification in Java.</description>
|
<description>An open-source implementation of the FHIR specification in Java.</description>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>7.3.6-SNAPSHOT</version>
|
<version>7.3.7-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
Loading…
Reference in New Issue