Skip to content

Commit

Permalink
issue #1708 add custom compartment reference params for faster compar…
Browse files Browse the repository at this point in the history
…tment searches

Signed-off-by: Robin Arnold <robin.arnold23@ibm.com>
  • Loading branch information
punktilious committed Nov 18, 2020
1 parent a98a3db commit d862936
Show file tree
Hide file tree
Showing 13 changed files with 1,137 additions and 263 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.UTC;

import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
Expand All @@ -24,7 +22,6 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;

import com.ibm.fhir.config.FHIRRequestContext;
import com.ibm.fhir.persistence.exception.FHIRPersistenceException;
import com.ibm.fhir.persistence.jdbc.dao.api.IResourceReferenceDAO;
import com.ibm.fhir.persistence.jdbc.dao.api.JDBCIdentityCache;
Expand All @@ -41,6 +38,8 @@
import com.ibm.fhir.persistence.jdbc.exception.FHIRPersistenceDataAccessException;
import com.ibm.fhir.persistence.jdbc.impl.ParameterTransactionDataImpl;
import com.ibm.fhir.schema.control.FhirSchemaConstants;
import com.ibm.fhir.search.util.ReferenceValue;
import com.ibm.fhir.search.util.ReferenceValue.ReferenceType;

/**
* Batch insert into the parameter values tables. Avoids having to create one stored procedure
Expand All @@ -51,11 +50,6 @@
public class ParameterVisitorBatchDAO implements ExtractedParameterValueVisitor, AutoCloseable {
private static final Logger logger = Logger.getLogger(ParameterVisitorBatchDAO.class.getName());

private static final String HISTORY = "_history";
private static final String HTTP = "http:";
private static final String HTTPS = "https:";
private static final String URN = "urn:";

// the connection to use for the inserts
private final Connection connection;

Expand Down Expand Up @@ -118,9 +112,6 @@ public class ParameterVisitorBatchDAO implements ExtractedParameterValueVisitor,
// The common cache for all our identity lookup needs
private final JDBCIdentityCache identityCache;

// The server base "https://example.com:9443/" extracted the first time we need it
private String serverBase;

// If not null, we stash certain parameter data here for insertion later
private final ParameterTransactionDataImpl transactionData;

Expand Down Expand Up @@ -746,58 +737,9 @@ private boolean isBase(ExtractedParameterValue param) {
return "Resource".equals(param.getBase());
}

/**
* Get the leading part of the url e.g. https://example.com
* @return
*/
private String getServerUrl() throws FHIRPersistenceException {

if (this.serverBase != null) {
return this.serverBase;
}

String uri = FHIRRequestContext.get().getOriginalRequestUri();

// request URI is not set for all unit-tests, so we need to take that into account
if (uri == null) {
return null;
}

try {
StringBuilder result = new StringBuilder();
URL url = new URL(uri);

result.append(url.getProtocol());
result.append("://");
result.append(url.getHost());

if (url.getPort() != -1) {
result.append(":");
result.append(url.getPort());
}

// https://example.com:9443/
result.append("/");

final String endpoint = "fhir-server/api/v4/";
if (uri.contains(endpoint)) {
result.append(endpoint);
}

// Cache the result so we don't have to compute it over and over
this.serverBase = result.toString();
return this.serverBase;
} catch (MalformedURLException x) {
// not very likely at this point
logger.severe("Malformed server URL: " + uri);
throw new FHIRPersistenceException("Server URL is malformed!");
}
}

@Override
public void visit(ReferenceParmVal rpv) throws FHIRPersistenceException {
String valueString = rpv.getValueString();
if (valueString == null || valueString.isEmpty()) {
if (rpv.getRefValue() == null) {
return;
}

Expand All @@ -809,66 +751,31 @@ public void visit(ReferenceParmVal rpv) throws FHIRPersistenceException {
throw new FHIRPersistenceException("Resource type not found in cache: '" + resourceType + "'");
}

final String base = getServerUrl();
ResourceTokenValueRec rec;
if (base != null && valueString.startsWith(base)) {
// - relative reference https://example.com/Patient/123
// Because this reference is to a local FHIR resource (inside this server), we need use the correct
// resource type name (assigned as the code system)
// - https://localhost:9443/fhir-server/api/v4/Patient/1234
// - https://example.com/Patient/1234
// - https://example.com/Patient/1234/_history/2
valueString = valueString.substring(base.length());

// Patient/1234
// Patient/1234/_history/2
String[] tokens = valueString.split("/");
if (tokens.length > 1) {
String refResourceType = tokens[0];
String refLogicalId = tokens[1];
Integer refVersion = null;
if (tokens.length == 4 && HISTORY.equals(tokens[2])) {
// versioned reference
refVersion = Integer.parseInt(tokens[3]);
}

// Store a token value configured as a reference to another resource
rec = new ResourceTokenValueRec(parameterNameId, resourceType, resourceTypeId, logicalResourceId, refResourceType, refLogicalId, refVersion);
} else {
// stored as a token with the default system
rec = new ResourceTokenValueRec(parameterNameId, resourceType, resourceTypeId, logicalResourceId, TokenParmVal.DEFAULT_TOKEN_SYSTEM, valueString);
}
} else if (valueString.startsWith(HTTP) || valueString.startsWith(HTTPS) || valueString.startsWith(URN)) {
// - absolute URL ==> http://some.system/a/fhir/resource/path
// - absolute URI ==> urn:uuid:53fefa32-1111-2222-3333-55ee120877b7
// stored as a token with the default system
rec = new ResourceTokenValueRec(parameterNameId, resourceType, resourceTypeId, logicalResourceId, TokenParmVal.DEFAULT_TOKEN_SYSTEM, valueString);
} else if (valueString.startsWith("#")) {
// - Internal ==> #fragmentid1
// stored as a token value with the default system
rec = new ResourceTokenValueRec(parameterNameId, resourceType, resourceTypeId, logicalResourceId, TokenParmVal.DEFAULT_TOKEN_SYSTEM, valueString);
} else {
// - Relative ==> Patient/1234
// - Relative ==> Patient/1234/_history/2
String[] tokens = valueString.split("/");
if (tokens.length > 1) {
String refResourceType = tokens[0];
String refLogicalId = tokens[1];
Integer refVersion = null;
if (tokens.length == 4 && HISTORY.equals(tokens[2])) {
// versioned reference
refVersion = Integer.parseInt(tokens[3]);
}
// The ReferenceValue has already been processed to convert the reference to
// the required standard form, ready for insertion as a token value.
ReferenceValue refValue = rpv.getRefValue();

// Store a token value configured as a reference to another resource
rec = new ResourceTokenValueRec(parameterNameId, resourceType, resourceTypeId, logicalResourceId, refResourceType, refLogicalId, refVersion);
// Ignore references containing only a "display" element (apparently supported by the spec,
// but contains nothing useful to store because there's no searchable value).
String refResourceType = refValue.getTargetResourceType();
String refLogicalId = refValue.getValue();
Integer refVersion = refValue.getVersion();
ResourceTokenValueRec rec;

} else {
// SearchReferenceTest system integration tests require support for arbitrary reference strings
// - Relative ==> 1234
final String codeSystem = TokenParmVal.DEFAULT_TOKEN_SYSTEM;
rec = new ResourceTokenValueRec(parameterNameId, resourceType, resourceTypeId, logicalResourceId, codeSystem, valueString);
}
if (refValue.getType() == ReferenceType.DISPLAY_ONLY || refValue.getType() == ReferenceType.INVALID) {
// protect against code regression. Invalid/improper references should be
// filtered out already.
logger.warning("Invalid reference parameter type: " + resourceType + "." + rpv.getName() + " type=" + refValue.getType().name());
throw new IllegalArgumentException("Invalid reference parameter value. See server log for details.");
}

if (refResourceType != null) {
// Store a token value configured as a reference to another resource
rec = new ResourceTokenValueRec(parameterNameId, resourceType, resourceTypeId, logicalResourceId, refResourceType, refLogicalId, refVersion);
} else {
// stored as a token with the default system
rec = new ResourceTokenValueRec(parameterNameId, resourceType, resourceTypeId, logicalResourceId, TokenParmVal.DEFAULT_TOKEN_SYSTEM, refLogicalId);
}

if (this.transactionData != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.ibm.fhir.persistence.exception.FHIRPersistenceException;
import com.ibm.fhir.search.SearchConstants.Type;
import com.ibm.fhir.search.util.ReferenceValue;

/**
* DTO representing external and local reference parameters
Expand All @@ -21,11 +22,14 @@ public class ReferenceParmVal implements ExtractedParameterValue {
private String name;

// The reference value
private String valueString;
//private String valueString;

// The SearchParameter base type. If "Resource", then this is a Resource-level attribute
private String base;

// The value of the reference after it has been processed to determine target resource type, version etc.
private ReferenceValue refValue;

/**
* Public constructor
*/
Expand All @@ -41,19 +45,40 @@ public String getName() {
return name;
}

public String getValueString() {
return valueString;
}
// public String getValueString() {
// return valueString;
// }

// public void setValueString(String valueString) {
// this.valueString = valueString;
// }

public void setValueString(String valueString) {
this.valueString = valueString;
/**
* Get the refValue
* @return
*/
public ReferenceValue getRefValue() {
return this.refValue;
}

/**
* Set the refValue
* @param refValue
*/
public void setRefValue(ReferenceValue refValue) {
this.refValue = refValue;
}

/**
* Get the reference type of the parameter (the origin, not the target of the reference)
*/
public String getResourceType() {
return resourceType;
}

/**
* Set the reference type of the parameter (the origin, not the target of the reference)
*/
public void setResourceType(String resourceType) {
this.resourceType = resourceType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
import com.ibm.fhir.persistence.jdbc.dto.ExtractedParameterValue;
import com.ibm.fhir.persistence.jdbc.dto.NumberParmVal;
import com.ibm.fhir.persistence.jdbc.dto.QuantityParmVal;
import com.ibm.fhir.persistence.jdbc.dto.ReferenceParmVal;
import com.ibm.fhir.persistence.jdbc.dto.StringParmVal;
import com.ibm.fhir.persistence.jdbc.dto.TokenParmVal;
import com.ibm.fhir.persistence.jdbc.exception.FHIRPersistenceDBConnectException;
Expand All @@ -138,9 +139,14 @@
import com.ibm.fhir.search.SearchConstants;
import com.ibm.fhir.search.SearchConstants.Modifier;
import com.ibm.fhir.search.SummaryValueSet;
import com.ibm.fhir.search.compartment.CompartmentUtil;
import com.ibm.fhir.search.context.FHIRSearchContext;
import com.ibm.fhir.search.date.DateTimeHandler;
import com.ibm.fhir.search.exception.FHIRSearchException;
import com.ibm.fhir.search.parameters.QueryParameter;
import com.ibm.fhir.search.reference.value.CompartmentReference;
import com.ibm.fhir.search.util.ReferenceValue;
import com.ibm.fhir.search.util.ReferenceValue.ReferenceType;
import com.ibm.fhir.search.util.SearchUtil;

/**
Expand Down Expand Up @@ -1227,6 +1233,21 @@ private TransactionSynchronizationRegistry getTrxSynchRegistry() throws FHIRPers
}
}

private List<ExtractedParameterValue> extractCompartmentValues(Resource fhirResource, com.ibm.fhir.persistence.jdbc.dto.Resource resourceDTO) throws Exception {

List<ExtractedParameterValue> result = null;

Map<String, Set<java.lang.String>> compartmentRefParams = CompartmentUtil.getCompartmentParamsForResourceType(fhirResource.getClass().getSimpleName());

if (!compartmentRefParams.isEmpty()) {
//result = SearchUtil.extractCompartmentParameterValues(fhirResource, compartmentRefParams);
} else {
result = Collections.emptyList();
}

return result;
}

/**
* Extracts search parameters for the passed FHIR Resource.
* @param fhirResource - Some FHIR Resource
Expand Down Expand Up @@ -1442,12 +1463,48 @@ private List<ExtractedParameterValue> extractSearchParameters(Resource fhirResou
}
}
}

// Augment the extracted parameter list with special values we use to represent compartment relationships.
// These references are stored as tokens and are used by the search query builder
// for compartment-based searches
addCompartmentParams(allParameters, fhirResource);
} finally {
log.exiting(CLASSNAME, METHODNAME);
}
return allParameters;
}

/**
* Augment the given list with additional reference values
* @param allParameters
*/
protected void addCompartmentParams(List<ExtractedParameterValue> allParameters, Resource fhirResource) throws FHIRSearchException {
final String resourceType = fhirResource.getClass().getSimpleName();
Map<String,Set<String>> compartmentRefParams = CompartmentUtil.getCompartmentParamsForResourceType(resourceType);
Map<String, Set<CompartmentReference>> compartmentMap = SearchUtil.extractCompartmentParameterValues(fhirResource, compartmentRefParams);

for (Map.Entry<String, Set<CompartmentReference>> entry: compartmentMap.entrySet()) {
final String compartmentName = entry.getKey();
final String parameterName = CompartmentUtil.makeCompartmentParamName(compartmentName);

// Create a reference parameter value for each CompartmentReference extracted from the resource
for (CompartmentReference compartmentRef: entry.getValue()) {
ReferenceParmVal pv = new ReferenceParmVal();
pv.setName(parameterName);
pv.setResourceType(resourceType);

// ReferenceType doesn't really matter here, but LITERAL_RELATIVE is appropriate
ReferenceValue rv = new ReferenceValue(compartmentName, compartmentRef.getReferenceResourceValue(), ReferenceType.LITERAL_RELATIVE, null);
pv.setRefValue(rv);

if (log.isLoggable(Level.FINE)) {
log.fine("Adding compartment reference parameter: [" + resourceType + "] "+ parameterName + " = " + rv.getTargetResourceType() + "/" + rv.getValue());
}
allParameters.add(pv);
}
}
}

/**
* Create a Parameter DTO from the primitive value.
* Note: this method only sets the value;
Expand Down
Loading

0 comments on commit d862936

Please sign in to comment.