Skip to content

Commit

Permalink
Issue #1922 - implement whole-system search in new query builder
Browse files Browse the repository at this point in the history
Signed-off-by: Mike Schroeder <mschroed@us.ibm.com>
  • Loading branch information
michaelwschroeder committed Jun 21, 2021
1 parent 6c18b9b commit aab551b
Show file tree
Hide file tree
Showing 39 changed files with 658 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package com.ibm.fhir.persistence.jdbc;

import com.ibm.fhir.persistence.jdbc.dao.api.ICommonTokenValuesCache;
import com.ibm.fhir.persistence.jdbc.dao.api.IIdNameCache;
import com.ibm.fhir.persistence.jdbc.dao.api.INameIdCache;

/**
Expand Down Expand Up @@ -34,6 +35,12 @@ public interface FHIRPersistenceJDBCCache {
*/
INameIdCache<Integer> getResourceTypeCache();

/**
* Getter for the cache of resource type ids used to look up resource type name
* @return
*/
IIdNameCache<Integer> getResourceTypeNameCache();

/**
* Getter for the cache of parameter names
* @return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import com.ibm.fhir.persistence.jdbc.FHIRPersistenceJDBCCache;
import com.ibm.fhir.persistence.jdbc.dao.api.ICommonTokenValuesCache;
import com.ibm.fhir.persistence.jdbc.dao.api.IIdNameCache;
import com.ibm.fhir.persistence.jdbc.dao.api.INameIdCache;

/**
Expand All @@ -21,6 +22,8 @@ public class FHIRPersistenceJDBCCacheImpl implements FHIRPersistenceJDBCCache {

private final INameIdCache<Integer> resourceTypeCache;

private final IIdNameCache<Integer> resourceTypeNameCache;

private final INameIdCache<Integer> parameterNameCache;

private final ICommonTokenValuesCache resourceReferenceCache;
Expand All @@ -31,11 +34,14 @@ public class FHIRPersistenceJDBCCacheImpl implements FHIRPersistenceJDBCCache {
/**
* Public constructor
* @param resourceTypeCache
* @param resourceTypeNameCache
* @param parameterNameCache
* @param resourceReferenceCache
*/
public FHIRPersistenceJDBCCacheImpl(INameIdCache<Integer> resourceTypeCache, INameIdCache<Integer> parameterNameCache, ICommonTokenValuesCache resourceReferenceCache) {
public FHIRPersistenceJDBCCacheImpl(INameIdCache<Integer> resourceTypeCache, IIdNameCache<Integer> resourceTypeNameCache,
INameIdCache<Integer> parameterNameCache, ICommonTokenValuesCache resourceReferenceCache) {
this.resourceTypeCache = resourceTypeCache;
this.resourceTypeNameCache = resourceTypeNameCache;
this.parameterNameCache = parameterNameCache;
this.resourceReferenceCache = resourceReferenceCache;
}
Expand All @@ -47,11 +53,22 @@ public ICommonTokenValuesCache getResourceReferenceCache() {
return resourceReferenceCache;
}

/**
* @return the resourceTypeCache
*/
@Override
public INameIdCache<Integer> getResourceTypeCache() {
return this.resourceTypeCache;
}

/**
* @return the resourceTypeNameCache
*/
@Override
public IIdNameCache<Integer> getResourceTypeNameCache() {
return this.resourceTypeNameCache;
}

/**
* @return the parameterNameCache
*/
Expand All @@ -63,6 +80,7 @@ public INameIdCache<Integer> getParameterNameCache() {
public void transactionCommitted() {
logger.fine("Transaction committed - updating cache shared maps");
resourceTypeCache.updateSharedMaps();
resourceTypeNameCache.updateSharedMaps();
parameterNameCache.updateSharedMaps();
resourceReferenceCache.updateSharedMaps();
}
Expand All @@ -71,6 +89,7 @@ public void transactionCommitted() {
public void transactionRolledBack() {
logger.fine("Transaction rolled back - clearing local maps");
resourceTypeCache.clearLocalMaps();
resourceTypeNameCache.clearLocalMaps();
parameterNameCache.clearLocalMaps();
resourceReferenceCache.clearLocalMaps();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/*
* (C) Copyright IBM Corp. 2020
* (C) Copyright IBM Corp. 2020, 2021
*
* SPDX-License-Identifier: Apache-2.0
*/

package com.ibm.fhir.persistence.jdbc.cache;

import java.util.Map;
import java.util.stream.Collectors;

import com.ibm.fhir.persistence.exception.FHIRPersistenceException;
import com.ibm.fhir.persistence.jdbc.FHIRPersistenceJDBCCache;
Expand All @@ -25,8 +26,8 @@ public class FHIRPersistenceJDBCCacheUtil {
*/
public static FHIRPersistenceJDBCCache create(int codeSystemCacheSize, int tokenValueCacheSize, int canonicalCacheSize) {
ICommonTokenValuesCache rrc = new CommonTokenValuesCacheImpl(codeSystemCacheSize, tokenValueCacheSize, canonicalCacheSize);
return new FHIRPersistenceJDBCCacheImpl(new NameIdCache<Integer>(), new NameIdCache<Integer>(), rrc);

return new FHIRPersistenceJDBCCacheImpl(new NameIdCache<Integer>(), new IdNameCache<Integer>(), new NameIdCache<Integer>(), rrc);
}
/**
* Prefill the cache with constants already committed in the database
Expand All @@ -36,6 +37,9 @@ public static FHIRPersistenceJDBCCache create(int codeSystemCacheSize, int token
public static void prefill(ResourceDAO resourceDAO, ParameterDAO parameterDAO, FHIRPersistenceJDBCCache cache) throws FHIRPersistenceException {
Map<String,Integer> resourceTypes = resourceDAO.readAllResourceTypeNames();
cache.getResourceTypeCache().prefill(resourceTypes);

Map<Integer,String> resourceTypeNames = resourceTypes.entrySet().stream().collect(Collectors.toMap(map -> map.getValue(), map -> map.getKey()));
cache.getResourceTypeNameCache().prefill(resourceTypeNames);

Map<String,Integer> parameterNames = parameterDAO.readAllSearchParameterNames();
cache.getParameterNameCache().prefill(parameterNames);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* (C) Copyright IBM Corp. 2021
*
* SPDX-License-Identifier: Apache-2.0
*/

package com.ibm.fhir.persistence.jdbc.cache;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.ibm.fhir.persistence.jdbc.dao.api.IIdNameCache;


/**
* @param <T> the type of the key value held by the cache
*/
public class IdNameCache<T> implements IIdNameCache<T> {

// We use LinkedHashMap for the local map because we also need to maintain order
// of insertion to make sure we have correct LRU behavior when updating the shared cache
private final ThreadLocal<Map<T,String>> local = new ThreadLocal<>();

// The cache shared at the server level
private final ConcurrentHashMap<T,String> shared = new ConcurrentHashMap<>();

/**
* Public constructor
*/
public IdNameCache() {
}

@Override
public void updateSharedMaps() {

Map<T,String> localMap = local.get();
if (localMap != null) {
// no need to synchronize because we can use a ConcurrentHashMap
shared.putAll(localMap);
localMap.clear();
}
}

@Override
public String getName(T key) {
String result = null;
Map<T,String> localMap = local.get();
if (localMap != null) {
result = localMap.get(key);
}

if (result == null) {
result = shared.get(key);
}
return result;
}

@Override
public void addEntry(T id, String name) {
Map<T,String> localMap = local.get();
if (localMap == null) {
localMap = new HashMap<>();
local.set(localMap);
}
localMap.put(id, name);
}

@Override
public void reset() {
local.remove();
shared.clear();
}

@Override
public void clearLocalMaps() {
Map<T,String> map = local.get();
if (map != null) {
map.clear();
}
}

@Override
public void prefill(Map<T,String> content) {
// as the given content is supposed to be already committed in the database,
// we can add it directly to the shared map
this.shared.putAll(content);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* (C) Copyright IBM Corp. 2021
*
* SPDX-License-Identifier: Apache-2.0
*/

package com.ibm.fhir.persistence.jdbc.dao.api;

import java.util.Map;

/**
* Interface to a cache mapping an id of type T to a string. Supports
* thread-local caching to support temporary staging of values pending
* successful completion of a transaction.
* @param<T>
*/
public interface IIdNameCache<T> {

/**
* Get the name for the given id
* @param id
* @return
*/
String getName(T id);

/**
* Add the entry to the local cache
* @param id
* @param name
*/
void addEntry(T id, String name);

/**
* Called after a transaction commit() to transfer all the staged (thread-local) data
* over to the shared cache.
*/
void updateSharedMaps();

/**
* Clear both local shared caches - useful for unit tests
*/
void reset();

/**
* Clear anything cached in thread-local (after transaction rollback, for example)
*/
void clearLocalMaps();

/**
* Prefill the shared map with the given content (must come data already
* committed in the database)
* @param content
*/
void prefill(Map<T,String> content);
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,15 @@ List<Resource> search(String sqlSelect)
*/
int searchCount(String sqlSelectCount) throws FHIRPersistenceDataAccessException, FHIRPersistenceDBConnectException;

/**
* Executes the whole-system filter search contained in the passed {@link Select}, using its encapsulated search string and bind variables.
* @param select - Contains a search query and (optionally) bind variables.
* @return Map<Integer, List<Long>> A map of FHIR resource type ID to list of logical resource IDs satisfying the passed search.
* @throws FHIRPersistenceDataAccessException
* @throws FHIRPersistenceDBConnectException
*/
Map<Integer, List<Long>> searchWholeSystem(Select select) throws FHIRPersistenceDataAccessException, FHIRPersistenceDBConnectException;

/**
* Sets the current persistence context
* @param context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -858,4 +858,41 @@ protected void deleteFromParameterTable(Connection conn, String tableName, long
stmt.executeUpdate();
}
}

@Override
public Map<Integer, List<Long>> searchWholeSystem(Select wholeSystemQuery) throws FHIRPersistenceDataAccessException,
FHIRPersistenceDBConnectException {
final String METHODNAME = "searchWholeSystem";
log.entering(CLASSNAME, METHODNAME);

Map<Integer, List<Long>> resultMap = new HashMap<>();
Connection connection = getConnection(); // do not close
ResultSet resultSet = null;
long dbCallStartTime;
double dbCallDuration;

try (PreparedStatement stmt = QueryUtil.prepareSelect(connection, wholeSystemQuery, getTranslator())) {
dbCallStartTime = System.nanoTime();
resultSet = stmt.executeQuery();
dbCallDuration = (System.nanoTime() - dbCallStartTime) / 1e6;
if (log.isLoggable(Level.FINE)) {
log.fine("Successfully retrieved logical resource Ids [took " + dbCallDuration + " ms]");
}

// Transform the resultSet into a map of resource type IDs to logical resource IDs
while (resultSet.next()) {
Integer resourceTypeId = resultSet.getInt(1);
Long logicalResourceId = resultSet.getLong(2);
resultMap.computeIfAbsent(resourceTypeId, k -> new ArrayList<>()).add(logicalResourceId);
}
} catch (Throwable e) {
FHIRPersistenceDataAccessException fx = new FHIRPersistenceDataAccessException("Failure retrieving logical resource Ids");
final String errMsg = "Failure retrieving logical resource Ids. SqlQueryData=" + wholeSystemQuery.toDebugString();
throw severe(log, fx, errMsg, e);
} finally {
log.exiting(CLASSNAME, METHODNAME);
}

return resultMap;
}
}
Loading

0 comments on commit aab551b

Please sign in to comment.