Multitenancy work
This commit is contained in:
parent
e55ccf88fc
commit
c26a5553e9
|
@ -65,12 +65,16 @@ public enum Pointcut {
|
|||
* <li>
|
||||
* ca.uhn.fhir.rest.client.api.IHttpRequest - The details of the request
|
||||
* </li>
|
||||
* <li>
|
||||
* ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request
|
||||
* </li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* Hook methods must return <code>void</code>.
|
||||
*/
|
||||
CLIENT_REQUEST(void.class,
|
||||
"ca.uhn.fhir.rest.client.api.IHttpRequest"
|
||||
"ca.uhn.fhir.rest.client.api.IHttpRequest",
|
||||
"ca.uhn.fhir.rest.client.api.IRestfulClient"
|
||||
),
|
||||
|
||||
/**
|
||||
|
@ -82,7 +86,12 @@ public enum Pointcut {
|
|||
* <ul>
|
||||
* <li>
|
||||
* ca.uhn.fhir.rest.client.api.IHttpRequest - The details of the request
|
||||
* ca.uhn.fhir.rest.client.api.IHttpRequest - The details of the response
|
||||
* </li>
|
||||
* <li>
|
||||
* ca.uhn.fhir.rest.client.api.IHttpResponse - The details of the response
|
||||
* </li>
|
||||
* <li>
|
||||
* ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request
|
||||
* </li>
|
||||
* </ul>
|
||||
* </p>
|
||||
|
@ -90,7 +99,8 @@ public enum Pointcut {
|
|||
*/
|
||||
CLIENT_RESPONSE(void.class,
|
||||
"ca.uhn.fhir.rest.client.api.IHttpRequest",
|
||||
"ca.uhn.fhir.rest.client.api.IHttpResponse"
|
||||
"ca.uhn.fhir.rest.client.api.IHttpResponse",
|
||||
"ca.uhn.fhir.rest.client.api.IRestfulClient"
|
||||
),
|
||||
|
||||
/**
|
||||
|
|
|
@ -63,6 +63,11 @@ public interface IHttpRequest {
|
|||
*/
|
||||
String getUri();
|
||||
|
||||
/**
|
||||
* Modify the request URI, or null
|
||||
*/
|
||||
void setUri(String theUrl);
|
||||
|
||||
/**
|
||||
* Return the HTTP verb (e.g. "GET")
|
||||
*/
|
||||
|
|
|
@ -22,16 +22,17 @@ package ca.uhn.fhir.rest.param;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static ca.uhn.fhir.model.primitive.IdDt.isValidLong;
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/ {
|
||||
|
||||
|
@ -78,6 +79,17 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/
|
|||
setValueAsQueryToken(null, null, qualifier, theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public ReferenceParam(IIdType theValue) {
|
||||
if (theValue != null) {
|
||||
setValueAsQueryToken(null, null, null, theValue.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String doGetQueryParameterQualifier() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
@ -209,7 +221,7 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/
|
|||
*/
|
||||
public ReferenceParam setValue(String theValue) {
|
||||
IdDt id = new IdDt(theValue);
|
||||
String qualifier= null;
|
||||
String qualifier = null;
|
||||
if (id.hasResourceType()) {
|
||||
qualifier = ":" + id.getResourceType();
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionContainsMultipleWithDuplica
|
|||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionEntryHasInvalidVerb=Transaction bundle entry has missing or invalid HTTP Verb specified in Bundle.entry({1}).request.method. Found value: "{0}"
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionMissingUrl=Unable to perform {0}, no URL provided.
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1}
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.noSystemOrTypeHistoryForPartitionAwareServer=Type- and Server- level history operation not supported on partitioned server
|
||||
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.cantValidateWithNoResource=No resource supplied for $validate operation (resource is required unless mode is \"delete\")
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.deleteBlockedBecauseDisabled=Resource deletion is not permitted on this server
|
||||
|
@ -155,5 +156,5 @@ ca.uhn.fhir.jpa.partition.PartitionConfigSvcImpl.cantCreateDuplicatePartitionNam
|
|||
ca.uhn.fhir.jpa.partition.PartitionConfigSvcImpl.cantDeleteDefaultPartition=Can not delete default partition
|
||||
ca.uhn.fhir.jpa.partition.PartitionConfigSvcImpl.cantRenameDefaultPartition=Can not rename default partition
|
||||
|
||||
ca.uhn.fhir.jpa.partition.RequestTenantSelectingInterceptor.unknownTenantName=Unknown tenant: {0}
|
||||
ca.uhn.fhir.jpa.partition.RequestTenantPartitionInterceptor.unknownTenantName=Unknown tenant: {0}
|
||||
|
||||
|
|
|
@ -89,7 +89,12 @@ public class OkHttpRestfulRequest implements IHttpRequest {
|
|||
return myUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void setUri(String theUrl) {
|
||||
myUrl = theUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHttpVerbName() {
|
||||
return myRequestTypeEnum.name();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.apache.http.client.methods.HttpRequestBase;
|
|||
import org.apache.http.entity.ContentType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -110,6 +111,11 @@ public class ApacheHttpRequest implements IHttpRequest {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUri(String theUrl) {
|
||||
myRequest.setURI(URI.create(theUrl));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUri() {
|
||||
return myRequest.getURI().toString();
|
||||
|
|
|
@ -297,6 +297,7 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
|
||||
HookParams requestParams = new HookParams();
|
||||
requestParams.add(IHttpRequest.class, httpRequest);
|
||||
requestParams.add(IRestfulClient.class, this);
|
||||
getInterceptorService().callHooks(Pointcut.CLIENT_REQUEST, requestParams);
|
||||
|
||||
response = httpRequest.execute();
|
||||
|
@ -304,6 +305,7 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
HookParams responseParams = new HookParams();
|
||||
responseParams.add(IHttpRequest.class, httpRequest);
|
||||
responseParams.add(IHttpResponse.class, response);
|
||||
responseParams.add(IRestfulClient.class, this);
|
||||
getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams);
|
||||
|
||||
String mimeType;
|
||||
|
|
|
@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client.interceptor;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpResponse;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -35,7 +37,8 @@ import java.io.IOException;
|
|||
*
|
||||
* @see ThreadLocalCapturingInterceptor for an interceptor that uses a ThreadLocal in order to work in multithreaded environments
|
||||
*/
|
||||
public class CapturingInterceptor implements IClientInterceptor {
|
||||
@Interceptor
|
||||
public class CapturingInterceptor {
|
||||
|
||||
private IHttpRequest myLastRequest;
|
||||
private IHttpResponse myLastResponse;
|
||||
|
@ -56,12 +59,12 @@ public class CapturingInterceptor implements IClientInterceptor {
|
|||
return myLastResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Hook(value = Pointcut.CLIENT_REQUEST, order = InterceptorOrders.CAPTURING_INTERCEPTOR_REQUEST)
|
||||
public void interceptRequest(IHttpRequest theRequest) {
|
||||
myLastRequest = theRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Hook(value = Pointcut.CLIENT_RESPONSE, order = InterceptorOrders.CAPTURING_INTERCEPTOR_RESPONSE)
|
||||
public void interceptResponse(IHttpResponse theResponse) {
|
||||
//Buffer the reponse to avoid errors when content has already been read and the entity is not repeatable
|
||||
bufferResponse(theResponse);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package ca.uhn.fhir.rest.client.interceptor;
|
||||
|
||||
public interface InterceptorOrders {
|
||||
|
||||
int LOGGING_INTERCEPTOR_REQUEST = -2;
|
||||
int URL_TENANT_SELECTION_INTERCEPTOR_REQUEST = 100;
|
||||
int CAPTURING_INTERCEPTOR_REQUEST = 1000;
|
||||
|
||||
int CAPTURING_INTERCEPTOR_RESPONSE = -1;
|
||||
int LOGGING_INTERCEPTOR_RESPONSE = 1001;
|
||||
}
|
|
@ -20,6 +20,19 @@ package ca.uhn.fhir.rest.client.interceptor;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpResponse;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -27,20 +40,6 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpResponse;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
@Interceptor
|
||||
public class LoggingInterceptor implements IClientInterceptor {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoggingInterceptor.class);
|
||||
|
@ -62,9 +61,8 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||
|
||||
/**
|
||||
* Constructor for client logging interceptor
|
||||
*
|
||||
* @param theVerbose
|
||||
* If set to true, all logging is enabled
|
||||
*
|
||||
* @param theVerbose If set to true, all logging is enabled
|
||||
*/
|
||||
public LoggingInterceptor(boolean theVerbose) {
|
||||
if (theVerbose) {
|
||||
|
@ -78,7 +76,7 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Hook(Pointcut.CLIENT_REQUEST)
|
||||
@Hook(value = Pointcut.CLIENT_REQUEST, order = InterceptorOrders.LOGGING_INTERCEPTOR_RESPONSE)
|
||||
public void interceptRequest(IHttpRequest theRequest) {
|
||||
if (myLogRequestSummary) {
|
||||
myLog.info("Client request: {}", theRequest);
|
||||
|
@ -102,7 +100,7 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Hook(Pointcut.CLIENT_RESPONSE)
|
||||
@Hook(value = Pointcut.CLIENT_RESPONSE, order = InterceptorOrders.LOGGING_INTERCEPTOR_REQUEST)
|
||||
public void interceptResponse(IHttpResponse theResponse) throws IOException {
|
||||
if (myLogResponseSummary) {
|
||||
String message = "HTTP " + theResponse.getStatus() + " " + theResponse.getStatusInfo();
|
||||
|
@ -167,18 +165,18 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||
StringBuilder b = new StringBuilder();
|
||||
if (theHeaders != null && !theHeaders.isEmpty()) {
|
||||
Iterator<String> nameEntries = theHeaders.keySet().iterator();
|
||||
while(nameEntries.hasNext()) {
|
||||
while (nameEntries.hasNext()) {
|
||||
String key = nameEntries.next();
|
||||
Iterator<String> values = theHeaders.get(key).iterator();
|
||||
while(values.hasNext()) {
|
||||
while (values.hasNext()) {
|
||||
String value = values.next();
|
||||
b.append(key);
|
||||
b.append(": ");
|
||||
b.append(value);
|
||||
if (nameEntries.hasNext() || values.hasNext()) {
|
||||
b.append('\n');
|
||||
}
|
||||
b.append(key);
|
||||
b.append(": ");
|
||||
b.append(value);
|
||||
if (nameEntries.hasNext() || values.hasNext()) {
|
||||
b.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return b;
|
||||
|
@ -187,9 +185,8 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||
/**
|
||||
* Sets a logger to use to log messages (default is a logger with this class' name). This can be used to redirect
|
||||
* logs to a differently named logger instead.
|
||||
*
|
||||
* @param theLogger
|
||||
* The logger to use. Must not be null.
|
||||
*
|
||||
* @param theLogger The logger to use. Must not be null.
|
||||
*/
|
||||
public void setLogger(Logger theLogger) {
|
||||
Validate.notNull(theLogger, "theLogger can not be null");
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package ca.uhn.fhir.rest.client.interceptor;
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
/**
|
||||
* This interceptor adds a path element representing the tenant ID to each client request. It is primarily
|
||||
* intended to be used with clients that are accessing servers using
|
||||
* <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/multitenancy.html#url-base-multitenancy">URL Base Multitenancy</a>.
|
||||
*/
|
||||
public class UrlTenantSelectionInterceptor {
|
||||
|
||||
private String myTenantId;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public UrlTenantSelectionInterceptor() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theTenantId The tenant ID to add to URL base
|
||||
*/
|
||||
public UrlTenantSelectionInterceptor(String theTenantId) {
|
||||
myTenantId = theTenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tenant ID
|
||||
*/
|
||||
public String getTenantId() {
|
||||
return myTenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tenant ID
|
||||
*/
|
||||
public void setTenantId(String theTenantId) {
|
||||
myTenantId = theTenantId;
|
||||
}
|
||||
|
||||
@Hook(value = Pointcut.CLIENT_REQUEST, order = InterceptorOrders.URL_TENANT_SELECTION_INTERCEPTOR_REQUEST)
|
||||
public void request(IRestfulClient theClient, IHttpRequest theRequest) {
|
||||
String tenantId = getTenantId();
|
||||
if (isBlank(tenantId)) {
|
||||
return;
|
||||
}
|
||||
String requestUri = theRequest.getUri();
|
||||
String serverBase = theClient.getServerBase();
|
||||
Validate.isTrue(requestUri.startsWith(serverBase), "Request URI %s does not start with server base %s", requestUri, serverBase);
|
||||
|
||||
String newUri = serverBase + "/" + tenantId + requestUri.substring(serverBase.length());
|
||||
theRequest.setUri(newUri);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: add
|
||||
issue: 1783
|
||||
title: "The client interceptor pointcuts `CLIENT_REQUEST` and `CLIENT_RESPONSE` now allow a parameter of type `IRestfulClient` to be
|
||||
injected, containing a reference to the client making the request. In addition, CLIENT_REQUEST interceptors are now able to
|
||||
modify the URL of the request before it is performed."
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: add
|
||||
issue: 1783
|
||||
title: "A new client interceptor called UrlTenantSelectionInterceptor has been added. This interceptor allows the dynamic
|
||||
selection of a tenant ID on servers that are using URL Base Tenant Selection."
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: add
|
||||
issue: 1783
|
||||
title: "In the JPA server, the ModelConfig setting 'DefaultSearchParamsCanBeOverridden' now has a default value of
|
||||
*true* (previously this was *false*). In addition, when creating/updating a SearchParameter resource, the system
|
||||
will now raise an error if the client is attempting to override a built-in SearchParameter when this setting
|
||||
is disabled (previously this was silently ignored)."
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
<ul>
|
||||
<li>Hibernate ORM (JPA): 5.4.6 -> 5.4.12</li>
|
||||
<li>Hibernate Search (JPA): 5.11.3 -> 5.11.5</li>
|
||||
<li>Hibernate Validator (JPA): 5.4.2.Final -> 6.1.3.Final</li>
|
||||
<li>Guava (JPA): 28.0 -> 28.2</li>
|
||||
</ul>"
|
||||
- item:
|
||||
|
|
|
@ -75,6 +75,14 @@ The following example shows how to configure your client to inject a bearer toke
|
|||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/ClientExamples.java|cookie}}
|
||||
```
|
||||
|
||||
# Multitenancy: Add tenant ID to path
|
||||
|
||||
When communicating with a server that supports [URL Base Multitenancy](/docs/server_plain/multitenancy.html#url-base-multitenancy), an extra element needs to be added to the request path. This can be done by simply appending the path to the base URL supplied to the client, but it can also be dynamically appended using this interceptor.
|
||||
|
||||
* [UrlTenantSelectionInterceptor JavaDoc](/apidocs/hapi-fhir-client/ca/uhn/fhir/rest/client/interceptor/UrlTenantSelectionInterceptor.html)
|
||||
* [UrlTenantSelectionInterceptor Source](https://github.com/jamesagnew/hapi-fhir/blob/master/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/UrlTenantSelectionInterceptor.java)
|
||||
|
||||
|
||||
# Performance: GZip Outgoing Request Bodies
|
||||
|
||||
The GZipContentInterceptor compresses outgoing contents. With this interceptor, if the client is transmitting resources to the server (e.g. for a create, update, transaction, etc.) the content will be GZipped before transmission to the server.
|
||||
|
|
|
@ -6,6 +6,8 @@ Partitioning is a relatively new feature in HAPI FHIR and has a number of known
|
|||
|
||||
None of the limitations listed here are considered permanent. Over time the HAPI FHIR team are hoping to make all of these features partition aware.
|
||||
|
||||
* **Server Capability Statement is not partition aware**: The server creates and exposes a single server capability statement, covering all partitions. This can be misleading when partitioning us used as a multitenancy strategy.
|
||||
|
||||
* **Subscriptions may not be partitioned**: All subscriptions must be placed in the default partition, and subscribers will receive deliveries for any matching resources from all partitions.
|
||||
|
||||
* **Conformance resources may not be partitioned**: The following resources must be placed in the default partition, and will be shared for any validation activities across all partitions:
|
||||
|
|
|
@ -4,7 +4,7 @@ If you wish to allow a single endpoint to support multiple tenants, you may supp
|
|||
|
||||
This means that additional logic will be performed during request parsing to determine a tenant ID, which will be supplied to resource providers. This can be useful in servers that have multiple distinct logical pools of resources hosted on the same infrastructure.
|
||||
|
||||
## URL Base Multitenancy
|
||||
# URL Base Multitenancy
|
||||
|
||||
Using URL Base Multitenancy means that an additional element is added to the path of each resource between the server base URL and the resource name. For example, if your restful server is deployed to `http://acme.org:8080/baseDstu3` and a client wishes to access Patient 123 for Tenant "FOO", the resource ID (and URL to fetch that resource) would be `http://acme.org:8080/FOO/Patient/123`.
|
||||
|
||||
|
|
|
@ -119,4 +119,9 @@ public class JaxRsHttpRequest implements IHttpRequest {
|
|||
return ""; // TODO: can we get this from somewhere?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUri(String theUrl) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -187,10 +187,6 @@ public class DaoConfig {
|
|||
*/
|
||||
private boolean myDeleteEnabled = true;
|
||||
|
||||
/**
|
||||
* @since 5.0.0
|
||||
*/
|
||||
private boolean myPartitioningEnabled;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -1918,24 +1914,6 @@ public class DaoConfig {
|
|||
return myDeleteEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled (default is <code>false</code>) the JPA server will support data partitioning
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public void setPartitioningEnabled(boolean theMultiTenancyEnabled) {
|
||||
myPartitioningEnabled = theMultiTenancyEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled (default is <code>false</code>) the JPA server will support data partitioning
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public boolean isPartitioningEnabled() {
|
||||
return myPartitioningEnabled;
|
||||
}
|
||||
|
||||
public enum StoreMetaSourceInformationEnum {
|
||||
NONE(false, false),
|
||||
SOURCE_URI(true, false),
|
||||
|
|
|
@ -11,7 +11,9 @@ import ca.uhn.fhir.jpa.bulk.BulkDataExportProvider;
|
|||
import ca.uhn.fhir.jpa.bulk.BulkDataExportSvcImpl;
|
||||
import ca.uhn.fhir.jpa.bulk.IBulkDataExportSvc;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.partition.IPartitionConfigSvc;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.partition.PartitionConfigSvcImpl;
|
||||
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
||||
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperService;
|
||||
|
@ -19,6 +21,7 @@ import ca.uhn.fhir.jpa.entity.Search;
|
|||
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices;
|
||||
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
|
||||
import ca.uhn.fhir.jpa.partition.RequestTenantPartitionInterceptor;
|
||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory;
|
||||
|
@ -202,7 +205,7 @@ public abstract class BaseConfig {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public RequestPartitionHelperService requestPartitionHelperService() {
|
||||
public IRequestPartitionHelperService requestPartitionHelperService() {
|
||||
return new RequestPartitionHelperService();
|
||||
}
|
||||
|
||||
|
@ -228,6 +231,11 @@ public abstract class BaseConfig {
|
|||
return new JpaConsentContextServices();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PartitionConfig partitionConfig() {
|
||||
return new PartitionConfig();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public IPartitionConfigSvc partitionConfigSvc() {
|
||||
|
@ -240,6 +248,12 @@ public abstract class BaseConfig {
|
|||
return new PartitionManagementProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public RequestTenantPartitionInterceptor requestTenantPartitionInterceptor() {
|
||||
return new RequestTenantPartitionInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public TerminologyUploaderProvider terminologyUploaderProvider() {
|
||||
|
|
|
@ -17,6 +17,7 @@ import ca.uhn.fhir.jpa.delete.DeleteConflictService;
|
|||
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
|
@ -177,6 +178,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
private SearchBuilderFactory mySearchBuilderFactory;
|
||||
private FhirContext myContext;
|
||||
private ApplicationContext myApplicationContext;
|
||||
@Autowired
|
||||
private PartitionConfig myPartitionConfig;
|
||||
|
||||
@Override
|
||||
protected IInterceptorBroadcaster getInterceptorBroadcaster() {
|
||||
|
@ -983,7 +986,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
if (thePerformIndexing) {
|
||||
|
||||
newParams = new ResourceIndexedSearchParams();
|
||||
mySearchParamWithInlineReferencesExtractor.populateFromResource(newParams, theUpdateTime, entity, theResource, existingParams, theRequest);
|
||||
mySearchParamWithInlineReferencesExtractor.populateFromResource(myPartitionConfig, newParams, theUpdateTime, entity, theResource, existingParams, theRequest);
|
||||
|
||||
changed = populateResourceIntoEntity(theRequest, theResource, entity, true);
|
||||
if (changed.isChanged()) {
|
||||
|
|
|
@ -32,8 +32,8 @@ import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
|
|||
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
|
||||
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseTag;
|
||||
|
@ -45,6 +45,7 @@ import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
|||
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||
|
@ -144,7 +145,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
private String myResourceName;
|
||||
private Class<T> myResourceType;
|
||||
@Autowired
|
||||
private RequestPartitionHelperService myRequestPartitionHelperService;
|
||||
private IRequestPartitionHelperService myRequestPartitionHelperService;
|
||||
@Autowired
|
||||
private PartitionConfig myPartitionConfig;
|
||||
|
||||
@Override
|
||||
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel, RequestDetails theRequest) {
|
||||
|
@ -686,6 +689,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
@Override
|
||||
public IBundleProvider history(Date theSince, Date theUntil, RequestDetails theRequestDetails) {
|
||||
if (myPartitionConfig.isPartitioningEnabled()) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "noSystemOrTypeHistoryForPartitionAwareServer");
|
||||
throw new MethodNotAllowedException(msg);
|
||||
}
|
||||
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails);
|
||||
notifyInterceptors(RestOperationTypeEnum.HISTORY_TYPE, requestDetails);
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -47,6 +50,8 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
@Autowired
|
||||
@Qualifier("myResourceCountsCache")
|
||||
public ResourceCountCache myResourceCountsCache;
|
||||
@Autowired
|
||||
private PartitionConfig myPartitionConfig;
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
|
@ -76,6 +81,11 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
|
||||
@Override
|
||||
public IBundleProvider history(Date theSince, Date theUntil, RequestDetails theRequestDetails) {
|
||||
if (myPartitionConfig.isPartitioningEnabled()) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "noSystemOrTypeHistoryForPartitionAwareServer");
|
||||
throw new MethodNotAllowedException(msg);
|
||||
}
|
||||
|
||||
if (theRequestDetails != null) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails);
|
||||
|
|
|
@ -23,10 +23,10 @@ package ca.uhn.fhir.jpa.dao;
|
|||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
|
@ -42,7 +42,10 @@ import org.apache.commons.lang3.Validate;
|
|||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.highlight.Formatter;
|
||||
import org.apache.lucene.search.highlight.*;
|
||||
import org.apache.lucene.search.highlight.Highlighter;
|
||||
import org.apache.lucene.search.highlight.QueryScorer;
|
||||
import org.apache.lucene.search.highlight.Scorer;
|
||||
import org.apache.lucene.search.highlight.TokenGroup;
|
||||
import org.hibernate.search.jpa.FullTextEntityManager;
|
||||
import org.hibernate.search.jpa.FullTextQuery;
|
||||
import org.hibernate.search.query.dsl.BooleanJunction;
|
||||
|
@ -56,7 +59,12 @@ import org.springframework.transaction.support.TransactionTemplate;
|
|||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
@ -273,7 +281,7 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
|
|||
}
|
||||
|
||||
@Autowired
|
||||
private RequestPartitionHelperService myRequestPartitionHelperService;
|
||||
private IRequestPartitionHelperService myRequestPartitionHelperService;
|
||||
|
||||
@Transactional()
|
||||
@Override
|
||||
|
|
|
@ -39,6 +39,7 @@ import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
|
|||
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
|
@ -169,6 +170,8 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private Set<ResourcePersistentId> myPidSet;
|
||||
private PredicateBuilder myPredicateBuilder;
|
||||
private PartitionId myPartitionId;
|
||||
@Autowired
|
||||
private PartitionConfig myPartitionConfig;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -483,7 +486,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
Predicate joinParam1 = theBuilder.equal(join.get("myParamName"), theSort.getParamName());
|
||||
theQueryRoot.addPredicate(joinParam1);
|
||||
} else {
|
||||
Long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(myResourceName, theSort.getParamName());
|
||||
Long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(myPartitionConfig, myPartitionId, myResourceName, theSort.getParamName());
|
||||
Predicate joinParam1 = theBuilder.equal(join.get("myHashIdentity"), hashIdentity);
|
||||
theQueryRoot.addPredicate(joinParam1);
|
||||
}
|
||||
|
|
|
@ -59,6 +59,20 @@ public interface IForcedIdDao extends JpaRepository<ForcedId, Long> {
|
|||
@Query("SELECT f.myForcedId, f.myResourcePid FROM ForcedId f WHERE myResourceType = :resource_type AND myForcedId IN ( :forced_id )")
|
||||
Collection<Object[]> findByTypeAndForcedId(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedId);
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("SELECT f.myForcedId, f.myResourcePid FROM ForcedId f WHERE myPartitionIdValue = :partition_id AND myResourceType = :resource_type AND myForcedId IN ( :forced_id )")
|
||||
Collection<Object[]> findByTypeAndForcedIdInPartition(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedId, @Param("partition_id") Integer thePartitionId);
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("SELECT f.myForcedId, f.myResourcePid FROM ForcedId f WHERE myPartitionIdValue IS NULL AND myResourceType = :resource_type AND myForcedId IN ( :forced_id )")
|
||||
Collection<Object[]> findByTypeAndForcedIdInPartitionNull(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedId);
|
||||
|
||||
/**
|
||||
* Warning: No DB index exists for this particular query, so it may not perform well
|
||||
*
|
||||
|
@ -84,4 +98,31 @@ public interface IForcedIdDao extends JpaRepository<ForcedId, Long> {
|
|||
"JOIN ResourceTable t ON t.myId = f.myResourcePid " +
|
||||
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id )")
|
||||
Collection<Object[]> findAndResolveByForcedIdWithNoType(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedIds);
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("" +
|
||||
"SELECT " +
|
||||
" f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " +
|
||||
"FROM ForcedId f " +
|
||||
"JOIN ResourceTable t ON t.myId = f.myResourcePid " +
|
||||
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND f.myPartitionIdValue = :partition_id")
|
||||
Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartition(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedIds, @Param("partition_id") Integer thePartitionId);
|
||||
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("" +
|
||||
"SELECT " +
|
||||
" f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " +
|
||||
"FROM ForcedId f " +
|
||||
"JOIN ResourceTable t ON t.myId = f.myResourcePid " +
|
||||
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND f.myPartitionIdValue IS NULL")
|
||||
Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartitionNull(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedIds);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -67,4 +67,10 @@ public interface IResourceTableDao extends JpaRepository<ResourceTable, Long> {
|
|||
|
||||
@Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid)")
|
||||
Collection<Object[]> findLookupFieldsByResourcePid(@Param("pid") List<Long> thePids);
|
||||
|
||||
@Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid) AND t.myPartitionIdValue = :partition_id")
|
||||
Collection<Object[]> findLookupFieldsByResourcePidInPartition(@Param("pid") List<Long> thePids, @Param("partition_id") Integer thePartitionId);
|
||||
|
||||
@Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid) AND t.myPartitionIdValue IS NULL")
|
||||
Collection<Object[]> findLookupFieldsByResourcePidInPartitionNull(@Param("pid") List<Long> thePids);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
|||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.IResourceLinkResolver;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
|
@ -64,11 +65,11 @@ public class DaoResourceLinkResolver implements IResourceLinkResolver {
|
|||
private DaoRegistry myDaoRegistry;
|
||||
|
||||
@Override
|
||||
public IResourceLookup findTargetResource(RuntimeSearchParam theSearchParam, String theSourcePath, IIdType theSourceResourceId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest) {
|
||||
public IResourceLookup findTargetResource(PartitionId thePartitionId, RuntimeSearchParam theSearchParam, String theSourcePath, IIdType theSourceResourceId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest) {
|
||||
IResourceLookup resolvedResource;
|
||||
String idPart = theSourceResourceId.getIdPart();
|
||||
try {
|
||||
resolvedResource = myIdHelperService.resolveResourceIdentity(theTypeString, idPart, theRequest);
|
||||
resolvedResource = myIdHelperService.resolveResourceIdentity(thePartitionId, theTypeString, idPart, theRequest);
|
||||
ourLog.trace("Translated {}/{} to resource PID {}", theType, idPart, resolvedResource);
|
||||
} catch (ResourceNotFoundException e) {
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ public class DaoSearchParamSynchronizer {
|
|||
}
|
||||
|
||||
private <T extends BaseResourceIndex> void synchronize(ResourceIndexedSearchParams theParams, ResourceTable theEntity, AddRemoveCount theAddRemoveCount, Collection<T> theNewParms, Collection<T> theExistingParms) {
|
||||
theParams.calculateHashes(theNewParms);
|
||||
List<T> quantitiesToRemove = subtract(theExistingParms, theNewParms);
|
||||
List<T> quantitiesToAdd = subtract(theNewParms, theExistingParms);
|
||||
tryToReuseIndexEntities(quantitiesToRemove, quantitiesToAdd);
|
||||
|
@ -71,6 +70,9 @@ public class DaoSearchParamSynchronizer {
|
|||
}
|
||||
for (T next : quantitiesToAdd) {
|
||||
next.setPartitionId(theEntity.getPartitionId());
|
||||
}
|
||||
theParams.calculateHashes(theNewParms);
|
||||
for (T next : quantitiesToAdd) {
|
||||
myEntityManager.merge(next);
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ import java.util.Date;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -122,10 +123,10 @@ public class IdHelperService {
|
|||
* @throws ResourceNotFoundException If the ID can not be found
|
||||
*/
|
||||
@Nonnull
|
||||
public IResourceLookup resolveResourceIdentity(String theResourceName, String theResourceId, RequestDetails theRequestDetails) throws ResourceNotFoundException {
|
||||
public IResourceLookup resolveResourceIdentity(PartitionId thePartitionId, String theResourceName, String theResourceId, RequestDetails theRequestDetails) throws ResourceNotFoundException {
|
||||
// We only pass 1 input in so only 0..1 will come back
|
||||
IdDt id = new IdDt(theResourceName, theResourceId);
|
||||
Collection<IResourceLookup> matches = translateForcedIdToPids(theRequestDetails, Collections.singletonList(id));
|
||||
Collection<IResourceLookup> matches = translateForcedIdToPids(thePartitionId, theRequestDetails, Collections.singletonList(id));
|
||||
assert matches.size() <= 1;
|
||||
if (matches.isEmpty()) {
|
||||
throw new ResourceNotFoundException(id);
|
||||
|
@ -145,7 +146,7 @@ public class IdHelperService {
|
|||
if (myDaoConfig.isDeleteEnabled()) {
|
||||
retVal = resolveResourceIdentity(thePartitionId, theResourceType, theId);
|
||||
} else {
|
||||
String key = thePartitionId.getPartitionIdStringOrNullString() + "/" + theResourceType + "/" + theId;
|
||||
String key = stringifyForKey(thePartitionId) + "/" + theResourceType + "/" + theId;
|
||||
retVal = myPersistentIdCache.get(key, t -> resolveResourceIdentity(thePartitionId, theResourceType, theId));
|
||||
}
|
||||
|
||||
|
@ -157,10 +158,13 @@ public class IdHelperService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given a collection of resource IDs (resource type + id), resolves the internal persistent IDs
|
||||
* Given a collection of resource IDs (resource type + id), resolves the internal persistent IDs.
|
||||
* <p>
|
||||
* This implementation will always try to use a cache for performance, meaning that it can resolve resources that
|
||||
* are deleted (but note that forced IDs can't change, so the cache can't return incorrect results)
|
||||
*/
|
||||
@Nonnull
|
||||
public List<ResourcePersistentId> resolveResourcePersistentIds(List<IIdType> theIds, RequestDetails theRequest) {
|
||||
public List<ResourcePersistentId> resolveResourcePersistentIdsWithCache(PartitionId thePartitionId, List<IIdType> theIds, RequestDetails theRequest) {
|
||||
theIds.forEach(id -> Validate.isTrue(id.hasIdPart()));
|
||||
|
||||
if (theIds.isEmpty()) {
|
||||
|
@ -190,29 +194,35 @@ public class IdHelperService {
|
|||
|
||||
} else {
|
||||
|
||||
if (!myDaoConfig.isDeleteEnabled()) {
|
||||
for (Iterator<String> idIterator = nextIds.iterator(); idIterator.hasNext(); ) {
|
||||
String nextId = idIterator.next();
|
||||
String key = nextResourceType + "/" + nextId;
|
||||
Long nextCachedPid = myPersistentIdCache.getIfPresent(key);
|
||||
if (nextCachedPid != null) {
|
||||
idIterator.remove();
|
||||
retVal.add(new ResourcePersistentId(nextCachedPid));
|
||||
}
|
||||
for (Iterator<String> idIterator = nextIds.iterator(); idIterator.hasNext(); ) {
|
||||
String nextId = idIterator.next();
|
||||
String key = stringifyForKey(thePartitionId) + "/" + nextResourceType + "/" + nextId;
|
||||
Long nextCachedPid = myPersistentIdCache.getIfPresent(key);
|
||||
if (nextCachedPid != null) {
|
||||
idIterator.remove();
|
||||
retVal.add(new ResourcePersistentId(nextCachedPid));
|
||||
}
|
||||
}
|
||||
|
||||
if (nextIds.size() > 0) {
|
||||
Collection<Object[]> views = myForcedIdDao.findByTypeAndForcedId(nextResourceType, nextIds);
|
||||
|
||||
Collection<Object[]> views;
|
||||
if (thePartitionId != null) {
|
||||
if (thePartitionId.getPartitionId() != null) {
|
||||
views = myForcedIdDao.findByTypeAndForcedIdInPartition(nextResourceType, nextIds, thePartitionId.getPartitionId());
|
||||
} else {
|
||||
views = myForcedIdDao.findByTypeAndForcedIdInPartitionNull(nextResourceType, nextIds);
|
||||
}
|
||||
} else {
|
||||
views = myForcedIdDao.findByTypeAndForcedId(nextResourceType, nextIds);
|
||||
}
|
||||
for (Object[] nextView : views) {
|
||||
String forcedId = (String) nextView[0];
|
||||
Long pid = (Long) nextView[1];
|
||||
retVal.add(new ResourcePersistentId(pid));
|
||||
|
||||
if (!myDaoConfig.isDeleteEnabled()) {
|
||||
String key = nextResourceType + "/" + forcedId;
|
||||
myPersistentIdCache.put(key, pid);
|
||||
}
|
||||
String key = stringifyForKey(thePartitionId) + "/" + nextResourceType + "/" + forcedId;
|
||||
myPersistentIdCache.put(key, pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,7 +293,7 @@ public class IdHelperService {
|
|||
return pid.get();
|
||||
}
|
||||
|
||||
private Collection<IResourceLookup> translateForcedIdToPids(RequestDetails theRequest, Collection<IIdType> theId) {
|
||||
private Collection<IResourceLookup> translateForcedIdToPids(PartitionId thePartitionId, RequestDetails theRequest, Collection<IIdType> theId) {
|
||||
theId.forEach(id -> Validate.isTrue(id.hasIdPart()));
|
||||
|
||||
if (theId.isEmpty()) {
|
||||
|
@ -299,9 +309,19 @@ public class IdHelperService {
|
|||
.map(t -> t.getIdPartAsLong())
|
||||
.collect(Collectors.toList());
|
||||
if (!pids.isEmpty()) {
|
||||
myResourceTableDao.findLookupFieldsByResourcePid(pids)
|
||||
Collection<Object[]> lookup;
|
||||
if (thePartitionId != null) {
|
||||
if (thePartitionId.getPartitionId() != null) {
|
||||
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartition(pids, thePartitionId.getPartitionId());
|
||||
} else {
|
||||
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionNull(pids);
|
||||
}
|
||||
} else {
|
||||
lookup = myResourceTableDao.findLookupFieldsByResourcePid(pids);
|
||||
}
|
||||
lookup
|
||||
.stream()
|
||||
.map(lookup -> new ResourceLookup((String) lookup[0], (Long) lookup[1], (Date) lookup[2]))
|
||||
.map(t -> new ResourceLookup((String) t[0], (Long) t[1], (Date) t[2]))
|
||||
.forEach(retVal::add);
|
||||
}
|
||||
}
|
||||
|
@ -327,12 +347,20 @@ public class IdHelperService {
|
|||
Collection<Object[]> views;
|
||||
if (isBlank(nextResourceType)) {
|
||||
warnAboutUnqualifiedForcedIdResolution(theRequest);
|
||||
|
||||
// FIXME: deal with partition here
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoType(nextIds);
|
||||
|
||||
} else {
|
||||
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoType(nextResourceType, nextIds);
|
||||
|
||||
if (thePartitionId != null) {
|
||||
if (thePartitionId.getPartitionId() != null) {
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition(nextResourceType, nextIds, thePartitionId.getPartitionId());
|
||||
} else {
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull(nextResourceType, nextIds);
|
||||
}
|
||||
} else {
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoType(nextResourceType, nextIds);
|
||||
}
|
||||
}
|
||||
|
||||
for (Object[] next : views) {
|
||||
|
@ -379,6 +407,14 @@ public class IdHelperService {
|
|||
.build();
|
||||
}
|
||||
|
||||
private static String stringifyForKey(PartitionId thePartitionId) {
|
||||
String retVal = "(null)";
|
||||
if (thePartitionId != null) {
|
||||
retVal = thePartitionId.getPartitionIdStringOrNullString();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static boolean isValidPid(IIdType theId) {
|
||||
if (theId == null) {
|
||||
return false;
|
||||
|
|
|
@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
|||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.MatchResourceUrlService;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
|
||||
|
@ -95,12 +96,12 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
protected EntityManager myEntityManager;
|
||||
|
||||
public void populateFromResource(ResourceIndexedSearchParams theParams, Date theUpdateTime, ResourceTable theEntity, IBaseResource theResource, ResourceIndexedSearchParams theExistingParams, RequestDetails theRequest) {
|
||||
public void populateFromResource(PartitionConfig thePartitionConfig, ResourceIndexedSearchParams theParams, Date theUpdateTime, ResourceTable theEntity, IBaseResource theResource, ResourceIndexedSearchParams theExistingParams, RequestDetails theRequest) {
|
||||
mySearchParamExtractorService.extractFromResource(theRequest, theParams, theEntity, theResource);
|
||||
|
||||
Set<Map.Entry<String, RuntimeSearchParam>> activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType()).entrySet();
|
||||
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.ENABLED) {
|
||||
theParams.findMissingSearchParams(myDaoConfig.getModelConfig(), theEntity, activeSearchParams);
|
||||
theParams.findMissingSearchParams(thePartitionConfig, myDaoConfig.getModelConfig(), theEntity, activeSearchParams);
|
||||
}
|
||||
|
||||
theParams.setUpdatedTime(theUpdateTime);
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.BasePartitionable;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
|
@ -52,17 +53,18 @@ import java.util.List;
|
|||
|
||||
abstract class BasePredicateBuilder {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(BasePredicateBuilder.class);
|
||||
@Autowired
|
||||
FhirContext myContext;
|
||||
@Autowired
|
||||
DaoConfig myDaoConfig;
|
||||
|
||||
boolean myDontUseHashesForSearch;
|
||||
final CriteriaBuilder myCriteriaBuilder;
|
||||
final QueryRoot myQueryRoot;
|
||||
final Class<? extends IBaseResource> myResourceType;
|
||||
final String myResourceName;
|
||||
final SearchParameterMap myParams;
|
||||
@Autowired
|
||||
FhirContext myContext;
|
||||
@Autowired
|
||||
DaoConfig myDaoConfig;
|
||||
boolean myDontUseHashesForSearch;
|
||||
@Autowired
|
||||
private PartitionConfig myPartitionConfig;
|
||||
|
||||
BasePredicateBuilder(SearchBuilder theSearchBuilder) {
|
||||
myCriteriaBuilder = theSearchBuilder.getBuilder();
|
||||
|
@ -117,7 +119,7 @@ abstract class BasePredicateBuilder {
|
|||
Join<ResourceTable, SearchParamPresent> paramPresentJoin = myQueryRoot.join("mySearchParamPresents", JoinType.LEFT);
|
||||
|
||||
Expression<Long> hashPresence = paramPresentJoin.get("myHashPresence").as(Long.class);
|
||||
Long hash = SearchParamPresent.calculateHashPresence(theResourceName, theParamName, !theMissing);
|
||||
Long hash = SearchParamPresent.calculateHashPresence(myPartitionConfig, thePartitionId, theResourceName, theParamName, !theMissing);
|
||||
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
predicates.add(myCriteriaBuilder.equal(hashPresence, hash));
|
||||
|
@ -142,7 +144,7 @@ abstract class BasePredicateBuilder {
|
|||
myQueryRoot.setHasIndexJoins();
|
||||
}
|
||||
|
||||
Predicate combineParamIndexPredicateWithParamNamePredicate(String theResourceName, String theParamName, From<?, ? extends BaseResourceIndexedSearchParam> theFrom, Predicate thePredicate) {
|
||||
Predicate combineParamIndexPredicateWithParamNamePredicate(String theResourceName, String theParamName, From<?, ? extends BaseResourceIndexedSearchParam> theFrom, Predicate thePredicate, PartitionId thePartitionId) {
|
||||
if (myDontUseHashesForSearch) {
|
||||
Predicate resourceTypePredicate = myCriteriaBuilder.equal(theFrom.get("myResourceType"), theResourceName);
|
||||
Predicate paramNamePredicate = myCriteriaBuilder.equal(theFrom.get("myParamName"), theParamName);
|
||||
|
@ -150,11 +152,15 @@ abstract class BasePredicateBuilder {
|
|||
return outerPredicate;
|
||||
}
|
||||
|
||||
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(theResourceName, theParamName);
|
||||
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(myPartitionConfig, thePartitionId, theResourceName, theParamName);
|
||||
Predicate hashIdentityPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentity"), hashIdentity);
|
||||
return myCriteriaBuilder.and(hashIdentityPredicate, thePredicate);
|
||||
}
|
||||
|
||||
public PartitionConfig getPartitionConfig() {
|
||||
return myPartitionConfig;
|
||||
}
|
||||
|
||||
Predicate createPredicateNumeric(String theResourceName,
|
||||
String theParamName,
|
||||
From<?, ? extends BaseResourceIndexedSearchParam> theFrom,
|
||||
|
@ -163,7 +169,7 @@ abstract class BasePredicateBuilder {
|
|||
ParamPrefixEnum thePrefix,
|
||||
BigDecimal theValue,
|
||||
final Expression<BigDecimal> thePath,
|
||||
String invalidMessageName) {
|
||||
String invalidMessageName, PartitionId thePartitionId) {
|
||||
Predicate num;
|
||||
// Per discussions with Grahame Grieve and James Agnew on 11/13/19, modified logic for EQUAL and NOT_EQUAL operators below so as to
|
||||
// use exact value matching. The "fuzz amount" matching is still used with the APPROXIMATE operator.
|
||||
|
@ -207,13 +213,13 @@ abstract class BasePredicateBuilder {
|
|||
if (theParamName == null) {
|
||||
return num;
|
||||
}
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, num);
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, num, thePartitionId);
|
||||
}
|
||||
|
||||
void addPartitionIdPredicate(PartitionId thePartitionId, Join<ResourceTable, ? extends BasePartitionable> theJoin, List<Predicate> theCodePredicates) {
|
||||
if (thePartitionId != null) {
|
||||
Integer partitionId = thePartitionId.getPartitionId();
|
||||
Predicate partitionPredicate;
|
||||
Predicate partitionPredicate;
|
||||
if (partitionId != null) {
|
||||
partitionPredicate = myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue").as(Integer.class), partitionId);
|
||||
} else {
|
||||
|
|
|
@ -111,19 +111,19 @@ public class PredicateBuilder {
|
|||
return myPredicateBuilderResourceId.addPredicateResourceId(theValues, theResourceName, theOperation, thePartitionId);
|
||||
}
|
||||
|
||||
Predicate createPredicateString(IQueryParameterType theLeftValue, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theStringJoin) {
|
||||
return myPredicateBuilderString.createPredicateString(theLeftValue, theResourceName, theName, theBuilder, theStringJoin);
|
||||
Predicate createPredicateString(IQueryParameterType theLeftValue, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theStringJoin, PartitionId thePartitionId) {
|
||||
return myPredicateBuilderString.createPredicateString(theLeftValue, theResourceName, theName, theBuilder, theStringJoin, thePartitionId);
|
||||
}
|
||||
|
||||
Collection<Predicate> createPredicateToken(List<IQueryParameterType> theTokens, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theTokenJoin) {
|
||||
return myPredicateBuilderToken.createPredicateToken(theTokens, theResourceName, theName, theBuilder, theTokenJoin);
|
||||
Collection<Predicate> createPredicateToken(List<IQueryParameterType> theTokens, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theTokenJoin, PartitionId thePartitionId) {
|
||||
return myPredicateBuilderToken.createPredicateToken(theTokens, theResourceName, theName, theBuilder, theTokenJoin, thePartitionId);
|
||||
}
|
||||
|
||||
Predicate createPredicateDate(IQueryParameterType theLeftValue, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> theDateJoin) {
|
||||
return myPredicateBuilderDate.createPredicateDate(theLeftValue, theResourceName, theName, theBuilder, theDateJoin);
|
||||
Predicate createPredicateDate(IQueryParameterType theLeftValue, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> theDateJoin, PartitionId thePartitionId) {
|
||||
return myPredicateBuilderDate.createPredicateDate(theLeftValue, theResourceName, theName, theBuilder, theDateJoin, thePartitionId);
|
||||
}
|
||||
|
||||
Predicate createPredicateQuantity(IQueryParameterType theLeftValue, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> theDateJoin) {
|
||||
return myPredicateBuilderQuantity.createPredicateQuantity(theLeftValue, theResourceName, theName, theBuilder, theDateJoin);
|
||||
Predicate createPredicateQuantity(IQueryParameterType theLeftValue, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> theDateJoin, PartitionId thePartitionId) {
|
||||
return myPredicateBuilderQuantity.createPredicateQuantity(theLeftValue, theResourceName, theName, theBuilder, theDateJoin, thePartitionId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
|
|||
String theResourceName,
|
||||
String theParamName,
|
||||
CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamCoords> theFrom) {
|
||||
From<?, ResourceIndexedSearchParamCoords> theFrom, PartitionId thePartitionId) {
|
||||
String latitudeValue;
|
||||
String longitudeValue;
|
||||
Double distanceKm = 0.0;
|
||||
|
@ -120,7 +120,7 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
|
|||
longitudePredicate = longitudePredicateFromBox(theBuilder, theFrom, box);
|
||||
}
|
||||
Predicate singleCode = theBuilder.and(latitudePredicate, longitudePredicate);
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, thePartitionId);
|
||||
}
|
||||
|
||||
private Predicate latitudePredicateFromBox(CriteriaBuilder theBuilder, From<?, ResourceIndexedSearchParamCoords> theFrom, SearchBox theBox) {
|
||||
|
@ -166,8 +166,8 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
|
|||
theResourceName,
|
||||
theParamName,
|
||||
myCriteriaBuilder,
|
||||
join
|
||||
);
|
||||
join,
|
||||
thePartitionId);
|
||||
codePredicates.add(singleCode);
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,8 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
theParamName,
|
||||
myCriteriaBuilder,
|
||||
join,
|
||||
operation);
|
||||
operation,
|
||||
thePartitionId);
|
||||
codePredicates.add(p);
|
||||
}
|
||||
|
||||
|
@ -99,7 +100,7 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
|
||||
myQueryRoot.setHasIndexJoins();
|
||||
if (newJoin) {
|
||||
Predicate identityAndValuePredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, orPredicates);
|
||||
Predicate identityAndValuePredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, orPredicates, thePartitionId);
|
||||
myQueryRoot.addPredicate(identityAndValuePredicate);
|
||||
} else {
|
||||
myQueryRoot.addPredicate(orPredicates);
|
||||
|
@ -112,14 +113,15 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
String theResourceName,
|
||||
String theParamName,
|
||||
CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamDate> theFrom) {
|
||||
From<?, ResourceIndexedSearchParamDate> theFrom,
|
||||
PartitionId thePartitionId) {
|
||||
Predicate predicateDate = createPredicateDate(theParam,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
theBuilder,
|
||||
theFrom,
|
||||
null);
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, predicateDate);
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, predicateDate, thePartitionId);
|
||||
}
|
||||
|
||||
private Predicate createPredicateDate(IQueryParameterType theParam,
|
||||
|
@ -127,7 +129,8 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
String theParamName,
|
||||
CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamDate> theFrom,
|
||||
SearchFilterParser.CompareOperation theOperation) {
|
||||
SearchFilterParser.CompareOperation theOperation,
|
||||
PartitionId thePartitionId) {
|
||||
|
||||
Predicate p;
|
||||
if (theParam instanceof DateParam) {
|
||||
|
@ -155,6 +158,7 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
return p;
|
||||
}
|
||||
|
||||
// FIXME: does this need a partition ID?
|
||||
private Predicate createPredicateDateFromRange(CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamDate> theFrom,
|
||||
DateRangeParam theRange,
|
||||
|
|
|
@ -98,8 +98,8 @@ class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateB
|
|||
|
||||
String invalidMessageName = "invalidNumberPrefix";
|
||||
|
||||
Predicate predicateNumeric = createPredicateNumeric(theResourceName, theParamName, join, myCriteriaBuilder, nextOr, prefix, value, fromObj, invalidMessageName);
|
||||
Predicate predicateOuter = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, predicateNumeric);
|
||||
Predicate predicateNumeric = createPredicateNumeric(theResourceName, theParamName, join, myCriteriaBuilder, nextOr, prefix, value, fromObj, invalidMessageName, thePartitionId);
|
||||
Predicate predicateOuter = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, predicateNumeric, thePartitionId);
|
||||
codePredicates.add(predicateOuter);
|
||||
|
||||
} else {
|
||||
|
|
|
@ -32,7 +32,11 @@ import ca.uhn.fhir.rest.param.QuantityParam;
|
|||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.persistence.criteria.*;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.From;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -66,13 +70,7 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
|||
addPartitionIdPredicate(thePartitionId, join, codePredicates);
|
||||
|
||||
for (IQueryParameterType nextOr : theList) {
|
||||
|
||||
Predicate singleCode = createPredicateQuantity(nextOr,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
myCriteriaBuilder,
|
||||
join,
|
||||
theOperation);
|
||||
Predicate singleCode = createPredicateQuantity(nextOr, theResourceName, theParamName, myCriteriaBuilder, join, theOperation, thePartitionId);
|
||||
codePredicates.add(singleCode);
|
||||
}
|
||||
|
||||
|
@ -86,13 +84,15 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
|||
String theResourceName,
|
||||
String theParamName,
|
||||
CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamQuantity> theFrom) {
|
||||
From<?, ResourceIndexedSearchParamQuantity> theFrom,
|
||||
PartitionId thePartitionId) {
|
||||
return createPredicateQuantity(theParam,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
theBuilder,
|
||||
theFrom,
|
||||
null);
|
||||
null,
|
||||
thePartitionId);
|
||||
}
|
||||
|
||||
private Predicate createPredicateQuantity(IQueryParameterType theParam,
|
||||
|
@ -100,7 +100,8 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
|||
String theParamName,
|
||||
CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamQuantity> theFrom,
|
||||
SearchFilterParser.CompareOperation operation) {
|
||||
SearchFilterParser.CompareOperation operation,
|
||||
PartitionId thePartitionId) {
|
||||
String systemValue;
|
||||
String unitsValue;
|
||||
ParamPrefixEnum cmpValue = null;
|
||||
|
@ -157,7 +158,7 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
|||
final Expression<BigDecimal> path = theFrom.get("myValue");
|
||||
String invalidMessageName = "invalidQuantityPrefix";
|
||||
|
||||
Predicate num = createPredicateNumeric(theResourceName, null, theFrom, theBuilder, theParam, cmpValue, valueValue, path, invalidMessageName);
|
||||
Predicate num = createPredicateNumeric(theResourceName, null, theFrom, theBuilder, theParam, cmpValue, valueValue, path, invalidMessageName, thePartitionId);
|
||||
|
||||
Predicate singleCode;
|
||||
if (system == null && code == null) {
|
||||
|
@ -170,18 +171,18 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
|||
singleCode = theBuilder.and(system, code, num);
|
||||
}
|
||||
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, thePartitionId);
|
||||
}
|
||||
|
||||
Predicate hashPredicate;
|
||||
if (!isBlank(systemValue) && !isBlank(unitsValue)) {
|
||||
long hash = ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(theResourceName, theParamName, systemValue, unitsValue);
|
||||
long hash = ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(getPartitionConfig(), thePartitionId, theResourceName, theParamName, systemValue, unitsValue);
|
||||
hashPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentitySystemAndUnits"), hash);
|
||||
} else if (!isBlank(unitsValue)) {
|
||||
long hash = ResourceIndexedSearchParamQuantity.calculateHashUnits(theResourceName, theParamName, unitsValue);
|
||||
long hash = ResourceIndexedSearchParamQuantity.calculateHashUnits(getPartitionConfig(), thePartitionId, theResourceName, theParamName, unitsValue);
|
||||
hashPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentityAndUnits"), hash);
|
||||
} else {
|
||||
long hash = BaseResourceIndexedSearchParam.calculateHashIdentity(theResourceName, theParamName);
|
||||
long hash = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionConfig(), thePartitionId, theResourceName, theParamName);
|
||||
hashPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentity"), hash);
|
||||
}
|
||||
|
||||
|
@ -189,7 +190,7 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
|||
final Expression<BigDecimal> path = theFrom.get("myValue");
|
||||
String invalidMessageName = "invalidQuantityPrefix";
|
||||
|
||||
Predicate numericPredicate = createPredicateNumeric(theResourceName, null, theFrom, theBuilder, theParam, cmpValue, valueValue, path, invalidMessageName);
|
||||
Predicate numericPredicate = createPredicateNumeric(theResourceName, null, theFrom, theBuilder, theParam, cmpValue, valueValue, path, invalidMessageName, thePartitionId);
|
||||
|
||||
return theBuilder.and(hashPredicate, numericPredicate);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
|
||||
|
@ -63,6 +64,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|||
import ca.uhn.fhir.rest.param.*;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
|
@ -108,6 +110,8 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
@Autowired
|
||||
DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
PartitionConfig myPartitionConfig;
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
|
||||
PredicateBuilderReference(SearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder) {
|
||||
|
@ -190,7 +194,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
addPartitionIdPredicate(thePartitionId, join, codePredicates);
|
||||
|
||||
// Resources by ID
|
||||
List<ResourcePersistentId> targetPids = myIdHelperService.resolveResourcePersistentIds(targetIds, theRequest);
|
||||
List<ResourcePersistentId> targetPids = myIdHelperService.resolveResourcePersistentIdsWithCache(thePartitionId, targetIds, theRequest);
|
||||
if (!targetPids.isEmpty()) {
|
||||
ourLog.debug("Searching for resource link with target PIDs: {}", targetPids);
|
||||
Predicate pathPredicate;
|
||||
|
@ -201,7 +205,11 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
}
|
||||
Predicate pidPredicate;
|
||||
if ((operation == null) || (operation == SearchFilterParser.CompareOperation.eq)) {
|
||||
pidPredicate = join.get("myTargetResourcePid").in(ResourcePersistentId.toLongList(targetPids));
|
||||
if (targetPids.size() == 1) {
|
||||
pidPredicate = myCriteriaBuilder.equal(join.get("myTargetResourcePid").as(Long.class), targetPids.get(0).getIdAsLong());
|
||||
} else {
|
||||
pidPredicate = join.get("myTargetResourcePid").in(ResourcePersistentId.toLongList(targetPids));
|
||||
}
|
||||
} else {
|
||||
pidPredicate = join.get("myTargetResourcePid").in(ResourcePersistentId.toLongList(targetPids)).not();
|
||||
}
|
||||
|
@ -541,6 +549,13 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
|
||||
RuntimeSearchParam nextParamDef = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
|
||||
if (nextParamDef != null) {
|
||||
|
||||
if (myPartitionConfig.isPartitioningEnabled() && myPartitionConfig.isIncludePartitionInSearchHashes()) {
|
||||
if (thePartitionId == null) {
|
||||
throw new PreconditionFailedException("This server is not configured to support search against all partitions");
|
||||
}
|
||||
}
|
||||
|
||||
switch (nextParamDef.getParamType()) {
|
||||
case DATE:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
|
@ -578,7 +593,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
break;
|
||||
case COMPOSITE:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateComposite(theResourceName, nextParamDef, nextAnd);
|
||||
addPredicateComposite(theResourceName, nextParamDef, nextAnd, thePartitionId);
|
||||
}
|
||||
break;
|
||||
case URI:
|
||||
|
@ -623,6 +638,12 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
myQueryRoot.clearPredicates();
|
||||
myQueryRoot.addPredicates(holdPredicates);
|
||||
myQueryRoot.addPredicate(filterPredicate);
|
||||
|
||||
// Because filters can have an OR at the root, we never know for sure that we haven't done an optimized
|
||||
// search that doesn't check the resource type. This could be improved in the future, but for now it's
|
||||
// safest to just clear this flag. The test "testRetrieveDifferentTypeEq" will fail if we don't clear
|
||||
// this here.
|
||||
myQueryRoot.clearHasIndexJoins();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -932,7 +953,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
return parameter.substring(parameter.indexOf(".") + 1);
|
||||
}
|
||||
|
||||
private void addPredicateComposite(String theResourceName, RuntimeSearchParam theParamDef, List<? extends IQueryParameterType> theNextAnd) {
|
||||
private void addPredicateComposite(String theResourceName, RuntimeSearchParam theParamDef, List<? extends IQueryParameterType> theNextAnd, PartitionId thePartitionId) {
|
||||
// TODO: fail if missing is set for a composite query
|
||||
|
||||
IQueryParameterType or = theNextAnd.get(0);
|
||||
|
@ -943,37 +964,37 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
|
||||
RuntimeSearchParam left = theParamDef.getCompositeOf().get(0);
|
||||
IQueryParameterType leftValue = cp.getLeftValue();
|
||||
myQueryRoot.addPredicate(createCompositeParamPart(theResourceName, myQueryRoot.getRoot(), left, leftValue));
|
||||
myQueryRoot.addPredicate(createCompositeParamPart(theResourceName, myQueryRoot.getRoot(), left, leftValue, thePartitionId));
|
||||
|
||||
RuntimeSearchParam right = theParamDef.getCompositeOf().get(1);
|
||||
IQueryParameterType rightValue = cp.getRightValue();
|
||||
myQueryRoot.addPredicate(createCompositeParamPart(theResourceName, myQueryRoot.getRoot(), right, rightValue));
|
||||
myQueryRoot.addPredicate(createCompositeParamPart(theResourceName, myQueryRoot.getRoot(), right, rightValue, thePartitionId));
|
||||
|
||||
}
|
||||
|
||||
private Predicate createCompositeParamPart(String theResourceName, Root<ResourceTable> theRoot, RuntimeSearchParam theParam, IQueryParameterType leftValue) {
|
||||
private Predicate createCompositeParamPart(String theResourceName, Root<ResourceTable> theRoot, RuntimeSearchParam theParam, IQueryParameterType leftValue, PartitionId thePartitionId) {
|
||||
Predicate retVal = null;
|
||||
switch (theParam.getParamType()) {
|
||||
case STRING: {
|
||||
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> stringJoin = theRoot.join("myParamsString", JoinType.INNER);
|
||||
retVal = myPredicateBuilder.createPredicateString(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, stringJoin);
|
||||
retVal = myPredicateBuilder.createPredicateString(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, stringJoin, thePartitionId);
|
||||
break;
|
||||
}
|
||||
case TOKEN: {
|
||||
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> tokenJoin = theRoot.join("myParamsToken", JoinType.INNER);
|
||||
List<IQueryParameterType> tokens = Collections.singletonList(leftValue);
|
||||
Collection<Predicate> tokenPredicates = myPredicateBuilder.createPredicateToken(tokens, theResourceName, theParam.getName(), myCriteriaBuilder, tokenJoin);
|
||||
Collection<Predicate> tokenPredicates = myPredicateBuilder.createPredicateToken(tokens, theResourceName, theParam.getName(), myCriteriaBuilder, tokenJoin, thePartitionId);
|
||||
retVal = myCriteriaBuilder.and(tokenPredicates.toArray(new Predicate[0]));
|
||||
break;
|
||||
}
|
||||
case DATE: {
|
||||
From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> dateJoin = theRoot.join("myParamsDate", JoinType.INNER);
|
||||
retVal = myPredicateBuilder.createPredicateDate(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, dateJoin);
|
||||
retVal = myPredicateBuilder.createPredicateDate(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, dateJoin, thePartitionId);
|
||||
break;
|
||||
}
|
||||
case QUANTITY: {
|
||||
From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> dateJoin = theRoot.join("myParamsQuantity", JoinType.INNER);
|
||||
retVal = myPredicateBuilder.createPredicateQuantity(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, dateJoin);
|
||||
retVal = myPredicateBuilder.createPredicateQuantity(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, dateJoin, thePartitionId);
|
||||
break;
|
||||
}
|
||||
case COMPOSITE:
|
||||
|
|
|
@ -71,13 +71,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
|||
addPartitionIdPredicate(thePartitionId, join, codePredicates);
|
||||
|
||||
for (IQueryParameterType nextOr : theList) {
|
||||
IQueryParameterType theParameter = nextOr;
|
||||
Predicate singleCode = createPredicateString(theParameter,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
myCriteriaBuilder,
|
||||
join,
|
||||
theOperation);
|
||||
Predicate singleCode = createPredicateString(nextOr, theResourceName, theParamName, myCriteriaBuilder, join, theOperation, thePartitionId);
|
||||
codePredicates.add(singleCode);
|
||||
}
|
||||
|
||||
|
@ -93,13 +87,15 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
|||
String theResourceName,
|
||||
String theParamName,
|
||||
CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamString> theFrom) {
|
||||
From<?, ResourceIndexedSearchParamString> theFrom,
|
||||
PartitionId thePartitionId) {
|
||||
return createPredicateString(theParameter,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
theBuilder,
|
||||
theFrom,
|
||||
null);
|
||||
null,
|
||||
thePartitionId);
|
||||
}
|
||||
|
||||
private Predicate createPredicateString(IQueryParameterType theParameter,
|
||||
|
@ -107,7 +103,8 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
|||
String theParamName,
|
||||
CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamString> theFrom,
|
||||
SearchFilterParser.CompareOperation operation) {
|
||||
SearchFilterParser.CompareOperation operation,
|
||||
PartitionId thePartitionId) {
|
||||
String rawSearchTerm;
|
||||
if (theParameter instanceof TokenParam) {
|
||||
TokenParam id = (TokenParam) theParameter;
|
||||
|
@ -157,12 +154,12 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
|||
singleCode = theBuilder.and(singleCode, exactCode);
|
||||
}
|
||||
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, thePartitionId);
|
||||
}
|
||||
boolean exactMatch = theParameter instanceof StringParam && ((StringParam) theParameter).isExact();
|
||||
if (exactMatch) {
|
||||
// Exact match
|
||||
Long hash = ResourceIndexedSearchParamString.calculateHashExact(theResourceName, theParamName, rawSearchTerm);
|
||||
Long hash = ResourceIndexedSearchParamString.calculateHashExact(getPartitionConfig(), thePartitionId, theResourceName, theParamName, rawSearchTerm);
|
||||
return theBuilder.equal(theFrom.get("myHashExact").as(Long.class), hash);
|
||||
} else {
|
||||
// Normalized Match
|
||||
|
@ -190,34 +187,34 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
|||
Predicate predicate;
|
||||
if ((operation == null) ||
|
||||
(operation == SearchFilterParser.CompareOperation.sw)) {
|
||||
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(myDaoConfig.getModelConfig(), theResourceName, theParamName, normalizedString);
|
||||
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(getPartitionConfig(), thePartitionId, myDaoConfig.getModelConfig(), theResourceName, theParamName, normalizedString);
|
||||
Predicate hashCode = theBuilder.equal(theFrom.get("myHashNormalizedPrefix").as(Long.class), hash);
|
||||
Predicate singleCode = theBuilder.like(theFrom.get("myValueNormalized").as(String.class), likeExpression);
|
||||
predicate = theBuilder.and(hashCode, singleCode);
|
||||
} else if ((operation == SearchFilterParser.CompareOperation.ew) ||
|
||||
(operation == SearchFilterParser.CompareOperation.co)) {
|
||||
Predicate singleCode = theBuilder.like(theFrom.get("myValueNormalized").as(String.class), likeExpression);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, thePartitionId);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.eq) {
|
||||
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(myDaoConfig.getModelConfig(), theResourceName, theParamName, normalizedString);
|
||||
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(getPartitionConfig(), thePartitionId, myDaoConfig.getModelConfig(), theResourceName, theParamName, normalizedString);
|
||||
Predicate hashCode = theBuilder.equal(theFrom.get("myHashNormalizedPrefix").as(Long.class), hash);
|
||||
Predicate singleCode = theBuilder.like(theFrom.get("myValueNormalized").as(String.class), normalizedString);
|
||||
predicate = theBuilder.and(hashCode, singleCode);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ne) {
|
||||
Predicate singleCode = theBuilder.notEqual(theFrom.get("myValueNormalized").as(String.class), likeExpression);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, thePartitionId);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.gt) {
|
||||
Predicate singleCode = theBuilder.greaterThan(theFrom.get("myValueNormalized").as(String.class), likeExpression);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, thePartitionId);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.lt) {
|
||||
Predicate singleCode = theBuilder.lessThan(theFrom.get("myValueNormalized").as(String.class), likeExpression);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, thePartitionId);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ge) {
|
||||
Predicate singleCode = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueNormalized").as(String.class), likeExpression);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, thePartitionId);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.le) {
|
||||
Predicate singleCode = theBuilder.lessThanOrEqualTo(theFrom.get("myValueNormalized").as(String.class), likeExpression);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
||||
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, thePartitionId);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't yet know how to handle operation " + operation + " on a string");
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
Join<ResourceTable, ResourceIndexedSearchParamToken> join = createJoin(SearchBuilderJoinEnum.TOKEN, theParamName);
|
||||
addPartitionIdPredicate(thePartitionId, join, codePredicates);
|
||||
|
||||
Collection<Predicate> singleCode = createPredicateToken(tokens, theResourceName, theParamName, myCriteriaBuilder, join, theOperation);
|
||||
Collection<Predicate> singleCode = createPredicateToken(tokens, theResourceName, theParamName, myCriteriaBuilder, join, theOperation, thePartitionId);
|
||||
assert singleCode != null;
|
||||
codePredicates.addAll(singleCode);
|
||||
|
||||
|
@ -119,14 +119,16 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
String theResourceName,
|
||||
String theParamName,
|
||||
CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamToken> theFrom) {
|
||||
From<?, ResourceIndexedSearchParamToken> theFrom,
|
||||
PartitionId thePartitionId) {
|
||||
return createPredicateToken(
|
||||
theParameters,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
theBuilder,
|
||||
theFrom,
|
||||
null);
|
||||
null,
|
||||
thePartitionId);
|
||||
}
|
||||
|
||||
private Collection<Predicate> createPredicateToken(Collection<IQueryParameterType> theParameters,
|
||||
|
@ -134,7 +136,8 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
String theParamName,
|
||||
CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamToken> theFrom,
|
||||
SearchFilterParser.CompareOperation operation) {
|
||||
SearchFilterParser.CompareOperation operation,
|
||||
PartitionId thePartitionId) {
|
||||
final List<VersionIndependentConcept> codes = new ArrayList<>();
|
||||
|
||||
TokenParamModifier modifier = null;
|
||||
|
@ -210,19 +213,19 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
// System only
|
||||
List<VersionIndependentConcept> systemOnlyCodes = sortedCodesList.stream().filter(t -> isBlank(t.getCode())).collect(Collectors.toList());
|
||||
if (!systemOnlyCodes.isEmpty()) {
|
||||
retVal.add(addPredicate(theResourceName, theParamName, theBuilder, theFrom, systemOnlyCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_ONLY));
|
||||
retVal.add(addPredicate(theResourceName, theParamName, theBuilder, theFrom, systemOnlyCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_ONLY, thePartitionId));
|
||||
}
|
||||
|
||||
// Code only
|
||||
List<VersionIndependentConcept> codeOnlyCodes = sortedCodesList.stream().filter(t -> t.getSystem() == null).collect(Collectors.toList());
|
||||
if (!codeOnlyCodes.isEmpty()) {
|
||||
retVal.add(addPredicate(theResourceName, theParamName, theBuilder, theFrom, codeOnlyCodes, modifier, SearchBuilderTokenModeEnum.VALUE_ONLY));
|
||||
retVal.add(addPredicate(theResourceName, theParamName, theBuilder, theFrom, codeOnlyCodes, modifier, SearchBuilderTokenModeEnum.VALUE_ONLY, thePartitionId));
|
||||
}
|
||||
|
||||
// System and code
|
||||
List<VersionIndependentConcept> systemAndCodeCodes = sortedCodesList.stream().filter(t -> isNotBlank(t.getCode()) && t.getSystem() != null).collect(Collectors.toList());
|
||||
if (!systemAndCodeCodes.isEmpty()) {
|
||||
retVal.add(addPredicate(theResourceName, theParamName, theBuilder, theFrom, systemAndCodeCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_AND_VALUE));
|
||||
retVal.add(addPredicate(theResourceName, theParamName, theBuilder, theFrom, systemAndCodeCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_AND_VALUE, thePartitionId));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
|
@ -274,7 +277,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
}
|
||||
}
|
||||
|
||||
private Predicate addPredicate(String theResourceName, String theParamName, CriteriaBuilder theBuilder, From<?, ResourceIndexedSearchParamToken> theFrom, List<VersionIndependentConcept> theTokens, TokenParamModifier theModifier, SearchBuilderTokenModeEnum theTokenMode) {
|
||||
private Predicate addPredicate(String theResourceName, String theParamName, CriteriaBuilder theBuilder, From<?, ResourceIndexedSearchParamToken> theFrom, List<VersionIndependentConcept> theTokens, TokenParamModifier theModifier, SearchBuilderTokenModeEnum theTokenMode, PartitionId thePartitionId) {
|
||||
if (myDontUseHashesForSearch) {
|
||||
final Path<String> systemExpression = theFrom.get("mySystem");
|
||||
final Path<String> valueExpression = theFrom.get("myValue");
|
||||
|
@ -307,7 +310,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
or = theBuilder.not(or);
|
||||
}
|
||||
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, or);
|
||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, or, thePartitionId);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -322,14 +325,14 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
hashField = theFrom.get("myHashSystem").as(Long.class);
|
||||
values = theTokens
|
||||
.stream()
|
||||
.map(t -> ResourceIndexedSearchParamToken.calculateHashSystem(theResourceName, theParamName, t.getSystem()))
|
||||
.map(t -> ResourceIndexedSearchParamToken.calculateHashSystem(getPartitionConfig(), thePartitionId, theResourceName, theParamName, t.getSystem()))
|
||||
.collect(Collectors.toList());
|
||||
break;
|
||||
case VALUE_ONLY:
|
||||
hashField = theFrom.get("myHashValue").as(Long.class);
|
||||
values = theTokens
|
||||
.stream()
|
||||
.map(t -> ResourceIndexedSearchParamToken.calculateHashValue(theResourceName, theParamName, t.getCode()))
|
||||
.map(t -> ResourceIndexedSearchParamToken.calculateHashValue(getPartitionConfig(), thePartitionId, theResourceName, theParamName, t.getCode()))
|
||||
.collect(Collectors.toList());
|
||||
break;
|
||||
case SYSTEM_AND_VALUE:
|
||||
|
@ -337,14 +340,14 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
hashField = theFrom.get("myHashSystemAndValue").as(Long.class);
|
||||
values = theTokens
|
||||
.stream()
|
||||
.map(t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(theResourceName, theParamName, t.getSystem(), t.getCode()))
|
||||
.map(t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(getPartitionConfig(), thePartitionId, theResourceName, theParamName, t.getSystem(), t.getCode()))
|
||||
.collect(Collectors.toList());
|
||||
break;
|
||||
}
|
||||
|
||||
Predicate predicate = hashField.in(values);
|
||||
if (theModifier == TokenParamModifier.NOT) {
|
||||
Predicate identityPredicate = theBuilder.equal(theFrom.get("myHashIdentity").as(Long.class), BaseResourceIndexedSearchParam.calculateHashIdentity(theResourceName, theParamName));
|
||||
Predicate identityPredicate = theBuilder.equal(theFrom.get("myHashIdentity").as(Long.class), BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionConfig(), thePartitionId, theResourceName, theParamName));
|
||||
Predicate disjunctionPredicate = theBuilder.not(predicate);
|
||||
predicate = theBuilder.and(identityPredicate, disjunctionPredicate);
|
||||
}
|
||||
|
|
|
@ -108,13 +108,13 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
|||
}
|
||||
|
||||
Predicate uriPredicate = join.get("myUri").as(String.class).in(toFind);
|
||||
Predicate hashAndUriPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, uriPredicate);
|
||||
Predicate hashAndUriPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, uriPredicate, thePartitionId);
|
||||
codePredicates.add(hashAndUriPredicate);
|
||||
|
||||
} else if (param.getQualifier() == UriParamQualifierEnum.BELOW) {
|
||||
|
||||
Predicate uriPredicate = myCriteriaBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value));
|
||||
Predicate hashAndUriPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, uriPredicate);
|
||||
Predicate hashAndUriPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, uriPredicate, thePartitionId);
|
||||
codePredicates.add(hashAndUriPredicate);
|
||||
|
||||
} else {
|
||||
|
@ -125,7 +125,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
|||
|
||||
Predicate uriPredicate = null;
|
||||
if (operation == null || operation == SearchFilterParser.CompareOperation.eq) {
|
||||
long hashUri = ResourceIndexedSearchParamUri.calculateHashUri(theResourceName, theParamName, value);
|
||||
long hashUri = ResourceIndexedSearchParamUri.calculateHashUri(getPartitionConfig(), thePartitionId, theResourceName, theParamName, value);
|
||||
Predicate hashPredicate = myCriteriaBuilder.equal(join.get("myHashUri"), hashUri);
|
||||
codePredicates.add(hashPredicate);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ne) {
|
||||
|
@ -150,7 +150,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
|||
}
|
||||
|
||||
if (uriPredicate != null) {
|
||||
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(theResourceName, theParamName);
|
||||
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionConfig(), thePartitionId, theResourceName, theParamName);
|
||||
Predicate hashIdentityPredicate = myCriteriaBuilder.equal(join.get("myHashIdentity"), hashIdentity);
|
||||
codePredicates.add(myCriteriaBuilder.and(hashIdentityPredicate, uriPredicate));
|
||||
}
|
||||
|
@ -179,7 +179,8 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
|||
Predicate outerPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName,
|
||||
theParamName,
|
||||
join,
|
||||
orPredicate);
|
||||
orPredicate,
|
||||
thePartitionId);
|
||||
myQueryRoot.setHasIndexJoins();
|
||||
myQueryRoot.addPredicate(outerPredicate);
|
||||
return outerPredicate;
|
||||
|
|
|
@ -99,4 +99,8 @@ public class QueryRoot {
|
|||
public void setHasIndexJoins() {
|
||||
myHasIndexJoins = true;
|
||||
}
|
||||
|
||||
public void clearHasIndexJoins() {
|
||||
myHasIndexJoins = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
|||
for (IPrimitiveType<?> nextBaseType : theBase) {
|
||||
String nextBase = nextBaseType.getValueAsString();
|
||||
RuntimeSearchParam existingSearchParam = theSearchParamRegistry.getActiveSearchParam(nextBase, theCode);
|
||||
if (existingSearchParam.getId() == null) {
|
||||
if (existingSearchParam != null && existingSearchParam.getId() == null) {
|
||||
throw new UnprocessableEntityException("Can not override built-in search parameter " + nextBase + ":" + theCode + " because overriding is disabled on this server");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,5 @@ public interface IPartitionConfigSvc {
|
|||
PartitionEntity updatePartition(PartitionEntity thePartition);
|
||||
|
||||
void deletePartition(Integer thePartitionId);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package ca.uhn.fhir.jpa.partition;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IRequestPartitionHelperService {
|
||||
@Nullable
|
||||
PartitionId determineReadPartitionForRequest(@Nullable RequestDetails theRequest, String theResourceType);
|
||||
|
||||
@Nullable
|
||||
PartitionId determineCreatePartitionForRequest(@Nullable RequestDetails theRequest, @Nonnull IBaseResource theResource);
|
||||
}
|
|
@ -27,7 +27,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
public class PartitionConfigSvcImpl implements IPartitionConfigSvc {
|
||||
|
||||
public static final int DEFAULT_PERSISTED_PARTITION_ID = 0;
|
||||
private static final String DEFAULT_PERSISTED_PARTITION_NAME = "DEFAULT";
|
||||
public static final String DEFAULT_PERSISTED_PARTITION_NAME = "DEFAULT";
|
||||
private static final String DEFAULT_PERSISTED_PARTITION_DESC = "Default partition";
|
||||
private static final Pattern PARTITION_NAME_VALID_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+");
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PartitionConfigSvcImpl.class);
|
||||
|
|
|
@ -12,8 +12,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class PartitionManagementProvider {
|
||||
|
@ -50,7 +48,7 @@ public class PartitionManagementProvider {
|
|||
* $partition-management-update-partition
|
||||
* </code>
|
||||
*/
|
||||
@Operation(name = ProviderConstants.PARTITION_MANAGEMENT_ADD_PARTITION)
|
||||
@Operation(name = ProviderConstants.PARTITION_MANAGEMENT_UPDATE_PARTITION)
|
||||
public IBaseParameters updatePartition(
|
||||
@ResourceParam IBaseParameters theRequest,
|
||||
@OperationParam(name=ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, min = 1, max = 1, typeName = "integer") IPrimitiveType<Integer> thePartitionId,
|
||||
|
@ -71,7 +69,7 @@ public class PartitionManagementProvider {
|
|||
* $partition-management-delete-partition
|
||||
* </code>
|
||||
*/
|
||||
@Operation(name = ProviderConstants.PARTITION_MANAGEMENT_ADD_PARTITION)
|
||||
@Operation(name = ProviderConstants.PARTITION_MANAGEMENT_DELETE_PARTITION)
|
||||
public IBaseParameters updatePartition(
|
||||
@ResourceParam IBaseParameters theRequest,
|
||||
@OperationParam(name=ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, min = 1, max = 1, typeName = "integer") IPrimitiveType<Integer> thePartitionId
|
||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.interceptor.api.HookParams;
|
|||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -19,7 +20,7 @@ import java.util.HashSet;
|
|||
|
||||
import static ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster.doCallHooksAndReturnObject;
|
||||
|
||||
public class RequestPartitionHelperService {
|
||||
public class RequestPartitionHelperService implements IRequestPartitionHelperService {
|
||||
|
||||
private final HashSet<Object> myPartitioningBlacklist;
|
||||
|
||||
|
@ -31,6 +32,8 @@ public class RequestPartitionHelperService {
|
|||
private IPartitionConfigSvc myPartitionConfigSvc;
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
@Autowired
|
||||
private PartitionConfig myPartitionConfig;
|
||||
|
||||
public RequestPartitionHelperService() {
|
||||
myPartitioningBlacklist = new HashSet<>();
|
||||
|
@ -53,6 +56,7 @@ public class RequestPartitionHelperService {
|
|||
* Invoke the <code>STORAGE_PARTITION_IDENTIFY_READ</code> interceptor pointcut to determine the tenant for a read request
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public PartitionId determineReadPartitionForRequest(@Nullable RequestDetails theRequest, String theResourceType) {
|
||||
if (myPartitioningBlacklist.contains(theResourceType)) {
|
||||
return null;
|
||||
|
@ -60,14 +64,14 @@ public class RequestPartitionHelperService {
|
|||
|
||||
PartitionId partitionId = null;
|
||||
|
||||
if (myDaoConfig.isPartitioningEnabled()) {
|
||||
if (myPartitionConfig.isPartitioningEnabled()) {
|
||||
// Interceptor call: STORAGE_PARTITION_IDENTIFY_READ
|
||||
HookParams params = new HookParams()
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest);
|
||||
partitionId = (PartitionId) doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_IDENTIFY_READ, params);
|
||||
|
||||
validatePartition(partitionId, theResourceType, theRequest);
|
||||
validatePartition(partitionId, theResourceType);
|
||||
}
|
||||
|
||||
return partitionId;
|
||||
|
@ -77,10 +81,11 @@ public class RequestPartitionHelperService {
|
|||
* Invoke the <code>STORAGE_PARTITION_IDENTIFY_CREATE</code> interceptor pointcut to determine the tenant for a read request
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public PartitionId determineCreatePartitionForRequest(@Nullable RequestDetails theRequest, @Nonnull IBaseResource theResource) {
|
||||
|
||||
PartitionId partitionId = null;
|
||||
if (myDaoConfig.isPartitioningEnabled()) {
|
||||
if (myPartitionConfig.isPartitioningEnabled()) {
|
||||
// Interceptor call: STORAGE_PARTITION_IDENTIFY_CREATE
|
||||
HookParams params = new HookParams()
|
||||
.add(IBaseResource.class, theResource)
|
||||
|
@ -89,13 +94,13 @@ public class RequestPartitionHelperService {
|
|||
partitionId = (PartitionId) doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params);
|
||||
|
||||
String resourceName = myFhirContext.getResourceDefinition(theResource).getName();
|
||||
validatePartition(partitionId, resourceName, theRequest);
|
||||
validatePartition(partitionId, resourceName);
|
||||
}
|
||||
|
||||
return partitionId;
|
||||
}
|
||||
|
||||
private void validatePartition(@Nullable PartitionId thePartitionId, @Nonnull String theResourceName, RequestDetails theRequestDetails) {
|
||||
private void validatePartition(@Nullable PartitionId thePartitionId, @Nonnull String theResourceName) {
|
||||
if (thePartitionId != null && thePartitionId.getPartitionId() != null) {
|
||||
|
||||
// Make sure we're not using one of the conformance resources in a non-default partition
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class RequestTenantSelectingInterceptor {
|
||||
public class RequestTenantPartitionInterceptor {
|
||||
|
||||
@Autowired
|
||||
private IPartitionConfigSvc myPartitionConfigSvc;
|
||||
|
@ -36,7 +36,7 @@ public class RequestTenantSelectingInterceptor {
|
|||
try {
|
||||
partition = myPartitionConfigSvc.getPartitionByName(tenantId);
|
||||
} catch (IllegalArgumentException e) {
|
||||
String msg = myFhirContext.getLocalizer().getMessageSanitized(RequestTenantSelectingInterceptor.class, "unknownTenantName", tenantId);
|
||||
String msg = myFhirContext.getLocalizer().getMessageSanitized(RequestTenantPartitionInterceptor.class, "unknownTenantName", tenantId);
|
||||
throw new ResourceNotFoundException(msg);
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
|||
import ca.uhn.fhir.jpa.dao.IResultIterator;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchInclude;
|
||||
|
@ -156,7 +157,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
@Autowired
|
||||
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
|
||||
@Autowired
|
||||
private RequestPartitionHelperService myRequestPartitionHelperService;
|
||||
private IRequestPartitionHelperService myRequestPartitionHelperService;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -593,6 +594,11 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
myPersistedJpaBundleProviderFactory = thePersistedJpaBundleProviderFactory;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setRequestPartitionHelperService(IRequestPartitionHelperService theRequestPartitionHelperService) {
|
||||
myRequestPartitionHelperService = theRequestPartitionHelperService;
|
||||
}
|
||||
|
||||
/**
|
||||
* A search task is a Callable task that runs in
|
||||
* a thread pool to handle an individual search. One instance
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.sp;
|
|||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
||||
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
||||
|
@ -37,6 +38,9 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
|
|||
@Autowired
|
||||
private ISearchParamPresentDao mySearchParamPresentDao;
|
||||
|
||||
@Autowired
|
||||
private PartitionConfig myPartitionConfig;
|
||||
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
|
||||
|
@ -63,6 +67,7 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
|
|||
String paramName = next.getKey();
|
||||
|
||||
SearchParamPresent present = new SearchParamPresent();
|
||||
present.setPartitionConfig(myPartitionConfig);
|
||||
present.setResource(theResource);
|
||||
present.setParamName(paramName);
|
||||
present.setPresent(next.getValue());
|
||||
|
|
|
@ -104,6 +104,10 @@ public class TestUtil {
|
|||
Subselect subselect = theClazz.getAnnotation(Subselect.class);
|
||||
boolean isView = (subselect != null);
|
||||
|
||||
// FIXME: remove?
|
||||
Embeddable embeddable = theClazz.getAnnotation(Embeddable.class);
|
||||
boolean isEmbeddable = (embeddable != null);
|
||||
|
||||
scan(theClazz, theNames, theIsSuperClass, isView);
|
||||
|
||||
for (Field nextField : theClazz.getDeclaredFields()) {
|
||||
|
@ -126,11 +130,13 @@ public class TestUtil {
|
|||
boolean hasColumn = nextField.getAnnotation(Column.class) != null;
|
||||
boolean hasJoinColumn = nextField.getAnnotation(JoinColumn.class) != null;
|
||||
boolean hasEmbeddedId = nextField.getAnnotation(EmbeddedId.class) != null;
|
||||
boolean hasEmbedded = nextField.getAnnotation(Embedded.class) != null;
|
||||
OneToMany oneToMany = nextField.getAnnotation(OneToMany.class);
|
||||
OneToOne oneToOne = nextField.getAnnotation(OneToOne.class);
|
||||
boolean isOtherSideOfOneToManyMapping = oneToMany != null && isNotBlank(oneToMany.mappedBy());
|
||||
boolean isOtherSideOfOneToOneMapping = oneToOne != null && isNotBlank(oneToOne.mappedBy());
|
||||
Validate.isTrue(
|
||||
hasEmbedded ||
|
||||
hasColumn ||
|
||||
hasJoinColumn ||
|
||||
isOtherSideOfOneToManyMapping ||
|
||||
|
|
|
@ -111,7 +111,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
|||
SLF4JLogLevel level = SLF4JLogLevel.INFO;
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
.logQueryBySlf4j(level, "SQL")
|
||||
// .logQueryBySlf4j(level, "SQL")
|
||||
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
|
||||
// .countQuery(new ThreadQueryCountHolder())
|
||||
.beforeQuery(new BlockLargeNumbersOfParamsListener())
|
||||
|
|
|
@ -243,48 +243,6 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrideAndDisableBuiltInSearchParametersWithOverridingDisabled() {
|
||||
myModelConfig.setDefaultSearchParamsCanBeOverridden(false);
|
||||
|
||||
SearchParameter memberSp = new SearchParameter();
|
||||
memberSp.setCode("member");
|
||||
memberSp.setBase(ResourceTypeEnum.GROUP);
|
||||
memberSp.setType(SearchParamTypeEnum.REFERENCE);
|
||||
memberSp.setXpath("Group.member.entity");
|
||||
memberSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
|
||||
memberSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
|
||||
mySearchParameterDao.create(memberSp, mySrd);
|
||||
|
||||
SearchParameter identifierSp = new SearchParameter();
|
||||
identifierSp.setCode("identifier");
|
||||
identifierSp.setBase(ResourceTypeEnum.GROUP);
|
||||
identifierSp.setType(SearchParamTypeEnum.TOKEN);
|
||||
identifierSp.setXpath("Group.identifier");
|
||||
identifierSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
|
||||
identifierSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
|
||||
mySearchParameterDao.create(identifierSp, mySrd);
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().addGiven("G");
|
||||
IIdType pid = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
Group g = new Group();
|
||||
g.addIdentifier().setSystem("urn:foo").setValue("bar");
|
||||
g.addMember().getEntity().setReference(pid);
|
||||
myGroupDao.create(g);
|
||||
|
||||
assertThat(myResourceLinkDao.findAll(), not(empty()));
|
||||
assertThat(ListUtil.filter(myResourceIndexedSearchParamTokenDao.findAll(), new ListUtil.Filter<ResourceIndexedSearchParamToken>() {
|
||||
@Override
|
||||
public boolean isOut(ResourceIndexedSearchParamToken object) {
|
||||
return !object.getResourceType().equals("Group") || object.isMissing();
|
||||
}
|
||||
}), not(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrideAndDisableBuiltInSearchParametersWithOverridingEnabled() {
|
||||
myModelConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||
|
|
|
@ -212,7 +212,7 @@ public class FhirResourceDaoDstu2UpdateTest extends BaseJpaDstu2Test {
|
|||
p2.setId(new IdDt("Patient/" + p1id.getIdPart()));
|
||||
myOrganizationDao.update(p2, mySrd);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
} catch (InvalidRequestException e) {
|
||||
ourLog.error("Good", e);
|
||||
}
|
||||
|
||||
|
|
|
@ -582,15 +582,15 @@ public class FhirResourceDaoDstu3UpdateTest extends BaseJpaDstu3Test {
|
|||
myOrganizationDao.update(p2, mySrd);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
// good
|
||||
assertEquals("Existing resource ID[Patient/1] is of type[Patient] - Cannot update with [Organization]", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
p2.setId(new IdType("Patient/" + p1id.getIdPart()));
|
||||
myOrganizationDao.update(p2, mySrd);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
ourLog.error("Good", e);
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Incorrect resource type (Patient) for this DAO, wanted: Organization", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystem;
|
|||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.interceptor.PerformanceTracingLoggingInterceptor;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
|
@ -188,6 +189,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
|||
@Autowired
|
||||
protected DaoConfig myDaoConfig;
|
||||
@Autowired
|
||||
protected PartitionConfig myPartitionConfig;
|
||||
@Autowired
|
||||
protected ModelConfig myModelConfig;
|
||||
@Autowired
|
||||
@Qualifier("myDeviceDaoR4")
|
||||
|
|
|
@ -9,7 +9,12 @@ import ca.uhn.fhir.rest.param.StringParam;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.CarePlan;
|
||||
import org.hl7.fhir.r4.model.DateType;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.RiskAssessment;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
|
@ -18,7 +23,9 @@ import org.junit.Test;
|
|||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@SuppressWarnings({"Duplicates"})
|
||||
public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
||||
|
@ -183,22 +190,21 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("status eq active or _id eq %s",
|
||||
idVal)));
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("status eq active or _id eq %s", idVal)));
|
||||
myCaptureQueriesListener.clear();
|
||||
found = toUnqualifiedVersionlessIdValues(myEncounterDao.search(map));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(found, Matchers.empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("_id eq %s", idVal)));
|
||||
found = toUnqualifiedVersionlessIdValues(myEncounterDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("_id eq %s",
|
||||
idVal)));
|
||||
found = toUnqualifiedVersionlessIdValues(myEncounterDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("_id eq %s",
|
||||
idVal)));
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("_id eq %s", idVal)));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(id1));
|
||||
|
||||
|
|
|
@ -265,7 +265,6 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
|||
|
||||
myCaptureQueriesListener.clear();
|
||||
assertEquals(1, myObservationDao.search(map).size().intValue());
|
||||
// myCaptureQueriesListener.logAllQueriesForCurrentThread();
|
||||
// Resolve forced ID, Perform search, load result
|
||||
assertEquals(3, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
||||
assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
|
||||
|
@ -279,8 +278,8 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
|||
myCaptureQueriesListener.clear();
|
||||
assertEquals(1, myObservationDao.search(map).size().intValue());
|
||||
myCaptureQueriesListener.logAllQueriesForCurrentThread();
|
||||
// Resolve forced ID, Perform search, load result
|
||||
assertEquals(3, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
||||
// Resolve forced ID, Perform search, load result (this time we reuse the cached forced-id resolution)
|
||||
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
||||
assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
|
||||
assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
|
||||
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
|
||||
|
|
|
@ -344,34 +344,12 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
|||
memberSp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||
memberSp.setExpression("Group.member.entity");
|
||||
memberSp.setStatus(Enumerations.PublicationStatus.RETIRED);
|
||||
mySearchParameterDao.create(memberSp, mySrd);
|
||||
|
||||
SearchParameter identifierSp = new SearchParameter();
|
||||
identifierSp.setCode("identifier");
|
||||
identifierSp.addBase("Group");
|
||||
identifierSp.setType(Enumerations.SearchParamType.TOKEN);
|
||||
identifierSp.setExpression("Group.identifier");
|
||||
identifierSp.setStatus(Enumerations.PublicationStatus.RETIRED);
|
||||
mySearchParameterDao.create(identifierSp, mySrd);
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().addGiven("G");
|
||||
IIdType pid = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
Group g = new Group();
|
||||
g.addIdentifier().setSystem("urn:foo").setValue("bar");
|
||||
g.addMember().getEntity().setReferenceElement(pid);
|
||||
myGroupDao.create(g);
|
||||
|
||||
assertThat(myResourceLinkDao.findAll(), not(empty()));
|
||||
assertThat(ListUtil.filter(myResourceIndexedSearchParamTokenDao.findAll(), new ListUtil.Filter<ResourceIndexedSearchParamToken>() {
|
||||
@Override
|
||||
public boolean isOut(ResourceIndexedSearchParamToken object) {
|
||||
return !object.getResourceType().equals("Group") || object.isMissing();
|
||||
}
|
||||
}), not(empty()));
|
||||
try {
|
||||
mySearchParameterDao.create(memberSp, mySrd);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertEquals("Can not override built-in search parameter Group:member because overriding is disabled on this server", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||
|
@ -983,10 +984,10 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
List<ResourceIndexedSearchParamNumber> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
|
||||
ourLog.info(toStringMultiline(results));
|
||||
|
||||
ResourceIndexedSearchParamNumber expect0 = new ResourceIndexedSearchParamNumber("RiskAssessment", RiskAssessment.SP_PROBABILITY, new BigDecimal("1.00"));
|
||||
ResourceIndexedSearchParamNumber expect0 = new ResourceIndexedSearchParamNumber(new PartitionConfig(), "RiskAssessment", RiskAssessment.SP_PROBABILITY, new BigDecimal("1.00"));
|
||||
expect0.setResource(resource);
|
||||
expect0.calculateHashes();
|
||||
ResourceIndexedSearchParamNumber expect1 = new ResourceIndexedSearchParamNumber("RiskAssessment", RiskAssessment.SP_PROBABILITY, new BigDecimal("2.00"));
|
||||
ResourceIndexedSearchParamNumber expect1 = new ResourceIndexedSearchParamNumber(new PartitionConfig(), "RiskAssessment", RiskAssessment.SP_PROBABILITY, new BigDecimal("2.00"));
|
||||
expect1.setResource(resource);
|
||||
expect1.calculateHashes();
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
|
||||
|
@ -650,10 +651,10 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
|
|||
List<ResourceIndexedSearchParamNumber> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
|
||||
ourLog.info(toStringMultiline(results));
|
||||
|
||||
ResourceIndexedSearchParamNumber expect0 = new ResourceIndexedSearchParamNumber("RiskAssessment", RiskAssessment.SP_PROBABILITY, new BigDecimal("1.00"));
|
||||
ResourceIndexedSearchParamNumber expect0 = new ResourceIndexedSearchParamNumber(new PartitionConfig(), "RiskAssessment", RiskAssessment.SP_PROBABILITY, new BigDecimal("1.00"));
|
||||
expect0.setResource(resource);
|
||||
expect0.calculateHashes();
|
||||
ResourceIndexedSearchParamNumber expect1 = new ResourceIndexedSearchParamNumber("RiskAssessment", RiskAssessment.SP_PROBABILITY, new BigDecimal("2.00"));
|
||||
ResourceIndexedSearchParamNumber expect1 = new ResourceIndexedSearchParamNumber(new PartitionConfig(), "RiskAssessment", RiskAssessment.SP_PROBABILITY, new BigDecimal("2.00"));
|
||||
expect1.setResource(resource);
|
||||
expect1.calculateHashes();
|
||||
|
||||
|
|
|
@ -708,7 +708,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test {
|
|||
p2.setId(new IdType("Patient/" + p1id.getIdPart()));
|
||||
myOrganizationDao.update(p2, mySrd);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
} catch (InvalidRequestException e) {
|
||||
ourLog.error("Good", e);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.interceptor.api.Interceptor;
|
|||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.partition.IPartitionConfigSvc;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
|
@ -12,10 +13,12 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
|
@ -29,6 +32,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.SearchParameter;
|
||||
|
@ -49,6 +53,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
@ -63,7 +68,9 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
private MyInterceptor myPartitionInterceptor;
|
||||
private LocalDate myPartitionDate;
|
||||
private LocalDate myPartitionDate2;
|
||||
private int myPartitionId;
|
||||
private int myPartitionId2;
|
||||
private boolean myHaveDroppedForcedIdUniqueConstraint;
|
||||
@Autowired
|
||||
private IPartitionConfigSvc myPartitionConfigSvc;
|
||||
|
@ -72,7 +79,9 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
public void after() {
|
||||
myPartitionInterceptor.assertNoRemainingIds();
|
||||
|
||||
myDaoConfig.setPartitioningEnabled(new DaoConfig().isPartitioningEnabled());
|
||||
myPartitionConfig.setIncludePartitionInSearchHashes(new PartitionConfig().isIncludePartitionInSearchHashes());
|
||||
myPartitionConfig.setPartitioningEnabled(new PartitionConfig().isPartitioningEnabled());
|
||||
myPartitionConfig.setAllowReferencesAcrossPartitions(new PartitionConfig().isAllowReferencesAcrossPartitions());
|
||||
|
||||
myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof MyInterceptor);
|
||||
myInterceptor = null;
|
||||
|
@ -90,12 +99,14 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
public void before() throws ServletException {
|
||||
super.before();
|
||||
|
||||
myDaoConfig.setPartitioningEnabled(true);
|
||||
myPartitionConfig.setPartitioningEnabled(true);
|
||||
myDaoConfig.setUniqueIndexesEnabled(true);
|
||||
myModelConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||
|
||||
myPartitionDate = LocalDate.of(2020, Month.JANUARY, 14);
|
||||
myPartitionId = 3;
|
||||
myPartitionDate2 = LocalDate.of(2020, Month.JANUARY, 15);
|
||||
myPartitionId = 1;
|
||||
myPartitionId2 = 2;
|
||||
|
||||
myPartitionInterceptor = new MyInterceptor();
|
||||
myInterceptorRegistry.registerInterceptor(myPartitionInterceptor);
|
||||
|
@ -124,6 +135,155 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_CrossPartitionReference_ByPid_Allowed() {
|
||||
myPartitionConfig.setAllowReferencesAcrossPartitions(true);
|
||||
|
||||
// Create patient in partition 1
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
Patient patient = new Patient();
|
||||
patient.setActive(true);
|
||||
IIdType patientId = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
|
||||
|
||||
// Create observation in partition 2
|
||||
addCreatePartition(myPartitionId2, myPartitionDate2);
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(patientId.getValue());
|
||||
IIdType obsId = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
runInTransaction(()->{
|
||||
List<ResourceLink> resLinks = myResourceLinkDao.findAll();
|
||||
ourLog.info("Resource links:\n{}", resLinks.toString());
|
||||
assertEquals(2, resLinks.size());
|
||||
assertEquals(obsId.getIdPartAsLong(), resLinks.get(0).getSourceResourcePid());
|
||||
assertEquals(patientId.getIdPartAsLong(), resLinks.get(0).getTargetResourcePid());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_CrossPartitionReference_ByPid_NotAllowed() {
|
||||
|
||||
// Create patient in partition 1
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
Patient patient = new Patient();
|
||||
patient.setActive(true);
|
||||
IIdType patientId = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
|
||||
|
||||
// Create observation in partition 2
|
||||
addCreatePartition(myPartitionId2, myPartitionDate2);
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(patientId.getValue());
|
||||
|
||||
try {
|
||||
myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), startsWith("Resource Patient/" + patientId.getIdPart() + " not found, specified in path: Observation.subject"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_CrossPartitionReference_ByForcedId_Allowed() {
|
||||
myPartitionConfig.setAllowReferencesAcrossPartitions(true);
|
||||
|
||||
// Create patient in partition 1
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
Patient patient = new Patient();
|
||||
patient.setId("ONE");
|
||||
patient.setActive(true);
|
||||
IIdType patientId = myPatientDao.update(patient).getId().toUnqualifiedVersionless();
|
||||
|
||||
// Create observation in partition 2
|
||||
addCreatePartition(myPartitionId2, myPartitionDate2);
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(patientId.getValue());
|
||||
IIdType obsId = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
runInTransaction(()->{
|
||||
List<ResourceLink> resLinks = myResourceLinkDao.findAll();
|
||||
ourLog.info("Resource links:\n{}", resLinks.toString());
|
||||
assertEquals(2, resLinks.size());
|
||||
assertEquals(obsId.getIdPartAsLong(), resLinks.get(0).getSourceResourcePid());
|
||||
assertEquals(patientId.getIdPart(), resLinks.get(0).getTargetResourceId());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_CrossPartitionReference_ByForcedId_NotAllowed() {
|
||||
|
||||
// Create patient in partition 1
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
Patient patient = new Patient();
|
||||
patient.setId("ONE");
|
||||
patient.setActive(true);
|
||||
IIdType patientId = myPatientDao.update(patient).getId().toUnqualifiedVersionless();
|
||||
|
||||
// Create observation in partition 2
|
||||
addCreatePartition(myPartitionId2, myPartitionDate2);
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(patientId.getValue());
|
||||
|
||||
try {
|
||||
myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), startsWith("Resource Patient/ONE not found, specified in path: Observation.subject"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_SamePartitionReference_DefaultPartition_ByPid() {
|
||||
myPartitionConfig.setAllowReferencesAcrossPartitions(true);
|
||||
|
||||
// Create patient in partition NULL
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
Patient patient = new Patient();
|
||||
patient.setActive(true);
|
||||
IIdType patientId = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
|
||||
|
||||
// Create observation in partition NULL
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(patientId.getValue());
|
||||
IIdType obsId = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
runInTransaction(()->{
|
||||
List<ResourceLink> resLinks = myResourceLinkDao.findAll();
|
||||
ourLog.info("Resource links:\n{}", resLinks.toString());
|
||||
assertEquals(2, resLinks.size());
|
||||
assertEquals(obsId.getIdPartAsLong(), resLinks.get(0).getSourceResourcePid());
|
||||
assertEquals(patientId.getIdPartAsLong(), resLinks.get(0).getTargetResourcePid());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_SamePartitionReference_DefaultPartition_ByForcedId() {
|
||||
myPartitionConfig.setAllowReferencesAcrossPartitions(true);
|
||||
|
||||
// Create patient in partition NULL
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
Patient patient = new Patient();
|
||||
patient.setId("ONE");
|
||||
patient.setActive(true);
|
||||
IIdType patientId = myPatientDao.update(patient).getId().toUnqualifiedVersionless();
|
||||
|
||||
// Create observation in partition NULL
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(patientId.getValue());
|
||||
IIdType obsId = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||
|
||||
runInTransaction(()->{
|
||||
List<ResourceLink> resLinks = myResourceLinkDao.findAll();
|
||||
ourLog.info("Resource links:\n{}", resLinks.toString());
|
||||
assertEquals(2, resLinks.size());
|
||||
assertEquals(obsId.getIdPartAsLong(), resLinks.get(0).getSourceResourcePid());
|
||||
assertEquals(patientId.getIdPart(), resLinks.get(0).getTargetResourceId());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSearchParameter_DefaultPartitionWithDate() {
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
|
@ -140,7 +300,7 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
runInTransaction(() -> {
|
||||
// HFJ_RESOURCE
|
||||
ResourceTable resourceTable = myResourceTableDao.findById(id).orElseThrow(IllegalArgumentException::new);
|
||||
assertEquals(null, resourceTable.getPartitionId().getPartitionId());
|
||||
assertNull(resourceTable.getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, resourceTable.getPartitionId().getPartitionDate());
|
||||
});
|
||||
}
|
||||
|
@ -269,7 +429,7 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
// HFJ_RES_PARAM_PRESENT
|
||||
List<SearchParamPresent> presents = mySearchParamPresentDao.findAllForResource(resourceTable);
|
||||
assertEquals(myPartitionId, presents.size());
|
||||
assertEquals(3, presents.size());
|
||||
assertEquals(myPartitionId, presents.get(0).getPartitionId().getPartitionId().intValue());
|
||||
assertEquals(myPartitionDate, presents.get(0).getPartitionId().getPartitionDate());
|
||||
|
||||
|
@ -451,8 +611,8 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
@Test
|
||||
public void testUpdateResourceWithPartition() {
|
||||
createRequestId();
|
||||
addCreatePartition(3, LocalDate.of(2020, Month.JANUARY, 14));
|
||||
addCreatePartition(3, LocalDate.of(2020, Month.JANUARY, 14));
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
|
||||
// Create a resource
|
||||
Patient p = new Patient();
|
||||
|
@ -1033,6 +1193,75 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_NORMALIZED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_StringParam_SearchAllPartitions_IncludePartitionInHashes() {
|
||||
myPartitionConfig.setIncludePartitionInSearchHashes(true);
|
||||
|
||||
addReadPartition(null);
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_FAMILY, new StringParam("FAMILY"));
|
||||
map.setLoadSynchronous(true);
|
||||
try {
|
||||
myPatientDao.search(map);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
assertEquals("This server is not configured to support search against all partitions", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_StringParam_SearchDefaultPartition_IncludePartitionInHashes() {
|
||||
myPartitionConfig.setIncludePartitionInSearchHashes(true);
|
||||
|
||||
IIdType patientIdNull = createPatient(null, withFamily("FAMILY"));
|
||||
createPatient(1, withFamily("FAMILY"));
|
||||
createPatient(2, withFamily("FAMILY"));
|
||||
|
||||
addDefaultReadPartition();
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_FAMILY, new StringParam("FAMILY"));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
searchSql = searchSql.toUpperCase();
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID IS NULL"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_NORMALIZED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_StringParam_SearchOnePartition_IncludePartitionInHashes() {
|
||||
myPartitionConfig.setIncludePartitionInSearchHashes(true);
|
||||
|
||||
createPatient(null, withFamily("FAMILY"));
|
||||
IIdType patientId1 = createPatient(1, withFamily("FAMILY"));
|
||||
createPatient(2, withFamily("FAMILY"));
|
||||
|
||||
addReadPartition(1);
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_FAMILY, new StringParam("FAMILY"));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_NORMALIZED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_TagParam_SearchAllPartitions() {
|
||||
IIdType patientIdNull = createPatient(null, withActiveTrue(), withTag("http://system", "code"));
|
||||
|
@ -1185,6 +1414,211 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_RefParam_TargetPid_SearchOnePartition() {
|
||||
createUniqueCompositeSp();
|
||||
|
||||
IIdType patientId = createPatient(myPartitionId, withBirthdate("2020-01-01"));
|
||||
IIdType observationId = createObservation(myPartitionId, withSubject(patientId));
|
||||
|
||||
addReadPartition(myPartitionId);
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myObservationDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(observationId));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.PARTITION_ID='1'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.SRC_PATH='Observation.subject'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.TARGET_RESOURCE_ID='" + patientId.getIdPartAsLong() + "'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
|
||||
// Same query, different partition
|
||||
addReadPartition(2);
|
||||
myCaptureQueriesListener.clear();
|
||||
map = new SearchParameterMap();
|
||||
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
|
||||
map.setLoadSynchronous(true);
|
||||
results = myObservationDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_RefParam_TargetPid_SearchDefaultPartition() {
|
||||
createUniqueCompositeSp();
|
||||
|
||||
IIdType patientId = createPatient(null, withBirthdate("2020-01-01"));
|
||||
IIdType observationId = createObservation(null, withSubject(patientId));
|
||||
|
||||
addDefaultReadPartition();;
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myObservationDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(observationId));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.SRC_PATH='Observation.subject'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.TARGET_RESOURCE_ID='" + patientId.getIdPartAsLong() + "'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
|
||||
// Same query, different partition
|
||||
addReadPartition(2);
|
||||
myCaptureQueriesListener.clear();
|
||||
map = new SearchParameterMap();
|
||||
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
|
||||
map.setLoadSynchronous(true);
|
||||
results = myObservationDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_RefParam_TargetForcedId_SearchOnePartition() {
|
||||
createUniqueCompositeSp();
|
||||
|
||||
IIdType patientId = createPatient(myPartitionId, withId("ONE"), withBirthdate("2020-01-01"));
|
||||
IIdType observationId = createObservation(myPartitionId, withSubject(patientId));
|
||||
|
||||
addReadPartition(myPartitionId);
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myObservationDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(observationId));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID='1' "));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "and forcedid0_.RESOURCE_TYPE='Patient'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
|
||||
// Same query, different partition
|
||||
addReadPartition(2);
|
||||
myCaptureQueriesListener.clear();
|
||||
map = new SearchParameterMap();
|
||||
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
|
||||
map.setLoadSynchronous(true);
|
||||
results = myObservationDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_RefParam_TargetForcedId_SearchDefaultPartition() {
|
||||
createUniqueCompositeSp();
|
||||
|
||||
IIdType patientId = createPatient(null, withId("ONE"), withBirthdate("2020-01-01"));
|
||||
IIdType observationId = createObservation(null, withSubject(patientId));
|
||||
|
||||
addDefaultReadPartition();;
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myObservationDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(observationId));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.RESOURCE_TYPE='Patient' "));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
|
||||
// Same query, different partition
|
||||
addReadPartition(2);
|
||||
myCaptureQueriesListener.clear();
|
||||
map = new SearchParameterMap();
|
||||
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
|
||||
map.setLoadSynchronous(true);
|
||||
results = myObservationDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistory_Instance_CorrectTenant() {
|
||||
IIdType id = createPatient(1, withBirthdate("2020-01-01"));
|
||||
|
||||
// Update the patient
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
Patient p = new Patient();
|
||||
p.setActive(false);
|
||||
p.setId(id);
|
||||
myPatientDao.update(p);
|
||||
|
||||
addReadPartition(1);
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider results = myPatientDao.history(id, null, null, mySrd);
|
||||
List<String> ids = toUnqualifiedIdValues(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(id.withVersion("2").getValue(), id.withVersion("1").getValue()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistory_Instance_WrongTenant() {
|
||||
IIdType id = createPatient(1, withBirthdate("2020-01-01"));
|
||||
|
||||
// Update the patient
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
Patient p = new Patient();
|
||||
p.setActive(false);
|
||||
p.setId(id);
|
||||
myPatientDao.update(p);
|
||||
|
||||
addReadPartition(2);
|
||||
try {
|
||||
myPatientDao.history(id, null, null, mySrd);
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistory_Server() {
|
||||
try {
|
||||
mySystemDao.history(null, null, mySrd);
|
||||
fail();
|
||||
} catch (MethodNotAllowedException e) {
|
||||
assertEquals("Type- and Server- level history operation not supported on partitioned server", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistory_Type() {
|
||||
try {
|
||||
myPatientDao.history(null, null, mySrd);
|
||||
fail();
|
||||
} catch (MethodNotAllowedException e) {
|
||||
assertEquals("Type- and Server- level history operation not supported on partitioned server", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void createUniqueCompositeSp() {
|
||||
addCreateNoPartition();
|
||||
SearchParameter sp = new SearchParameter();
|
||||
|
@ -1285,10 +1719,10 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
return t -> t.getBirthDateElement().setValueAsString(theBirthdate);
|
||||
}
|
||||
|
||||
private Consumer<Patient> withId(String theId) {
|
||||
private <T extends IBaseResource> Consumer<T> withId(String theId) {
|
||||
return t -> {
|
||||
assertThat(theId, matchesPattern("[a-zA-Z0-9]+"));
|
||||
t.setId("Patient/" + theId);
|
||||
t.setId(theId);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1296,6 +1730,29 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
return t -> t.getMeta().addTag(theSystem, theCode, theCode);
|
||||
}
|
||||
|
||||
public IIdType createObservation(Integer thePartitionId, Consumer<Observation>... theModifiers) {
|
||||
if (thePartitionId != null) {
|
||||
addCreatePartition(thePartitionId, null);
|
||||
} else {
|
||||
addCreateNoPartition();
|
||||
}
|
||||
|
||||
|
||||
Observation observation = new Observation();
|
||||
for (Consumer<Observation> next : theModifiers) {
|
||||
next.accept(observation);
|
||||
}
|
||||
|
||||
if (isNotBlank(observation.getId())) {
|
||||
return myObservationDao.update(observation, mySrd).getId().toUnqualifiedVersionless();
|
||||
} else {
|
||||
return myObservationDao.create(observation, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
}
|
||||
|
||||
private Consumer<Observation> withSubject(IIdType theSubject) {
|
||||
return t -> t.getSubject().setReferenceElement(theSubject.toUnqualifiedVersionless());
|
||||
}
|
||||
|
||||
@Interceptor
|
||||
public static class MyInterceptor {
|
||||
|
|
|
@ -9,11 +9,13 @@ import org.hl7.fhir.r4.model.CodeType;
|
|||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -25,6 +27,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
@ -57,7 +60,7 @@ public class PartitionManagementProviderTest {
|
|||
|
||||
@Test
|
||||
public void testAddPartition() {
|
||||
when(myPartitionConfigSvc.createPartition(any())).thenAnswer(t -> t.getArgument(0, PartitionEntity.class));
|
||||
when(myPartitionConfigSvc.createPartition(any())).thenAnswer(createAnswer());
|
||||
|
||||
Parameters input = new Parameters();
|
||||
input.addParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(123));
|
||||
|
@ -81,6 +84,49 @@ public class PartitionManagementProviderTest {
|
|||
assertEquals("a description", ((StringType) response.getParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_DESC)).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatePartition() {
|
||||
when(myPartitionConfigSvc.updatePartition(any())).thenAnswer(createAnswer());
|
||||
|
||||
Parameters input = new Parameters();
|
||||
input.addParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(123));
|
||||
input.addParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, new CodeType("PARTITION-123"));
|
||||
input.addParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_DESC, new CodeType("a description"));
|
||||
|
||||
Parameters response = myClient
|
||||
.operation()
|
||||
.onServer()
|
||||
.named(ProviderConstants.PARTITION_MANAGEMENT_UPDATE_PARTITION)
|
||||
.withParameters(input)
|
||||
.encodedXml()
|
||||
.execute();
|
||||
|
||||
ourLog.info("Response:\n{}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response));
|
||||
verify(myPartitionConfigSvc, times(1)).updatePartition(any());
|
||||
verifyNoMoreInteractions(myPartitionConfigSvc);
|
||||
|
||||
assertEquals(123, ((IntegerType) response.getParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID)).getValue().intValue());
|
||||
assertEquals("PARTITION-123", ((StringType) response.getParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME)).getValue());
|
||||
assertEquals("a description", ((StringType) response.getParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_DESC)).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeletePartition() {
|
||||
Parameters input = new Parameters();
|
||||
input.addParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(123));
|
||||
Parameters response = myClient
|
||||
.operation()
|
||||
.onServer()
|
||||
.named(ProviderConstants.PARTITION_MANAGEMENT_DELETE_PARTITION)
|
||||
.withParameters(input)
|
||||
.encodedXml()
|
||||
.execute();
|
||||
|
||||
ourLog.info("Response:\n{}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response));
|
||||
verify(myPartitionConfigSvc, times(1)).deletePartition(eq(123));
|
||||
verifyNoMoreInteractions(myPartitionConfigSvc);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class MyConfig {
|
||||
|
||||
|
@ -96,4 +142,11 @@ public class PartitionManagementProviderTest {
|
|||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Answer<Object> createAnswer() {
|
||||
return t -> {
|
||||
return t.getArgument(0, PartitionEntity.class);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -167,60 +167,6 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConformanceOverrideNotAllowed() {
|
||||
myModelConfig.setDefaultSearchParamsCanBeOverridden(false);
|
||||
|
||||
CapabilityStatement conformance = ourClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
Map<String, CapabilityStatementRestResourceSearchParamComponent> map = extractSearchParams(conformance, "Patient");
|
||||
|
||||
CapabilityStatementRestResourceSearchParamComponent param = map.get("foo");
|
||||
assertNull(param);
|
||||
|
||||
param = map.get("gender");
|
||||
assertNotNull(param);
|
||||
|
||||
// Add a custom search parameter
|
||||
SearchParameter fooSp = new SearchParameter();
|
||||
fooSp.addBase("Patient");
|
||||
fooSp.setCode("foo");
|
||||
fooSp.setName("foo");
|
||||
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
|
||||
fooSp.setTitle("FOO SP");
|
||||
fooSp.setExpression("Patient.gender");
|
||||
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
|
||||
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
|
||||
mySearchParameterDao.create(fooSp, mySrd);
|
||||
|
||||
// Disable an existing parameter
|
||||
fooSp = new SearchParameter();
|
||||
fooSp.addBase("Patient");
|
||||
fooSp.setCode("gender");
|
||||
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
|
||||
fooSp.setTitle("Gender");
|
||||
fooSp.setExpression("Patient.gender");
|
||||
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
|
||||
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED);
|
||||
mySearchParameterDao.create(fooSp, mySrd);
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
conformance = ourClient
|
||||
.capabilities()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
map = extractSearchParams(conformance, "Patient");
|
||||
|
||||
param = map.get("foo");
|
||||
assertEquals("foo", param.getName());
|
||||
|
||||
param = map.get("gender");
|
||||
assertNotNull(param);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatingParamMarksCorrectResourcesForReindexing() {
|
||||
|
|
|
@ -165,6 +165,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
|
||||
confProvider.setSearchParamRegistry(ourSearchParamRegistry);
|
||||
|
||||
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.ProviderConstants;
|
||||
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
||||
import ca.uhn.fhir.jpa.partition.RequestTenantPartitionInterceptor;
|
||||
import ca.uhn.fhir.jpa.util.TestUtil;
|
||||
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
|
||||
import ca.uhn.fhir.rest.client.interceptor.UrlTenantSelectionInterceptor;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import static ca.uhn.fhir.jpa.partition.PartitionConfigSvcImpl.DEFAULT_PERSISTED_PARTITION_NAME;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
public class MultitenantServerR4Test extends BaseResourceProviderR4Test {
|
||||
|
||||
@Autowired
|
||||
private RequestTenantPartitionInterceptor myRequestTenantPartitionInterceptor;
|
||||
@Autowired
|
||||
private PartitionManagementProvider myPartitionManagementProvider;
|
||||
|
||||
private CapturingInterceptor myCapturingInterceptor;
|
||||
private UrlTenantSelectionInterceptor myTenantInterceptor;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
super.before();
|
||||
|
||||
myPartitionConfig.setPartitioningEnabled(true);
|
||||
ourRestServer.registerInterceptor(myRequestTenantPartitionInterceptor);
|
||||
ourRestServer.registerProvider(myPartitionManagementProvider);
|
||||
ourRestServer.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
|
||||
|
||||
myCapturingInterceptor = new CapturingInterceptor();
|
||||
ourClient.getInterceptorService().registerInterceptor(myCapturingInterceptor);
|
||||
|
||||
myTenantInterceptor = new UrlTenantSelectionInterceptor();
|
||||
ourClient.getInterceptorService().registerInterceptor(myTenantInterceptor);
|
||||
|
||||
createTenants();
|
||||
}
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
super.after();
|
||||
|
||||
myPartitionConfig.setPartitioningEnabled(new PartitionConfig().isPartitioningEnabled());
|
||||
ourRestServer.unregisterInterceptor(myRequestTenantPartitionInterceptor);
|
||||
ourRestServer.unregisterProvider(myPartitionManagementProvider);
|
||||
ourRestServer.setTenantIdentificationStrategy(null);
|
||||
|
||||
ourClient.getInterceptorService().unregisterAllInterceptors();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldLogClient() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchCapabilityStatement() {
|
||||
myTenantInterceptor.setTenantId("TENANT-A");
|
||||
CapabilityStatement cs = ourClient.capabilities().ofType(CapabilityStatement.class).execute();
|
||||
|
||||
assertEquals("HAPI FHIR Server", cs.getSoftware().getName());
|
||||
assertEquals(ourServerBase + "/TENANT-A/metadata", myCapturingInterceptor.getLastRequest().getUri());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateAndRead() {
|
||||
|
||||
myTenantInterceptor.setTenantId("TENANT-A");
|
||||
Patient patientA = new Patient();
|
||||
patientA.setActive(true);
|
||||
IIdType idA = ourClient.create().resource(patientA).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
myTenantInterceptor.setTenantId("TENANT-B");
|
||||
Patient patientB = new Patient();
|
||||
patientB.setActive(true);
|
||||
ourClient.create().resource(patientB).execute();
|
||||
|
||||
// Now read back
|
||||
|
||||
myTenantInterceptor.setTenantId("TENANT-A");
|
||||
Patient response = ourClient.read().resource(Patient.class).withId(idA).execute();
|
||||
assertTrue(response.getActive());
|
||||
|
||||
myTenantInterceptor.setTenantId("TENANT-B");
|
||||
try {
|
||||
ourClient.read().resource(Patient.class).withId(idA).execute();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_InvalidTenant() {
|
||||
|
||||
myTenantInterceptor.setTenantId("TENANT-ZZZ");
|
||||
Patient patientA = new Patient();
|
||||
patientA.setActive(true);
|
||||
try {
|
||||
ourClient.create().resource(patientA).execute();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), containsString("Unknown tenant: TENANT-ZZZ"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void createTenants() {
|
||||
myTenantInterceptor.setTenantId(DEFAULT_PERSISTED_PARTITION_NAME);
|
||||
|
||||
ourClient
|
||||
.operation()
|
||||
.onServer()
|
||||
.named(ProviderConstants.PARTITION_MANAGEMENT_ADD_PARTITION)
|
||||
.withParameter(Parameters.class, ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(1))
|
||||
.andParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, new CodeType("TENANT-A"))
|
||||
.execute();
|
||||
|
||||
ourClient
|
||||
.operation()
|
||||
.onServer()
|
||||
.named(ProviderConstants.PARTITION_MANAGEMENT_ADD_PARTITION)
|
||||
.withParameter(Parameters.class, ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(2))
|
||||
.andParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, new CodeType("TENANT-B"))
|
||||
.execute();
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
|
@ -182,61 +182,6 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConformanceOverrideNotAllowed() {
|
||||
myModelConfig.setDefaultSearchParamsCanBeOverridden(false);
|
||||
|
||||
CapabilityStatement conformance = ourClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
Map<String, CapabilityStatementRestResourceSearchParamComponent> map = extractSearchParams(conformance, "Patient");
|
||||
|
||||
CapabilityStatementRestResourceSearchParamComponent param = map.get("foo");
|
||||
assertNull(param);
|
||||
|
||||
param = map.get("gender");
|
||||
assertNotNull(param);
|
||||
|
||||
// Add a custom search parameter
|
||||
SearchParameter fooSp = new SearchParameter();
|
||||
fooSp.addBase("Patient");
|
||||
fooSp.setCode("foo");
|
||||
fooSp.setName("foo");
|
||||
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
|
||||
fooSp.setTitle("FOO SP");
|
||||
fooSp.setExpression("Patient.gender");
|
||||
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
|
||||
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
|
||||
mySearchParameterDao.create(fooSp, mySrd);
|
||||
|
||||
// Disable an existing parameter
|
||||
fooSp = new SearchParameter();
|
||||
fooSp.addBase("Patient");
|
||||
fooSp.setCode("gender");
|
||||
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
|
||||
fooSp.setTitle("Gender");
|
||||
fooSp.setExpression("Patient.gender");
|
||||
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
|
||||
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.RETIRED);
|
||||
mySearchParameterDao.create(fooSp, mySrd);
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
conformance = ourClient
|
||||
.fetchConformance()
|
||||
.ofType(CapabilityStatement.class)
|
||||
.execute();
|
||||
map = extractSearchParams(conformance, "Patient");
|
||||
|
||||
param = map.get("foo");
|
||||
assertEquals("foo", param.getName());
|
||||
|
||||
param = map.get("gender");
|
||||
assertNotNull(param);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatingParamMarksCorrectResourcesForReindexing() {
|
||||
Patient pat = new Patient();
|
||||
|
|
|
@ -14,6 +14,8 @@ import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
|||
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;
|
||||
import ca.uhn.fhir.jpa.search.cache.ISearchResultCacheSvc;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
|
@ -108,6 +110,8 @@ public class SearchCoordinatorSvcImplTest {
|
|||
private SearchBuilderFactory mySearchBuilderFactory;
|
||||
@Mock
|
||||
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
|
||||
@Mock
|
||||
private IRequestPartitionHelperService myPartitionHelperSvc;
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
|
@ -131,6 +135,7 @@ public class SearchCoordinatorSvcImplTest {
|
|||
mySvc.setInterceptorBroadcasterForUnitTest(myInterceptorBroadcaster);
|
||||
mySvc.setSearchBuilderFactoryForUnitTest(mySearchBuilderFactory);
|
||||
mySvc.setPersistedJpaBundleProviderFactoryForUnitTest(myPersistedJpaBundleProviderFactory);
|
||||
mySvc.setRequestPartitionHelperService(myPartitionHelperSvc);
|
||||
|
||||
DaoConfig daoConfig = new DaoConfig();
|
||||
mySvc.setDaoConfigForUnitTest(daoConfig);
|
||||
|
|
|
@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.migrate.taskdef.BaseTableColumnTypeTask;
|
|||
import ca.uhn.fhir.jpa.migrate.taskdef.CalculateHashesTask;
|
||||
import ca.uhn.fhir.jpa.migrate.tasks.api.BaseMigrationTasks;
|
||||
import ca.uhn.fhir.jpa.migrate.tasks.api.Builder;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.util.VersionEnum;
|
||||
|
||||
|
@ -479,7 +480,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxCoords
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.5")
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -502,7 +503,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxDate
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.10")
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -523,7 +524,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxNumber
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.14")
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -560,9 +561,9 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxQuantity
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.22")
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY_AND_UNITS", t -> ResourceIndexedSearchParamQuantity.calculateHashUnits(t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_UNITS")))
|
||||
.addCalculator("HASH_IDENTITY_SYS_UNITS", t -> ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_SYSTEM"), t.getString("SP_UNITS")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY_AND_UNITS", t -> ResourceIndexedSearchParamQuantity.calculateHashUnits(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_UNITS")))
|
||||
.addCalculator("HASH_IDENTITY_SYS_UNITS", t -> ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_SYSTEM"), t.getString("SP_UNITS")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -591,8 +592,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxString
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.28")
|
||||
.setColumnName("HASH_NORM_PREFIX")
|
||||
.addCalculator("HASH_NORM_PREFIX", t -> ResourceIndexedSearchParamString.calculateHashNormalized(new ModelConfig(), t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_VALUE_NORMALIZED")))
|
||||
.addCalculator("HASH_EXACT", t -> ResourceIndexedSearchParamString.calculateHashExact(t.getResourceType(), t.getParamName(), t.getString("SP_VALUE_EXACT")))
|
||||
.addCalculator("HASH_NORM_PREFIX", t -> ResourceIndexedSearchParamString.calculateHashNormalized(new PartitionConfig(), null, new ModelConfig(), t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_VALUE_NORMALIZED")))
|
||||
.addCalculator("HASH_EXACT", t -> ResourceIndexedSearchParamString.calculateHashExact(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE_EXACT")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -639,10 +640,10 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxToken
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.39")
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")))
|
||||
.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")))
|
||||
.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")))
|
||||
.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")))
|
||||
.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -669,8 +670,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxUri
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.44")
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_URI", t -> ResourceIndexedSearchParamUri.calculateHashUri(t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_URI")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_URI", t -> ResourceIndexedSearchParamUri.calculateHashUri(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_URI")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -703,7 +704,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
Boolean present = columnToBoolean(t.get("SP_PRESENT"));
|
||||
String resType = (String) t.get("RES_TYPE");
|
||||
String paramName = (String) t.get("PARAM_NAME");
|
||||
Long hash = SearchParamPresent.calculateHashPresence(resType, paramName, present);
|
||||
Long hash = SearchParamPresent.calculateHashPresence(new PartitionConfig(), null, resType, paramName, present);
|
||||
consolidateSearchParamPresenceIndexesTask.executeSql("HFJ_RES_PARAM_PRESENT", "update HFJ_RES_PARAM_PRESENT set HASH_PRESENCE = ? where PID = ?", hash, pid);
|
||||
});
|
||||
version.addTask(consolidateSearchParamPresenceIndexesTask);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.migrate.taskdef;
|
||||
|
||||
import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
|
||||
import ca.uhn.fhir.jpa.migrate.tasks.api.BaseMigrationTasks;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
||||
import ca.uhn.fhir.util.VersionEnum;
|
||||
import org.junit.Test;
|
||||
|
@ -41,7 +41,7 @@ public class ArbitrarySqlTaskTest extends BaseTest {
|
|||
Boolean present = (Boolean) t.get("SP_PRESENT");
|
||||
String resType = (String) t.get("RES_TYPE");
|
||||
String paramName = (String) t.get("PARAM_NAME");
|
||||
Long hash = SearchParamPresent.calculateHashPresence(resType, paramName, present);
|
||||
Long hash = SearchParamPresent.calculateHashPresence(new PartitionConfig(), null, resType, paramName, present);
|
||||
task.executeSql("HFJ_RES_PARAM_PRESENT", "update HFJ_RES_PARAM_PRESENT set HASH_PRESENT = ? where PID = ?", hash, pid);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.migrate.taskdef;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||
import ca.uhn.fhir.util.VersionEnum;
|
||||
|
@ -26,10 +27,10 @@ public class CalculateHashesTest extends BaseTest {
|
|||
CalculateHashesTask task = new CalculateHashesTask(VersionEnum.V3_5_0, "1");
|
||||
task.setTableName("HFJ_SPIDX_TOKEN");
|
||||
task.setColumnName("HASH_IDENTITY");
|
||||
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME")));
|
||||
task.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")));
|
||||
task.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME")));
|
||||
task.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")));
|
||||
task.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")));
|
||||
task.setBatchSize(1);
|
||||
getMigrator().addTask(task);
|
||||
|
||||
|
@ -73,10 +74,10 @@ public class CalculateHashesTest extends BaseTest {
|
|||
CalculateHashesTask task = new CalculateHashesTask(VersionEnum.V3_5_0, "1");
|
||||
task.setTableName("HFJ_SPIDX_TOKEN");
|
||||
task.setColumnName("HASH_IDENTITY");
|
||||
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(t.getResourceType(), t.getString("SP_NAME")));
|
||||
task.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")));
|
||||
task.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionConfig(), null, t.getResourceType(), t.getString("SP_NAME")));
|
||||
task.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")));
|
||||
task.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(new PartitionConfig(), null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")));
|
||||
task.setBatchSize(3);
|
||||
getMigrator().addTask(task);
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package ca.uhn.fhir.jpa.model.config;
|
||||
|
||||
/**
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public class PartitionConfig {
|
||||
|
||||
private boolean myPartitioningEnabled = true;
|
||||
private boolean myAllowReferencesAcrossPartitions;
|
||||
private boolean myIncludePartitionInSearchHashes;
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>true</code>) the <code>PARTITION_ID</code> value will be factored into the
|
||||
* hash values used in the <code>HFJ_SPIDX_xxx</code> tables, removing the need to explicitly add a selector
|
||||
* on this column in queries. If set to <code>false</code>, an additional selector is used instead, which may perform
|
||||
* better when using native database partitioning features.
|
||||
* <p>
|
||||
* This setting has no effect if partitioning is not enabled via {@link #isPartitioningEnabled()}.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isIncludePartitionInSearchHashes() {
|
||||
return myIncludePartitionInSearchHashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>true</code>) the <code>PARTITION_ID</code> value will be factored into the
|
||||
* hash values used in the <code>HFJ_SPIDX_xxx</code> tables, removing the need to explicitly add a selector
|
||||
* on this column in queries. If set to <code>false</code>, an additional selector is used instead, which may perform
|
||||
* better when using native database partitioning features.
|
||||
* <p>
|
||||
* This setting has no effect if partitioning is not enabled via {@link #isPartitioningEnabled()}.
|
||||
* </p>
|
||||
*/
|
||||
public void setIncludePartitionInSearchHashes(boolean theIncludePartitionInSearchHashes) {
|
||||
myIncludePartitionInSearchHashes = theIncludePartitionInSearchHashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled (default is <code>false</code>) the JPA server will support data partitioning
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public boolean isPartitioningEnabled() {
|
||||
return myPartitioningEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled (default is <code>false</code>) the JPA server will support data partitioning
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public void setPartitioningEnabled(boolean theMultiTenancyEnabled) {
|
||||
myPartitioningEnabled = theMultiTenancyEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should resources references be permitted to cross partition boundaries. Default is <code>false</code>.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public boolean isAllowReferencesAcrossPartitions() {
|
||||
return myAllowReferencesAcrossPartitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should resources references be permitted to cross partition boundaries. Default is <code>false</code>.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public void setAllowReferencesAcrossPartitions(boolean theAllowReferencesAcrossPartitions) {
|
||||
myAllowReferencesAcrossPartitions = theAllowReferencesAcrossPartitions;
|
||||
}
|
||||
}
|
|
@ -22,17 +22,21 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import org.hibernate.annotations.OptimisticLock;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Transient;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class BaseHasResource implements IBaseResourceEntity, IBasePersistedResource {
|
||||
public abstract class BaseHasResource extends BasePartitionable implements IBaseResourceEntity, IBasePersistedResource {
|
||||
|
||||
@Column(name = "RES_DELETED_AT", nullable = true)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
|
@ -58,31 +62,13 @@ public abstract class BaseHasResource implements IBaseResourceEntity, IBasePersi
|
|||
@OptimisticLock(excluded = true)
|
||||
private Date myUpdated;
|
||||
|
||||
@Embedded
|
||||
private PartitionId myPartitionId;
|
||||
|
||||
/**
|
||||
* This is here to support queries only, do not set this field directly
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = PartitionId.PARTITION_ID, insertable = false, updatable = false, nullable = true)
|
||||
private Integer myPartitionIdValue;
|
||||
|
||||
/**
|
||||
* This is stored as an optimization to avoid neeind to query for this
|
||||
* This is stored as an optimization to avoid needing to query for this
|
||||
* after an update
|
||||
*/
|
||||
@Transient
|
||||
private transient String myTransientForcedId;
|
||||
|
||||
public PartitionId getPartitionId() {
|
||||
return myPartitionId;
|
||||
}
|
||||
|
||||
public void setPartitionId(PartitionId thePartitionId) {
|
||||
myPartitionId = thePartitionId;
|
||||
}
|
||||
|
||||
public String getTransientForcedId() {
|
||||
return myTransientForcedId;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
@ -39,6 +40,7 @@ import javax.persistence.ManyToOne;
|
|||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Transient;
|
||||
import java.util.Date;
|
||||
|
||||
@MappedSuperclass
|
||||
|
@ -48,6 +50,7 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
|
|||
* Don't change this without careful consideration. You will break existing hashes!
|
||||
*/
|
||||
private static final HashFunction HASH_FUNCTION = Hashing.murmur3_128(0);
|
||||
|
||||
/**
|
||||
* Don't make this public 'cause nobody better be able to modify it!
|
||||
*/
|
||||
|
@ -80,6 +83,9 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
|
|||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date myUpdated;
|
||||
|
||||
@Transient
|
||||
private transient PartitionConfig myPartitionConfig;
|
||||
|
||||
/**
|
||||
* Subclasses may override
|
||||
*/
|
||||
|
@ -145,16 +151,30 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
|
|||
throw new UnsupportedOperationException("No parameter matcher for " + theParam);
|
||||
}
|
||||
|
||||
public static long calculateHashIdentity(String theResourceType, String theParamName) {
|
||||
return hash(theResourceType, theParamName);
|
||||
public void setPartitionConfig(PartitionConfig thePartitionConfig) {
|
||||
myPartitionConfig = thePartitionConfig;
|
||||
}
|
||||
|
||||
public PartitionConfig getPartitionConfig() {
|
||||
return myPartitionConfig;
|
||||
}
|
||||
|
||||
public static long calculateHashIdentity(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String theResourceType, String theParamName) {
|
||||
return hash(thePartitionConfig, thePartitionId, theResourceType, theParamName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a fast and consistent hashing algorithm to a set of strings
|
||||
*/
|
||||
static long hash(String... theValues) {
|
||||
static long hash(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String... theValues) {
|
||||
Hasher hasher = HASH_FUNCTION.newHasher();
|
||||
|
||||
if (thePartitionConfig.isPartitioningEnabled() && thePartitionConfig.isIncludePartitionInSearchHashes() && thePartitionId != null) {
|
||||
if (thePartitionId.getPartitionId() != null) {
|
||||
hasher.putInt(thePartitionId.getPartitionId());
|
||||
}
|
||||
}
|
||||
|
||||
for (String next : theValues) {
|
||||
if (next == null) {
|
||||
hasher.putByte((byte) 0);
|
||||
|
|
|
@ -36,7 +36,7 @@ import javax.persistence.*;
|
|||
* so don't reuse these names
|
||||
*/
|
||||
})
|
||||
public class ForcedId {
|
||||
public class ForcedId extends BasePartitionable {
|
||||
|
||||
public static final int MAX_FORCED_ID_LENGTH = 100;
|
||||
public static final String IDX_FORCEDID_TYPE_FID = "IDX_FORCEDID_TYPE_FID";
|
||||
|
@ -64,9 +64,6 @@ public class ForcedId {
|
|||
@Column(name = "RESOURCE_TYPE", nullable = true, length = 100, updatable = true)
|
||||
private String myResourceType;
|
||||
|
||||
@Embedded
|
||||
private PartitionId myPartitionId;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -98,11 +95,4 @@ public class ForcedId {
|
|||
return myId;
|
||||
}
|
||||
|
||||
public PartitionId getPartitionId() {
|
||||
return myPartitionId;
|
||||
}
|
||||
|
||||
public void setPartitionId(PartitionId thePartitionId) {
|
||||
myPartitionId = thePartitionId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,9 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
// TODO: move this to ca.uhn.fhir.jpa.model.config
|
||||
public class ModelConfig {
|
||||
|
||||
/**
|
||||
* Default {@link #getTreatReferencesAsLogical() logical URL bases}. Includes the following
|
||||
* values:
|
||||
|
@ -55,7 +57,7 @@ public class ModelConfig {
|
|||
private boolean myAllowExternalReferences = false;
|
||||
private Set<String> myTreatBaseUrlsAsLocal = new HashSet<>();
|
||||
private Set<String> myTreatReferencesAsLogical = new HashSet<>(DEFAULT_LOGICAL_BASE_URLS);
|
||||
private boolean myDefaultSearchParamsCanBeOverridden = false;
|
||||
private boolean myDefaultSearchParamsCanBeOverridden = true;
|
||||
private Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>();
|
||||
private String myEmailFromAddress = "noreply@unknown.com";
|
||||
private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH;
|
||||
|
@ -76,7 +78,7 @@ public class ModelConfig {
|
|||
* the behaviour of the default search parameters.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value for this setting is {@code false}
|
||||
* The default value for this setting is {@code true}
|
||||
* </p>
|
||||
*/
|
||||
public boolean isDefaultSearchParamsCanBeOverridden() {
|
||||
|
@ -92,7 +94,7 @@ public class ModelConfig {
|
|||
* the behaviour of the default search parameters.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value for this setting is {@code false}
|
||||
* The default value for this setting is {@code true}
|
||||
* </p>
|
||||
*/
|
||||
public void setDefaultSearchParamsCanBeOverridden(boolean theDefaultSearchParamsCanBeOverridden) {
|
||||
|
|
|
@ -29,7 +29,7 @@ import javax.persistence.*;
|
|||
@Index(name = "IDX_RESVERPROV_REQUESTID", columnList = "REQUEST_ID")
|
||||
})
|
||||
@Entity
|
||||
public class ResourceHistoryProvenanceEntity {
|
||||
public class ResourceHistoryProvenanceEntity extends BasePartitionable {
|
||||
|
||||
public static final int SOURCE_URI_LENGTH = 100;
|
||||
|
||||
|
@ -47,9 +47,6 @@ public class ResourceHistoryProvenanceEntity {
|
|||
private String mySourceUri;
|
||||
@Column(name = "REQUEST_ID", length = Constants.REQUEST_ID_LENGTH, nullable = true)
|
||||
private String myRequestId;
|
||||
// FIXME: make sure this gets populated
|
||||
@Embedded
|
||||
private PartitionId myPartitionId;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -86,11 +83,4 @@ public class ResourceHistoryProvenanceEntity {
|
|||
return myId;
|
||||
}
|
||||
|
||||
public PartitionId getPartitionId() {
|
||||
return myPartitionId;
|
||||
}
|
||||
|
||||
public void setPartitionId(PartitionId thePartitionId) {
|
||||
myPartitionId = thePartitionId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
@ -61,7 +62,8 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP
|
|||
public ResourceIndexedSearchParamCoords() {
|
||||
}
|
||||
|
||||
public ResourceIndexedSearchParamCoords(String theResourceType, String theParamName, double theLatitude, double theLongitude) {
|
||||
public ResourceIndexedSearchParamCoords(PartitionConfig thePartitionConfig, String theResourceType, String theParamName, double theLatitude, double theLongitude) {
|
||||
setPartitionConfig(thePartitionConfig);
|
||||
setResourceType(theResourceType);
|
||||
setParamName(theParamName);
|
||||
setLatitude(theLatitude);
|
||||
|
@ -74,7 +76,7 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP
|
|||
if (myHashIdentity == null) {
|
||||
String resourceType = getResourceType();
|
||||
String paramName = getParamName();
|
||||
setHashIdentity(calculateHashIdentity(resourceType, paramName));
|
||||
setHashIdentity(calculateHashIdentity(getPartitionConfig(), getPartitionId(), resourceType, paramName));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
|
@ -77,7 +78,8 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceIndexedSearchParamDate(String theResourceType, String theParamName, Date theLow, Date theHigh, String theOriginalValue) {
|
||||
public ResourceIndexedSearchParamDate(PartitionConfig thePartitionConfig, String theResourceType, String theParamName, Date theLow, Date theHigh, String theOriginalValue) {
|
||||
setPartitionConfig(thePartitionConfig);
|
||||
setResourceType(theResourceType);
|
||||
setParamName(theParamName);
|
||||
setValueLow(theLow);
|
||||
|
@ -91,7 +93,7 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
if (myHashIdentity == null) {
|
||||
String resourceType = getResourceType();
|
||||
String paramName = getParamName();
|
||||
setHashIdentity(calculateHashIdentity(resourceType, paramName));
|
||||
setHashIdentity(calculateHashIdentity(getPartitionConfig(), getPartitionId(), resourceType, paramName));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,6 +207,7 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
return false;
|
||||
}
|
||||
|
||||
// FIXME: below is always true
|
||||
boolean result = true;
|
||||
if (lowerBound != null) {
|
||||
result &= (myValueLow.after(lowerBound) || myValueLow.equals(lowerBound));
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.BigDecimalNumericFieldBridge;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.NumberParam;
|
||||
|
@ -65,7 +66,8 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
|||
public ResourceIndexedSearchParamNumber() {
|
||||
}
|
||||
|
||||
public ResourceIndexedSearchParamNumber(String theResourceType, String theParamName, BigDecimal theValue) {
|
||||
public ResourceIndexedSearchParamNumber(PartitionConfig thePartitionConfig, String theResourceType, String theParamName, BigDecimal theValue) {
|
||||
setPartitionConfig(thePartitionConfig);
|
||||
setResourceType(theResourceType);
|
||||
setParamName(theParamName);
|
||||
setValue(theValue);
|
||||
|
@ -77,7 +79,7 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
|||
if (myHashIdentity == null) {
|
||||
String resourceType = getResourceType();
|
||||
String paramName = getParamName();
|
||||
setHashIdentity(calculateHashIdentity(resourceType, paramName));
|
||||
setHashIdentity(calculateHashIdentity(getPartitionConfig(), getPartitionId(), resourceType, paramName));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,11 +108,6 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
|||
return b.isEquals();
|
||||
}
|
||||
|
||||
public Long getHashIdentity() {
|
||||
calculateHashes();
|
||||
return myHashIdentity;
|
||||
}
|
||||
|
||||
public void setHashIdentity(Long theHashIdentity) {
|
||||
myHashIdentity = theHashIdentity;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.BigDecimalNumericFieldBridge;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
|
@ -91,8 +92,9 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
|||
}
|
||||
|
||||
|
||||
public ResourceIndexedSearchParamQuantity(String theResourceType, String theParamName, BigDecimal theValue, String theSystem, String theUnits) {
|
||||
public ResourceIndexedSearchParamQuantity(PartitionConfig thePartitionConfig, String theResourceType, String theParamName, BigDecimal theValue, String theSystem, String theUnits) {
|
||||
this();
|
||||
setPartitionConfig(thePartitionConfig);
|
||||
setResourceType(theResourceType);
|
||||
setParamName(theParamName);
|
||||
setSystem(theSystem);
|
||||
|
@ -108,9 +110,9 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
|||
String paramName = getParamName();
|
||||
String units = getUnits();
|
||||
String system = getSystem();
|
||||
setHashIdentity(calculateHashIdentity(resourceType, paramName));
|
||||
setHashIdentityAndUnits(calculateHashUnits(resourceType, paramName, units));
|
||||
setHashIdentitySystemAndUnits(calculateHashSystemAndUnits(resourceType, paramName, system, units));
|
||||
setHashIdentity(calculateHashIdentity(getPartitionConfig(), getPartitionId(), resourceType, paramName));
|
||||
setHashIdentityAndUnits(calculateHashUnits(getPartitionConfig(), getPartitionId(), resourceType, paramName, units));
|
||||
setHashIdentitySystemAndUnits(calculateHashSystemAndUnits(getPartitionConfig(), getPartitionId(), resourceType, paramName, system, units));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,12 +275,12 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
|||
return retval;
|
||||
}
|
||||
|
||||
public static long calculateHashSystemAndUnits(String theResourceType, String theParamName, String theSystem, String theUnits) {
|
||||
return hash(theResourceType, theParamName, theSystem, theUnits);
|
||||
public static long calculateHashSystemAndUnits(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String theResourceType, String theParamName, String theSystem, String theUnits) {
|
||||
return hash(thePartitionConfig, thePartitionId, theResourceType, theParamName, theSystem, theUnits);
|
||||
}
|
||||
|
||||
public static long calculateHashUnits(String theResourceType, String theParamName, String theUnits) {
|
||||
return hash(theResourceType, theParamName, theUnits);
|
||||
public static long calculateHashUnits(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String theResourceType, String theParamName, String theUnits) {
|
||||
return hash(thePartitionConfig, thePartitionId, theResourceType, theParamName, theUnits);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
|
@ -55,50 +56,6 @@ import static org.apache.commons.lang3.StringUtils.left;
|
|||
@Index(name = "IDX_SP_STRING_RESID", columnList = "RES_ID")
|
||||
})
|
||||
@Indexed()
|
||||
//@AnalyzerDefs({
|
||||
// @AnalyzerDef(name = "autocompleteEdgeAnalyzer",
|
||||
// tokenizer = @TokenizerDef(factory = PatternTokenizerFactory.class, params= {
|
||||
// @Parameter(name="pattern", value="(.*)"),
|
||||
// @Parameter(name="group", value="1")
|
||||
// }),
|
||||
// filters = {
|
||||
// @TokenFilterDef(factory = LowerCaseFilterFactory.class),
|
||||
// @TokenFilterDef(factory = StopFilterFactory.class),
|
||||
// @TokenFilterDef(factory = EdgeNGramFilterFactory.class, params = {
|
||||
// @Parameter(name = "minGramSize", value = "3"),
|
||||
// @Parameter(name = "maxGramSize", value = "50")
|
||||
// }),
|
||||
// }),
|
||||
// @AnalyzerDef(name = "autocompletePhoneticAnalyzer",
|
||||
// tokenizer = @TokenizerDef(factory=StandardTokenizerFactory.class),
|
||||
// filters = {
|
||||
// @TokenFilterDef(factory=StandardFilterFactory.class),
|
||||
// @TokenFilterDef(factory=StopFilterFactory.class),
|
||||
// @TokenFilterDef(factory=PhoneticFilterFactory.class, params = {
|
||||
// @Parameter(name="encoder", value="DoubleMetaphone")
|
||||
// }),
|
||||
// @TokenFilterDef(factory=SnowballPorterFilterFactory.class, params = {
|
||||
// @Parameter(name="language", value="English")
|
||||
// })
|
||||
// }),
|
||||
// @AnalyzerDef(name = "autocompleteNGramAnalyzer",
|
||||
// tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
|
||||
// filters = {
|
||||
// @TokenFilterDef(factory = WordDelimiterFilterFactory.class),
|
||||
// @TokenFilterDef(factory = LowerCaseFilterFactory.class),
|
||||
// @TokenFilterDef(factory = NGramFilterFactory.class, params = {
|
||||
// @Parameter(name = "minGramSize", value = "3"),
|
||||
// @Parameter(name = "maxGramSize", value = "20")
|
||||
// }),
|
||||
// }),
|
||||
// @AnalyzerDef(name = "standardAnalyzer",
|
||||
// tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
|
||||
// filters = {
|
||||
// @TokenFilterDef(factory = LowerCaseFilterFactory.class),
|
||||
// }) // Def
|
||||
// }
|
||||
//)
|
||||
//@formatter:on
|
||||
public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchParam {
|
||||
|
||||
/*
|
||||
|
@ -151,7 +108,8 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
super();
|
||||
}
|
||||
|
||||
public ResourceIndexedSearchParamString(ModelConfig theModelConfig, String theResourceType, String theParamName, String theValueNormalized, String theValueExact) {
|
||||
public ResourceIndexedSearchParamString(PartitionConfig thePartitionConfig, ModelConfig theModelConfig, String theResourceType, String theParamName, String theValueNormalized, String theValueExact) {
|
||||
setPartitionConfig(thePartitionConfig);
|
||||
setModelConfig(theModelConfig);
|
||||
setResourceType(theResourceType);
|
||||
setParamName(theParamName);
|
||||
|
@ -172,9 +130,9 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
String paramName = getParamName();
|
||||
String valueNormalized = getValueNormalized();
|
||||
String valueExact = getValueExact();
|
||||
setHashNormalizedPrefix(calculateHashNormalized(myModelConfig, resourceType, paramName, valueNormalized));
|
||||
setHashExact(calculateHashExact(resourceType, paramName, valueExact));
|
||||
setHashIdentity(calculateHashIdentity(resourceType, paramName));
|
||||
setHashNormalizedPrefix(calculateHashNormalized(getPartitionConfig(), getPartitionId(), myModelConfig, resourceType, paramName, valueNormalized));
|
||||
setHashExact(calculateHashExact(getPartitionConfig(), getPartitionId(), resourceType, paramName, valueExact));
|
||||
setHashIdentity(calculateHashIdentity(getPartitionConfig(), getPartitionId(), resourceType, paramName));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,11 +251,11 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
return b.build();
|
||||
}
|
||||
|
||||
public static long calculateHashExact(String theResourceType, String theParamName, String theValueExact) {
|
||||
return hash(theResourceType, theParamName, theValueExact);
|
||||
public static long calculateHashExact(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String theResourceType, String theParamName, String theValueExact) {
|
||||
return hash(thePartitionConfig, thePartitionId, theResourceType, theParamName, theValueExact);
|
||||
}
|
||||
|
||||
public static long calculateHashNormalized(ModelConfig theModelConfig, String theResourceType, String theParamName, String theValueNormalized) {
|
||||
public static long calculateHashNormalized(PartitionConfig thePartitionConfig, PartitionId thePartitionId, ModelConfig theModelConfig, String theResourceType, String theParamName, String theValueNormalized) {
|
||||
/*
|
||||
* If we're not allowing contained searches, we'll add the first
|
||||
* bit of the normalized value to the hash. This helps to
|
||||
|
@ -309,8 +267,7 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
hashPrefixLength = 0;
|
||||
}
|
||||
|
||||
long hash = hash(theResourceType, theParamName, left(theValueNormalized, hashPrefixLength));
|
||||
return hash;
|
||||
return hash(thePartitionConfig, thePartitionId, theResourceType, theParamName, left(theValueNormalized, hashPrefixLength));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -101,8 +102,9 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceIndexedSearchParamToken(String theResourceType, String theParamName, String theSystem, String theValue) {
|
||||
public ResourceIndexedSearchParamToken(PartitionConfig thePartitionConfig, String theResourceType, String theParamName, String theSystem, String theValue) {
|
||||
super();
|
||||
setPartitionConfig(thePartitionConfig);
|
||||
setResourceType(theResourceType);
|
||||
setParamName(theParamName);
|
||||
setSystem(theSystem);
|
||||
|
@ -117,10 +119,10 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
String paramName = getParamName();
|
||||
String system = getSystem();
|
||||
String value = getValue();
|
||||
setHashIdentity(calculateHashIdentity(resourceType, paramName));
|
||||
setHashSystem(calculateHashSystem(resourceType, paramName, system));
|
||||
setHashSystemAndValue(calculateHashSystemAndValue(resourceType, paramName, system, value));
|
||||
setHashValue(calculateHashValue(resourceType, paramName, value));
|
||||
setHashIdentity(calculateHashIdentity(getPartitionConfig(), getPartitionId(), resourceType, paramName));
|
||||
setHashSystem(calculateHashSystem(getPartitionConfig(), getPartitionId(), resourceType, paramName, system));
|
||||
setHashSystemAndValue(calculateHashSystemAndValue(getPartitionConfig(), getPartitionId(), resourceType, paramName, system, value));
|
||||
setHashValue(calculateHashValue(getPartitionConfig(), getPartitionId(), resourceType, paramName, value));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +192,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
|
||||
@Override
|
||||
public void setId(Long theId) {
|
||||
myId =theId;
|
||||
myId = theId;
|
||||
}
|
||||
|
||||
public String getSystem() {
|
||||
|
@ -244,39 +246,39 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
return false;
|
||||
}
|
||||
TokenParam token = (TokenParam) theParam;
|
||||
boolean retval = false;
|
||||
boolean retVal = false;
|
||||
String valueString = defaultString(getValue());
|
||||
String tokenValueString = defaultString(token.getValue());
|
||||
|
||||
// Only match on system if it wasn't specified
|
||||
if (token.getSystem() == null || token.getSystem().isEmpty()) {
|
||||
if (valueString.equalsIgnoreCase(tokenValueString)) {
|
||||
retval = true;
|
||||
retVal = true;
|
||||
}
|
||||
} else if (tokenValueString == null || tokenValueString.isEmpty()) {
|
||||
if (token.getSystem().equalsIgnoreCase(getSystem())) {
|
||||
retval = true;
|
||||
retVal = true;
|
||||
}
|
||||
} else {
|
||||
if (token.getSystem().equalsIgnoreCase(getSystem()) &&
|
||||
valueString.equalsIgnoreCase(tokenValueString)) {
|
||||
retval = true;
|
||||
retVal = true;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static long calculateHashSystem(String theResourceType, String theParamName, String theSystem) {
|
||||
return hash(theResourceType, theParamName, trim(theSystem));
|
||||
public static long calculateHashSystem(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String theResourceType, String theParamName, String theSystem) {
|
||||
return hash(thePartitionConfig, thePartitionId, theResourceType, theParamName, trim(theSystem));
|
||||
}
|
||||
|
||||
public static long calculateHashSystemAndValue(String theResourceType, String theParamName, String theSystem, String theValue) {
|
||||
return hash(theResourceType, theParamName, defaultString(trim(theSystem)), trim(theValue));
|
||||
public static long calculateHashSystemAndValue(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String theResourceType, String theParamName, String theSystem, String theValue) {
|
||||
return hash(thePartitionConfig, thePartitionId, theResourceType, theParamName, defaultString(trim(theSystem)), trim(theValue));
|
||||
}
|
||||
|
||||
public static long calculateHashValue(String theResourceType, String theParamName, String theValue) {
|
||||
public static long calculateHashValue(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String theResourceType, String theParamName, String theValue) {
|
||||
String value = trim(theValue);
|
||||
return hash(theResourceType, theParamName, value);
|
||||
return hash(thePartitionConfig, thePartitionId, theResourceType, theParamName, value);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -80,7 +81,8 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
|||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceIndexedSearchParamUri(String theResourceType, String theParamName, String theUri) {
|
||||
public ResourceIndexedSearchParamUri(PartitionConfig thePartitionConfig, String theResourceType, String theParamName, String theUri) {
|
||||
setPartitionConfig(thePartitionConfig);
|
||||
setResourceType(theResourceType);
|
||||
setParamName(theParamName);
|
||||
setUri(theUri);
|
||||
|
@ -93,8 +95,8 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
|||
String resourceType = getResourceType();
|
||||
String paramName = getParamName();
|
||||
String uri = getUri();
|
||||
setHashIdentity(calculateHashIdentity(resourceType, paramName));
|
||||
setHashUri(calculateHashUri(resourceType, paramName, uri));
|
||||
setHashIdentity(calculateHashIdentity(getPartitionConfig(), getPartitionId(), resourceType, paramName));
|
||||
setHashUri(calculateHashUri(getPartitionConfig(), getPartitionId(), resourceType, paramName, uri));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,8 +198,8 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
|||
return defaultString(getUri()).equalsIgnoreCase(uri.getValueNotNull());
|
||||
}
|
||||
|
||||
public static long calculateHashUri(String theResourceType, String theParamName, String theUri) {
|
||||
return hash(theResourceType, theParamName, theUri);
|
||||
public static long calculateHashUri(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String theResourceType, String theParamName, String theUri) {
|
||||
return hash(thePartitionConfig, thePartitionId, theResourceType, theParamName, theUri);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -123,6 +123,10 @@ public class ResourceLink extends BaseResourceIndex {
|
|||
mySourcePath = theSourcePath;
|
||||
}
|
||||
|
||||
public Long getSourceResourcePid() {
|
||||
return mySourceResourcePid;
|
||||
}
|
||||
|
||||
public ResourceTable getSourceResource() {
|
||||
return mySourceResource;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
|
@ -52,6 +53,8 @@ public class SearchParamPresent extends BasePartitionable implements Serializabl
|
|||
private transient String myParamName;
|
||||
@Column(name = "HASH_PRESENCE")
|
||||
private Long myHashPresence;
|
||||
@Transient
|
||||
private transient PartitionConfig myPartitionConfig;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -67,7 +70,7 @@ public class SearchParamPresent extends BasePartitionable implements Serializabl
|
|||
String resourceType = getResource().getResourceType();
|
||||
String paramName = getParamName();
|
||||
boolean present = myPresent;
|
||||
setHashPresence(calculateHashPresence(resourceType, paramName, present));
|
||||
setHashPresence(calculateHashPresence(getPartitionConfig(), getPartitionId(), resourceType, paramName, present));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,9 +117,17 @@ public class SearchParamPresent extends BasePartitionable implements Serializabl
|
|||
return b.build();
|
||||
}
|
||||
|
||||
public static long calculateHashPresence(String theResourceType, String theParamName, Boolean thePresent) {
|
||||
public void setPartitionConfig(PartitionConfig thePartitionConfig) {
|
||||
myPartitionConfig = thePartitionConfig;
|
||||
}
|
||||
|
||||
public PartitionConfig getPartitionConfig() {
|
||||
return myPartitionConfig;
|
||||
}
|
||||
|
||||
public static long calculateHashPresence(PartitionConfig thePartitionConfig, PartitionId thePartitionId, String theResourceType, String theParamName, Boolean thePresent) {
|
||||
String string = thePresent != null ? Boolean.toString(thePresent) : Boolean.toString(false);
|
||||
return BaseResourceIndexedSearchParam.hash(theResourceType, theParamName, string);
|
||||
return BaseResourceIndexedSearchParam.hash(thePartitionConfig, thePartitionId, theResourceType, theParamName, string);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,9 +32,15 @@ public class ProviderConstants {
|
|||
/**
|
||||
* Operation name: update partition
|
||||
*/
|
||||
public static final String PARTITION_MANAGEMENT_MODIFY_PARTITION = "partition-management-update-partition";
|
||||
public static final String PARTITION_MANAGEMENT_UPDATE_PARTITION = "partition-management-update-partition";
|
||||
|
||||
/**
|
||||
* Operation name: update partition
|
||||
*/
|
||||
public static final String PARTITION_MANAGEMENT_DELETE_PARTITION = "partition-management-delete-partition";
|
||||
|
||||
public static final String PARTITION_MANAGEMENT_PARTITION_ID = "partitionId";
|
||||
public static final String PARTITION_MANAGEMENT_PARTITION_NAME = "partitionName";
|
||||
public static final String PARTITION_MANAGEMENT_PARTITION_DESC = "description";
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.model.entity;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -35,8 +36,8 @@ public class ResourceIndexedSearchParamDateTest {
|
|||
|
||||
@Test
|
||||
public void equalsIsTrueForMatchingNullDates() {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", null, null, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", null, null, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", null, null, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", null, null, "SomeValue");
|
||||
|
||||
assertTrue(param.equals(param2));
|
||||
assertTrue(param2.equals(param));
|
||||
|
@ -45,8 +46,8 @@ public class ResourceIndexedSearchParamDateTest {
|
|||
|
||||
@Test
|
||||
public void equalsIsTrueForMatchingDates() {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1B, date2B, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", date1B, date2B, "SomeValue");
|
||||
|
||||
assertTrue(param.equals(param2));
|
||||
assertTrue(param2.equals(param));
|
||||
|
@ -55,8 +56,8 @@ public class ResourceIndexedSearchParamDateTest {
|
|||
|
||||
@Test
|
||||
public void equalsIsTrueForMatchingTimeStampsThatMatch() {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1A, timestamp2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1B, timestamp2B, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", timestamp1A, timestamp2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", timestamp1B, timestamp2B, "SomeValue");
|
||||
|
||||
assertTrue(param.equals(param2));
|
||||
assertTrue(param2.equals(param));
|
||||
|
@ -67,8 +68,8 @@ public class ResourceIndexedSearchParamDateTest {
|
|||
// other will be equivalent but will be a java.sql.Timestamp. Equals should work in both directions.
|
||||
@Test
|
||||
public void equalsIsTrueForMixedTimestampsAndDates() {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1A, timestamp2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", timestamp1A, timestamp2A, "SomeValue");
|
||||
|
||||
assertTrue(param.equals(param2));
|
||||
assertTrue(param2.equals(param));
|
||||
|
@ -77,8 +78,8 @@ public class ResourceIndexedSearchParamDateTest {
|
|||
|
||||
@Test
|
||||
public void equalsIsFalseForNonMatchingDates() {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date2A, date1A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", date2A, date1A, "SomeValue");
|
||||
|
||||
assertFalse(param.equals(param2));
|
||||
assertFalse(param2.equals(param));
|
||||
|
@ -87,8 +88,8 @@ public class ResourceIndexedSearchParamDateTest {
|
|||
|
||||
@Test
|
||||
public void equalsIsFalseForNonMatchingDatesNullCase() {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", null, null, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", null, null, "SomeValue");
|
||||
|
||||
assertFalse(param.equals(param2));
|
||||
assertFalse(param2.equals(param));
|
||||
|
@ -97,8 +98,8 @@ public class ResourceIndexedSearchParamDateTest {
|
|||
|
||||
@Test
|
||||
public void equalsIsFalseForNonMatchingTimeStamps() {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp1A, timestamp2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp2A, timestamp1A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", timestamp1A, timestamp2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", timestamp2A, timestamp1A, "SomeValue");
|
||||
|
||||
assertFalse(param.equals(param2));
|
||||
assertFalse(param2.equals(param));
|
||||
|
@ -107,8 +108,8 @@ public class ResourceIndexedSearchParamDateTest {
|
|||
|
||||
@Test
|
||||
public void equalsIsFalseForMixedTimestampsAndDatesThatDoNotMatch() {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate("Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate("Patient", "SomeResource", timestamp2A, timestamp1A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", date1A, date2A, "SomeValue");
|
||||
ResourceIndexedSearchParamDate param2 = new ResourceIndexedSearchParamDate(new PartitionConfig(), "Patient", "SomeResource", timestamp2A, timestamp1A, "SomeValue");
|
||||
|
||||
assertFalse(param.equals(param2));
|
||||
assertFalse(param2.equals(param));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.model.entity;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
@ -10,7 +11,7 @@ import static org.junit.Assert.assertNotEquals;
|
|||
public class ResourceIndexedSearchParamQuantityTest {
|
||||
|
||||
private ResourceIndexedSearchParamQuantity createParam(String theParamName, String theValue, String theSystem, String theUnits) {
|
||||
ResourceIndexedSearchParamQuantity token = new ResourceIndexedSearchParamQuantity("Patient", theParamName, new BigDecimal(theValue), theSystem, theUnits);
|
||||
ResourceIndexedSearchParamQuantity token = new ResourceIndexedSearchParamQuantity(new PartitionConfig(), "Patient", theParamName, new BigDecimal(theValue), theSystem, theUnits);
|
||||
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||
return token;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.model.entity;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -10,7 +11,7 @@ public class ResourceIndexedSearchParamStringTest {
|
|||
|
||||
@Test
|
||||
public void testHashFunctions() {
|
||||
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString(new ModelConfig(), "Patient", "NAME", "value", "VALUE");
|
||||
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString(new PartitionConfig(), new ModelConfig(), "Patient", "NAME", "value", "VALUE");
|
||||
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||
|
||||
// Make sure our hashing function gives consistent results
|
||||
|
@ -20,7 +21,7 @@ public class ResourceIndexedSearchParamStringTest {
|
|||
|
||||
@Test
|
||||
public void testHashFunctionsPrefixOnly() {
|
||||
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString(new ModelConfig(), "Patient", "NAME", "vZZZZZZZZZZZZZZZZ", "VZZZZZZzzzZzzzZ");
|
||||
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString(new PartitionConfig(), new ModelConfig(), "Patient", "NAME", "vZZZZZZZZZZZZZZZZ", "VZZZZZZzzzZzzzZ");
|
||||
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||
|
||||
// Should be the same as in testHashFunctions()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.model.entity;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -9,7 +10,7 @@ public class ResourceIndexedSearchParamTokenTest {
|
|||
|
||||
@Test
|
||||
public void testHashFunctions() {
|
||||
ResourceIndexedSearchParamToken token = new ResourceIndexedSearchParamToken("Patient", "NAME", "SYSTEM", "VALUE");
|
||||
ResourceIndexedSearchParamToken token = new ResourceIndexedSearchParamToken(new PartitionConfig(), "Patient", "NAME", "SYSTEM", "VALUE");
|
||||
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||
|
||||
// Make sure our hashing function gives consistent results
|
||||
|
@ -20,7 +21,7 @@ public class ResourceIndexedSearchParamTokenTest {
|
|||
|
||||
@Test
|
||||
public void testHashFunctionsWithOverlapNames() {
|
||||
ResourceIndexedSearchParamToken token = new ResourceIndexedSearchParamToken("Patient", "NAME", "SYSTEM", "VALUE");
|
||||
ResourceIndexedSearchParamToken token = new ResourceIndexedSearchParamToken(new PartitionConfig(), "Patient", "NAME", "SYSTEM", "VALUE");
|
||||
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||
|
||||
// Make sure our hashing function gives consistent results
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.model.entity;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -9,7 +10,7 @@ public class ResourceIndexedSearchParamUriTest {
|
|||
|
||||
@Test
|
||||
public void testHashFunctions() {
|
||||
ResourceIndexedSearchParamUri token = new ResourceIndexedSearchParamUri("Patient", "NAME", "http://example.com");
|
||||
ResourceIndexedSearchParamUri token = new ResourceIndexedSearchParamUri(new PartitionConfig(), "Patient", "NAME", "http://example.com");
|
||||
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||
|
||||
// Make sure our hashing function gives consistent results
|
||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
|
@ -59,6 +60,8 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
@Autowired
|
||||
private ModelConfig myModelConfig;
|
||||
@Autowired
|
||||
private PartitionConfig myPartitionConfig;
|
||||
private Set<String> myIgnoredForSearchDatatypes;
|
||||
private BaseRuntimeChildDefinition myQuantityValueValueChild;
|
||||
private BaseRuntimeChildDefinition myQuantitySystemValueChild;
|
||||
|
@ -455,7 +458,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
String system = extractValueAsString(myQuantitySystemValueChild, theValue);
|
||||
String code = extractValueAsString(myQuantityCodeValueChild, theValue);
|
||||
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(theResourceType, theSearchParam.getName(), nextValueValue, system, code);
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(myPartitionConfig, theResourceType, theSearchParam.getName(), nextValueValue, system, code);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
|
||||
|
@ -470,7 +473,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
String nextValueString = "urn:iso:std:iso:4217";
|
||||
String nextValueCode = extractValueAsString(myMoneyCurrencyChild, theValue);
|
||||
String searchParamName = theSearchParam.getName();
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(theResourceType, searchParamName, nextValueValue, nextValueString, nextValueCode);
|
||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(myPartitionConfig, theResourceType, searchParamName, nextValueValue, nextValueString, nextValueCode);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
|
||||
|
@ -547,7 +550,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
Date end = extractValueAsDate(myPeriodEndValueChild, theValue);
|
||||
|
||||
if (start != null || end != null) {
|
||||
ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(theResourceType, theSearchParam.getName(), start, end, startAsString);
|
||||
ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(myPartitionConfig, theResourceType, theSearchParam.getName(), start, end, startAsString);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
@ -581,7 +584,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
if (!dates.isEmpty()) {
|
||||
ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(theResourceType, theSearchParam.getName(), dates.first(), dates.last(), firstValue);
|
||||
ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(myPartitionConfig, theResourceType, theSearchParam.getName(), dates.first(), dates.last(), firstValue);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
@ -592,7 +595,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
BigDecimal value = extractValueAsBigDecimal(myDurationValueValueChild, theValue);
|
||||
if (value != null) {
|
||||
value = normalizeQuantityContainingTimeUnitsIntoDaysForNumberParam(system, code, value);
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(theResourceType, theSearchParam.getName(), value);
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(myPartitionConfig, theResourceType, theSearchParam.getName(), value);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
@ -603,7 +606,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
String system = extractValueAsString(myQuantitySystemValueChild, theValue);
|
||||
String code = extractValueAsString(myQuantityCodeValueChild, theValue);
|
||||
value = normalizeQuantityContainingTimeUnitsIntoDaysForNumberParam(system, code, value);
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(theResourceType, theSearchParam.getName(), value);
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(myPartitionConfig, theResourceType, theSearchParam.getName(), value);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
@ -613,7 +616,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
IPrimitiveType<Integer> value = (IPrimitiveType<Integer>) theValue;
|
||||
if (value.getValue() != null) {
|
||||
BigDecimal valueDecimal = new BigDecimal(value.getValue());
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(theResourceType, theSearchParam.getName(), valueDecimal);
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(myPartitionConfig, theResourceType, theSearchParam.getName(), valueDecimal);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
|
||||
|
@ -624,7 +627,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
IPrimitiveType<BigDecimal> value = (IPrimitiveType<BigDecimal>) theValue;
|
||||
if (value.getValue() != null) {
|
||||
BigDecimal valueDecimal = value.getValue();
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(theResourceType, theSearchParam.getName(), valueDecimal);
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(myPartitionConfig, theResourceType, theSearchParam.getName(), valueDecimal);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
|
||||
|
@ -651,7 +654,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
if (latitude != null && longitude != null) {
|
||||
double normalizedLatitude = Point.normalizeLatitude(latitude.doubleValue());
|
||||
double normalizedLongitude = Point.normalizeLongitude(longitude.doubleValue());
|
||||
ResourceIndexedSearchParamCoords nextEntity = new ResourceIndexedSearchParamCoords(theResourceType, theSearchParam.getName(), normalizedLatitude, normalizedLongitude);
|
||||
ResourceIndexedSearchParamCoords nextEntity = new ResourceIndexedSearchParamCoords(myPartitionConfig, theResourceType, theSearchParam.getName(), normalizedLatitude, normalizedLongitude);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
@ -772,7 +775,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
private void addDateTimeTypes(String theResourceType, Set<ResourceIndexedSearchParamDate> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
|
||||
IPrimitiveType<Date> nextBaseDateTime = (IPrimitiveType<Date>) theValue;
|
||||
if (nextBaseDateTime.getValue() != null) {
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(theResourceType, theSearchParam.getName(), nextBaseDateTime.getValue(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString());
|
||||
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(myPartitionConfig, theResourceType, theSearchParam.getName(), nextBaseDateTime.getValue(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString());
|
||||
theParams.add(param);
|
||||
}
|
||||
}
|
||||
|
@ -782,7 +785,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
IPrimitiveType<?> value = (IPrimitiveType<?>) theValue;
|
||||
String valueAsString = value.getValueAsString();
|
||||
if (isNotBlank(valueAsString)) {
|
||||
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(theResourceType, theSearchParam.getName(), valueAsString);
|
||||
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(myPartitionConfig, theResourceType, theSearchParam.getName(), valueAsString);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
@ -801,7 +804,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
valueNormalized = valueNormalized.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), theResourceType, searchParamName, valueNormalized, value);
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(myPartitionConfig, getModelConfig(), theResourceType, searchParamName, valueNormalized, value);
|
||||
|
||||
Set params = theParams;
|
||||
params.add(nextEntity);
|
||||
|
@ -820,7 +823,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
ResourceIndexedSearchParamToken nextEntity;
|
||||
nextEntity = new ResourceIndexedSearchParamToken(theResourceType, theSearchParam.getName(), system, value);
|
||||
nextEntity = new ResourceIndexedSearchParamToken(myPartitionConfig, theResourceType, theSearchParam.getName(), system, value);
|
||||
theParams.add(nextEntity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -36,6 +37,7 @@ public interface IResourceLinkResolver {
|
|||
* <p>
|
||||
* This method returns an {@link IResourceLookup} so as to avoid needing to resolve the entire resource.
|
||||
*
|
||||
* @param thePartitionId The partition ID of the target resource
|
||||
* @param theSearchParam The param that is being indexed
|
||||
* @param theSourcePath The path within the resource where this reference was found
|
||||
* @param theSourceResourceId The ID of the resource containing the reference to the target being resolved
|
||||
|
@ -44,7 +46,7 @@ public interface IResourceLinkResolver {
|
|||
* @param theReference The reference being resolved
|
||||
* @param theRequest The incoming request, if any
|
||||
*/
|
||||
IResourceLookup findTargetResource(RuntimeSearchParam theSearchParam, String theSourcePath, IIdType theSourceResourceId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest);
|
||||
IResourceLookup findTargetResource(PartitionId thePartitionId, RuntimeSearchParam theSearchParam, String theSourcePath, IIdType theSourceResourceId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest);
|
||||
|
||||
void validateTypeOrThrowException(Class<? extends IBaseResource> theType);
|
||||
|
||||
|
|
|
@ -21,23 +21,28 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.compare;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public final class ResourceIndexedSearchParams {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceIndexedSearchParams.class);
|
||||
|
||||
final public Collection<ResourceIndexedSearchParamString> myStringParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamToken> myTokenParams = new HashSet<>();
|
||||
final public Collection<ResourceIndexedSearchParamNumber> myNumberParams = new ArrayList<>();
|
||||
|
@ -85,7 +90,6 @@ public final class ResourceIndexedSearchParams {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public Collection<ResourceLink> getResourceLinks() {
|
||||
return myLinks;
|
||||
}
|
||||
|
@ -126,6 +130,186 @@ public final class ResourceIndexedSearchParams {
|
|||
}
|
||||
}
|
||||
|
||||
public void calculateHashes(Collection<? extends BaseResourceIndex> theStringParams) {
|
||||
for (BaseResourceIndex next : theStringParams) {
|
||||
next.calculateHashes();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getPopulatedResourceLinkParameters() {
|
||||
return myPopulatedResourceLinkParameters;
|
||||
}
|
||||
|
||||
public boolean matchParam(ModelConfig theModelConfig, String theResourceName, String theParamName, RuntimeSearchParam theParamDef, IQueryParameterType theParam) {
|
||||
if (theParamDef == null) {
|
||||
return false;
|
||||
}
|
||||
Collection<? extends BaseResourceIndexedSearchParam> resourceParams;
|
||||
switch (theParamDef.getParamType()) {
|
||||
case TOKEN:
|
||||
resourceParams = myTokenParams;
|
||||
break;
|
||||
case QUANTITY:
|
||||
resourceParams = myQuantityParams;
|
||||
break;
|
||||
case STRING:
|
||||
resourceParams = myStringParams;
|
||||
break;
|
||||
case NUMBER:
|
||||
resourceParams = myNumberParams;
|
||||
break;
|
||||
case URI:
|
||||
resourceParams = myUriParams;
|
||||
break;
|
||||
case DATE:
|
||||
resourceParams = myDateParams;
|
||||
break;
|
||||
case REFERENCE:
|
||||
return matchResourceLinks(theModelConfig, theResourceName, theParamName, theParam, theParamDef.getPath());
|
||||
case COMPOSITE:
|
||||
case HAS:
|
||||
case SPECIAL:
|
||||
default:
|
||||
resourceParams = null;
|
||||
}
|
||||
if (resourceParams == null) {
|
||||
return false;
|
||||
}
|
||||
Predicate<BaseResourceIndexedSearchParam> namedParamPredicate = param ->
|
||||
param.getParamName().equalsIgnoreCase(theParamName) &&
|
||||
param.matches(theParam);
|
||||
|
||||
return resourceParams.stream().anyMatch(namedParamPredicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Replace with the method below
|
||||
*/
|
||||
// KHS This needs to be public as libraries outside of hapi call it directly
|
||||
@Deprecated
|
||||
public boolean matchResourceLinks(String theResourceName, String theParamName, IQueryParameterType theParam, String theParamPath) {
|
||||
return matchResourceLinks(new ModelConfig(), theResourceName, theParamName, theParam, theParamPath);
|
||||
}
|
||||
|
||||
// KHS This needs to be public as libraries outside of hapi call it directly
|
||||
public boolean matchResourceLinks(ModelConfig theModelConfig, String theResourceName, String theParamName, IQueryParameterType theParam, String theParamPath) {
|
||||
ReferenceParam reference = (ReferenceParam) theParam;
|
||||
|
||||
Predicate<ResourceLink> namedParamPredicate = resourceLink ->
|
||||
searchParameterPathMatches(theResourceName, resourceLink, theParamName, theParamPath)
|
||||
&& resourceIdMatches(theModelConfig, resourceLink, reference);
|
||||
|
||||
return myLinks.stream().anyMatch(namedParamPredicate);
|
||||
}
|
||||
|
||||
private boolean resourceIdMatches(ModelConfig theModelConfig, ResourceLink theResourceLink, ReferenceParam theReference) {
|
||||
String baseUrl = theReference.getBaseUrl();
|
||||
if (isNotBlank(baseUrl)) {
|
||||
if (!theModelConfig.getTreatBaseUrlsAsLocal().contains(baseUrl)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String targetType = theResourceLink.getTargetResourceType();
|
||||
String targetId = theResourceLink.getTargetResourceId();
|
||||
|
||||
assert isNotBlank(targetType);
|
||||
assert isNotBlank(targetId);
|
||||
|
||||
if (theReference.hasResourceType()) {
|
||||
if (!theReference.getResourceType().equals(targetType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetId.equals(theReference.getIdPart())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean searchParameterPathMatches(String theResourceName, ResourceLink theResourceLink, String theParamName, String theParamPath) {
|
||||
String sourcePath = theResourceLink.getSourcePath();
|
||||
return sourcePath.equalsIgnoreCase(theParamPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResourceIndexedSearchParams{" +
|
||||
"stringParams=" + myStringParams +
|
||||
", tokenParams=" + myTokenParams +
|
||||
", numberParams=" + myNumberParams +
|
||||
", quantityParams=" + myQuantityParams +
|
||||
", dateParams=" + myDateParams +
|
||||
", uriParams=" + myUriParams +
|
||||
", coordsParams=" + myCoordsParams +
|
||||
", compositeStringUniques=" + myCompositeStringUniques +
|
||||
", links=" + myLinks +
|
||||
'}';
|
||||
}
|
||||
|
||||
public void findMissingSearchParams(PartitionConfig thePartitionConfig, ModelConfig theModelConfig, ResourceTable theEntity, Set<Entry<String, RuntimeSearchParam>> theActiveSearchParams) {
|
||||
findMissingSearchParams(thePartitionConfig, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.STRING, myStringParams);
|
||||
findMissingSearchParams(thePartitionConfig, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.NUMBER, myNumberParams);
|
||||
findMissingSearchParams(thePartitionConfig, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.QUANTITY, myQuantityParams);
|
||||
findMissingSearchParams(thePartitionConfig, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.DATE, myDateParams);
|
||||
findMissingSearchParams(thePartitionConfig, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.URI, myUriParams);
|
||||
findMissingSearchParams(thePartitionConfig, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.TOKEN, myTokenParams);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <RT extends BaseResourceIndexedSearchParam> void findMissingSearchParams(PartitionConfig thePartitionConfig, ModelConfig theModelConfig, ResourceTable theEntity, Set<Map.Entry<String, RuntimeSearchParam>> activeSearchParams, RestSearchParameterTypeEnum type,
|
||||
Collection<RT> paramCollection) {
|
||||
for (Map.Entry<String, RuntimeSearchParam> nextEntry : activeSearchParams) {
|
||||
String nextParamName = nextEntry.getKey();
|
||||
if (nextEntry.getValue().getParamType() == type) {
|
||||
boolean haveParam = false;
|
||||
for (BaseResourceIndexedSearchParam nextParam : paramCollection) {
|
||||
if (nextParam.getParamName().equals(nextParamName)) {
|
||||
haveParam = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveParam) {
|
||||
BaseResourceIndexedSearchParam param;
|
||||
switch (type) {
|
||||
case DATE:
|
||||
param = new ResourceIndexedSearchParamDate();
|
||||
break;
|
||||
case NUMBER:
|
||||
param = new ResourceIndexedSearchParamNumber();
|
||||
break;
|
||||
case QUANTITY:
|
||||
param = new ResourceIndexedSearchParamQuantity();
|
||||
break;
|
||||
case STRING:
|
||||
param = new ResourceIndexedSearchParamString()
|
||||
.setModelConfig(theModelConfig);
|
||||
break;
|
||||
case TOKEN:
|
||||
param = new ResourceIndexedSearchParamToken();
|
||||
break;
|
||||
case URI:
|
||||
param = new ResourceIndexedSearchParamUri();
|
||||
break;
|
||||
case COMPOSITE:
|
||||
case HAS:
|
||||
case REFERENCE:
|
||||
case SPECIAL:
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
param.setPartitionConfig(thePartitionConfig);
|
||||
param.setResource(theEntity);
|
||||
param.setMissing(true);
|
||||
param.setParamName(nextParamName);
|
||||
paramCollection.add((RT) param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to create a set of all possible combinations of
|
||||
|
@ -207,186 +391,4 @@ public final class ResourceIndexedSearchParams {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public void calculateHashes(Collection<? extends BaseResourceIndex> theStringParams) {
|
||||
for (BaseResourceIndex next : theStringParams) {
|
||||
next.calculateHashes();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getPopulatedResourceLinkParameters() {
|
||||
return myPopulatedResourceLinkParameters;
|
||||
}
|
||||
|
||||
public boolean matchParam(ModelConfig theModelConfig, String theResourceName, String theParamName, RuntimeSearchParam theParamDef, IQueryParameterType theParam) {
|
||||
if (theParamDef == null) {
|
||||
return false;
|
||||
}
|
||||
Collection<? extends BaseResourceIndexedSearchParam> resourceParams;
|
||||
switch (theParamDef.getParamType()) {
|
||||
case TOKEN:
|
||||
resourceParams = myTokenParams;
|
||||
break;
|
||||
case QUANTITY:
|
||||
resourceParams = myQuantityParams;
|
||||
break;
|
||||
case STRING:
|
||||
resourceParams = myStringParams;
|
||||
break;
|
||||
case NUMBER:
|
||||
resourceParams = myNumberParams;
|
||||
break;
|
||||
case URI:
|
||||
resourceParams = myUriParams;
|
||||
break;
|
||||
case DATE:
|
||||
resourceParams = myDateParams;
|
||||
break;
|
||||
case REFERENCE:
|
||||
return matchResourceLinks(theModelConfig, theResourceName, theParamName, theParam, theParamDef.getPath());
|
||||
case COMPOSITE:
|
||||
case HAS:
|
||||
case SPECIAL:
|
||||
default:
|
||||
resourceParams = null;
|
||||
}
|
||||
if (resourceParams == null) {
|
||||
return false;
|
||||
}
|
||||
Predicate<BaseResourceIndexedSearchParam> namedParamPredicate = param ->
|
||||
param.getParamName().equalsIgnoreCase(theParamName) &&
|
||||
param.matches(theParam);
|
||||
|
||||
return resourceParams.stream().anyMatch(namedParamPredicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Replace with the method below
|
||||
*/
|
||||
// KHS This needs to be public as libraries outside of hapi call it directly
|
||||
@Deprecated
|
||||
public boolean matchResourceLinks(String theResourceName, String theParamName, IQueryParameterType theParam, String theParamPath) {
|
||||
return matchResourceLinks(new ModelConfig(), theResourceName, theParamName, theParam, theParamPath);
|
||||
}
|
||||
|
||||
// KHS This needs to be public as libraries outside of hapi call it directly
|
||||
public boolean matchResourceLinks(ModelConfig theModelConfig, String theResourceName, String theParamName, IQueryParameterType theParam, String theParamPath) {
|
||||
ReferenceParam reference = (ReferenceParam)theParam;
|
||||
|
||||
Predicate<ResourceLink> namedParamPredicate = resourceLink ->
|
||||
searchParameterPathMatches(theResourceName, resourceLink, theParamName, theParamPath)
|
||||
&& resourceIdMatches(theModelConfig, resourceLink, reference);
|
||||
|
||||
return myLinks.stream().anyMatch(namedParamPredicate);
|
||||
}
|
||||
|
||||
private boolean resourceIdMatches(ModelConfig theModelConfig, ResourceLink theResourceLink, ReferenceParam theReference) {
|
||||
String baseUrl = theReference.getBaseUrl();
|
||||
if (isNotBlank(baseUrl)) {
|
||||
if (!theModelConfig.getTreatBaseUrlsAsLocal().contains(baseUrl)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String targetType = theResourceLink.getTargetResourceType();
|
||||
String targetId = theResourceLink.getTargetResourceId();
|
||||
|
||||
assert isNotBlank(targetType);
|
||||
assert isNotBlank(targetId);
|
||||
|
||||
if (theReference.hasResourceType()) {
|
||||
if (!theReference.getResourceType().equals(targetType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetId.equals(theReference.getIdPart())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean searchParameterPathMatches(String theResourceName, ResourceLink theResourceLink, String theParamName, String theParamPath) {
|
||||
String sourcePath = theResourceLink.getSourcePath();
|
||||
return sourcePath.equalsIgnoreCase(theParamPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResourceIndexedSearchParams{" +
|
||||
"stringParams=" + myStringParams +
|
||||
", tokenParams=" + myTokenParams +
|
||||
", numberParams=" + myNumberParams +
|
||||
", quantityParams=" + myQuantityParams +
|
||||
", dateParams=" + myDateParams +
|
||||
", uriParams=" + myUriParams +
|
||||
", coordsParams=" + myCoordsParams +
|
||||
", compositeStringUniques=" + myCompositeStringUniques +
|
||||
", links=" + myLinks +
|
||||
'}';
|
||||
}
|
||||
|
||||
public void findMissingSearchParams(ModelConfig theModelConfig, ResourceTable theEntity, Set<Entry<String, RuntimeSearchParam>> theActiveSearchParams) {
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.STRING, myStringParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.NUMBER, myNumberParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.QUANTITY, myQuantityParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.DATE, myDateParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.URI, myUriParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.TOKEN, myTokenParams);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <RT extends BaseResourceIndexedSearchParam> void findMissingSearchParams(ModelConfig theModelConfig, ResourceTable theEntity, Set<Map.Entry<String, RuntimeSearchParam>> activeSearchParams, RestSearchParameterTypeEnum type,
|
||||
Collection<RT> paramCollection) {
|
||||
for (Map.Entry<String, RuntimeSearchParam> nextEntry : activeSearchParams) {
|
||||
String nextParamName = nextEntry.getKey();
|
||||
if (nextEntry.getValue().getParamType() == type) {
|
||||
boolean haveParam = false;
|
||||
for (BaseResourceIndexedSearchParam nextParam : paramCollection) {
|
||||
if (nextParam.getParamName().equals(nextParamName)) {
|
||||
haveParam = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveParam) {
|
||||
BaseResourceIndexedSearchParam param;
|
||||
switch (type) {
|
||||
case DATE:
|
||||
param = new ResourceIndexedSearchParamDate();
|
||||
break;
|
||||
case NUMBER:
|
||||
param = new ResourceIndexedSearchParamNumber();
|
||||
break;
|
||||
case QUANTITY:
|
||||
param = new ResourceIndexedSearchParamQuantity();
|
||||
break;
|
||||
case STRING:
|
||||
param = new ResourceIndexedSearchParamString()
|
||||
.setModelConfig(theModelConfig);
|
||||
break;
|
||||
case TOKEN:
|
||||
param = new ResourceIndexedSearchParamToken();
|
||||
break;
|
||||
case URI:
|
||||
param = new ResourceIndexedSearchParamUri();
|
||||
break;
|
||||
case COMPOSITE:
|
||||
case HAS:
|
||||
case REFERENCE:
|
||||
case SPECIAL:
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
param.setResource(theEntity);
|
||||
param.setMissing(true);
|
||||
param.setParamName(nextParamName);
|
||||
paramCollection.add((RT) param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -24,8 +24,10 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
|
||||
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
|
@ -37,7 +39,6 @@ import org.hl7.fhir.instance.model.api.IBaseReference;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
@ -52,6 +53,8 @@ public class ResourceLinkExtractor {
|
|||
@Autowired
|
||||
private ModelConfig myModelConfig;
|
||||
@Autowired
|
||||
private PartitionConfig myPartitionConfig;
|
||||
@Autowired
|
||||
private FhirContext myContext;
|
||||
@Autowired
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
|
@ -174,7 +177,14 @@ public class ResourceLinkExtractor {
|
|||
|
||||
IResourceLookup targetResource = theResourceIdToResolvedTarget.get(theNextId.getValue());
|
||||
if (targetResource == null) {
|
||||
targetResource = theResourceLinkResolver.findTargetResource(nextSpDef, theNextPathsUnsplit, theNextId, theTypeString, theType, theReference, theRequest);
|
||||
|
||||
// If we're allowing references across partitions, we just don't include the partition ID when resolving the target
|
||||
PartitionId targetPartitionId = theEntity.getPartitionId();
|
||||
if (myPartitionConfig.isAllowReferencesAcrossPartitions()) {
|
||||
targetPartitionId = null;
|
||||
}
|
||||
|
||||
targetResource = theResourceLinkResolver.findTargetResource(targetPartitionId, nextSpDef, theNextPathsUnsplit, theNextId, theTypeString, theType, theReference, theRequest);
|
||||
}
|
||||
|
||||
if (targetResource == null) {
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.searchparam.matcher;
|
|||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
|
||||
import ca.uhn.fhir.jpa.model.cross.ResourceLookup;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.IResourceLinkResolver;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
|
@ -34,7 +35,7 @@ import org.springframework.stereotype.Service;
|
|||
public class InlineResourceLinkResolver implements IResourceLinkResolver {
|
||||
|
||||
@Override
|
||||
public IResourceLookup findTargetResource(RuntimeSearchParam theSearchParam, String theSourcePath, IIdType theSourceResourceId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest) {
|
||||
public IResourceLookup findTargetResource(PartitionId thePartitionId, RuntimeSearchParam theSearchParam, String theSourcePath, IIdType theSourceResourceId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest) {
|
||||
|
||||
/*
|
||||
* TODO: JA - This gets used during runtime in-memory matching for subscription. It's not
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue