Skip to content

Commit d86a419

Browse files
authored
Merge pull request #517 from IBM/lee-master
Search modifier refactoring and doc update
2 parents 5e41bac + c572793 commit d86a419

5 files changed

Lines changed: 70 additions & 28 deletions

File tree

docs/src/pages/Conformance.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ For information on how to specify custom search parameters, see [FHIRSearchConfi
8080
FHIR search modifiers are described at https://www.hl7.org/fhir/R4/search.html#modifiers and vary by search parameter type. The IBM FHIR Server implements a subset of the spec-defined search modifiers that is defined in the following table:
8181

8282
|FHIR Search Parameter Type|Supported Modifiers|"Default" search behavior when no Modifier or Prefix is present|
83-
|--------------------------|-------------------|-----------------------------------------------------|
83+
|--------------------------|-------------------|---------------------------------------------------------------|
8484
|String |`:exact`,`:contains`,`:missing` |"starts with" search that is case-insensitive and accent-insensitive|
8585
|Reference |`:[type]`,`:missing` |exact match search|
8686
|URI |`:below`,`:above`,`:missing` |exact match search|
87-
|Token |`:below`,`:not`,`:missing` |exact match search|
87+
|Token |`:missing` |exact match search|
8888
|Number |`:missing` |exact match search|
8989
|Date |`:missing` |exact match search|
9090
|Quantity |`:missing` |implicit range search (see http://hl7.org/fhir/R4/search.html#quantity)|

fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/JDBCConstants.java

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55
*/
66
package com.ibm.fhir.persistence.jdbc;
77

8+
import java.util.Arrays;
89
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
912

1013
import com.ibm.fhir.search.SearchConstants.Modifier;
1114
import com.ibm.fhir.search.SearchConstants.Prefix;
15+
import com.ibm.fhir.search.SearchConstants.Type;
1216

1317
public class JDBCConstants {
1418
/**
@@ -57,24 +61,40 @@ public class JDBCConstants {
5761
public static final String JOIN = " JOIN ";
5862
public static final String LEFT_JOIN = " LEFT JOIN ";
5963
public static final String COMBINED_RESULTS = " COMBINED_RESULTS";
60-
64+
65+
/**
66+
* Maps search parameter types to the currently supported list of modifiers for that type.
67+
*/
68+
public static Map<Type, List<Modifier>> supportedModifiersMap;
69+
6170
/**
6271
* Maps Parameter modifiers to SQL operators.
6372
*/
64-
public static HashMap<Modifier, JDBCOperator> modifierMap;
73+
public static Map<Modifier, JDBCOperator> modifierOperatorMap;
6574

6675
/**
6776
* Maps Parameter value prefix operators to SQL operators.
6877
*/
69-
public static HashMap<Prefix, JDBCOperator> prefixOperatorMap;
78+
public static Map<Prefix, JDBCOperator> prefixOperatorMap;
7079

7180
static {
72-
modifierMap = new HashMap<>();
73-
modifierMap.put(Modifier.ABOVE, JDBCOperator.GT);
74-
modifierMap.put(Modifier.BELOW, JDBCOperator.LT);
75-
modifierMap.put(Modifier.CONTAINS, JDBCOperator.LIKE);
76-
modifierMap.put(Modifier.EXACT, JDBCOperator.EQ);
77-
modifierMap.put(Modifier.NOT, JDBCOperator.NE);
81+
supportedModifiersMap = new HashMap<Type, List<Modifier>>();
82+
supportedModifiersMap.put(Type.STRING, Arrays.asList(Modifier.EXACT, Modifier.CONTAINS, Modifier.MISSING));
83+
supportedModifiersMap.put(Type.REFERENCE, Arrays.asList(Modifier.TYPE, Modifier.MISSING));
84+
supportedModifiersMap.put(Type.URI, Arrays.asList(Modifier.BELOW, Modifier.ABOVE, Modifier.MISSING));
85+
supportedModifiersMap.put(Type.TOKEN, Arrays.asList(Modifier.MISSING));
86+
supportedModifiersMap.put(Type.NUMBER, Arrays.asList(Modifier.MISSING));
87+
supportedModifiersMap.put(Type.DATE, Arrays.asList(Modifier.MISSING));
88+
supportedModifiersMap.put(Type.QUANTITY, Arrays.asList(Modifier.MISSING));
89+
supportedModifiersMap.put(Type.COMPOSITE, Arrays.asList(Modifier.MISSING));
90+
supportedModifiersMap.put(Type.SPECIAL, Arrays.asList(Modifier.MISSING));
91+
92+
modifierOperatorMap = new HashMap<>();
93+
modifierOperatorMap.put(Modifier.ABOVE, JDBCOperator.GT);
94+
modifierOperatorMap.put(Modifier.BELOW, JDBCOperator.LT);
95+
modifierOperatorMap.put(Modifier.CONTAINS, JDBCOperator.LIKE);
96+
modifierOperatorMap.put(Modifier.EXACT, JDBCOperator.EQ);
97+
modifierOperatorMap.put(Modifier.NOT, JDBCOperator.NE);
7898

7999
prefixOperatorMap = new HashMap<>();
80100
prefixOperatorMap.put(Prefix.EQ, JDBCOperator.EQ);
@@ -87,7 +107,7 @@ public class JDBCConstants {
87107
prefixOperatorMap.put(Prefix.EB, JDBCOperator.LT);
88108
prefixOperatorMap.put(Prefix.AP, JDBCOperator.EQ);
89109
}
90-
110+
91111
/**
92112
* An enumeration of SQL query operators.
93113
*/

fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/impl/FHIRPersistenceJDBCImpl.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
import com.ibm.fhir.persistence.exception.FHIRPersistenceException;
8080
import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceDeletedException;
8181
import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceNotFoundException;
82+
import com.ibm.fhir.persistence.jdbc.JDBCConstants;
8283
import com.ibm.fhir.persistence.jdbc.dao.api.FHIRDbDAO;
8384
import com.ibm.fhir.persistence.jdbc.dao.api.ParameterDAO;
8485
import com.ibm.fhir.persistence.jdbc.dao.api.ResourceDAO;
@@ -106,6 +107,7 @@
106107
import com.ibm.fhir.search.SearchConstants;
107108
import com.ibm.fhir.search.SummaryValueSet;
108109
import com.ibm.fhir.search.context.FHIRSearchContext;
110+
import com.ibm.fhir.search.parameters.QueryParameter;
109111
import com.ibm.fhir.search.util.SearchUtil;
110112

111113
/**
@@ -171,7 +173,7 @@ public FHIRPersistenceJDBCImpl(Properties configProps) throws Exception {
171173
this.setManagedConnection(this.getBaseDao().getConnection());
172174
this.resourceDao = new ResourceDAOImpl(this.getManagedConnection());
173175
this.parameterDao = new ParameterDAOImpl(this.getManagedConnection());
174-
176+
175177
log.exiting(CLASSNAME, METHODNAME);
176178
}
177179

@@ -417,8 +419,9 @@ public MultiResourceResult<Resource> search(FHIRPersistenceContext context, Clas
417419
SqlQueryData query;
418420

419421
try {
420-
queryBuilder = new JDBCQueryBuilder((ParameterDAO)this.getParameterDao(),
421-
(ResourceDAO)this.getResourceDao());
422+
checkModifiers(searchContext);
423+
queryBuilder = new JDBCQueryBuilder((ParameterDAO) this.getParameterDao(),
424+
(ResourceDAO) this.getResourceDao());
422425

423426
countQuery = queryBuilder.buildCountQuery(resourceType, searchContext);
424427
if (countQuery != null) {
@@ -507,6 +510,22 @@ public MultiResourceResult<Resource> search(FHIRPersistenceContext context, Clas
507510
}
508511
}
509512

513+
/**
514+
* @throws FHIRPersistenceException if the search context contains one or more unsupported modifiers
515+
*/
516+
private void checkModifiers(FHIRSearchContext searchContext) throws FHIRPersistenceException {
517+
for (QueryParameter param : searchContext.getSearchParameters()) {
518+
do {
519+
if(param.getModifier() != null &&
520+
!JDBCConstants.supportedModifiersMap.get(param.getType()).contains(param.getModifier())) {
521+
throw new FHIRPersistenceException("Found unsupported modifier '" + param.getModifier() + "'"
522+
+ " for search parameter '" + param.getCode() + "' of type " + param.getType());
523+
}
524+
param = param.getNextParameter();
525+
} while (param != null);
526+
}
527+
}
528+
510529
private ParameterDAO getParameterDao() {
511530
return parameterDao;
512531
}

fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/util/JDBCQueryBuilder.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
package com.ibm.fhir.persistence.jdbc.util;
88

99
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.AND;
10-
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.ON;
11-
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.OR;
1210
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.BIND_VAR;
1311
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.CODE_SYSTEM_ID;
1412
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.DATE_END;
@@ -19,7 +17,12 @@
1917
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.ESCAPE_PERCENT;
2018
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.ESCAPE_UNDERSCORE;
2119
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.FROM;
20+
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.JOIN;
21+
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.LEFT_JOIN;
2222
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.LEFT_PAREN;
23+
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.MAX_NUM_OF_COMPOSITE_COMPONENTS;
24+
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.ON;
25+
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.OR;
2326
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.PARAMETER_TABLE_ALIAS;
2427
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.PERCENT_WILDCARD;
2528
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.RIGHT_PAREN;
@@ -28,11 +31,8 @@
2831
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.TOKEN_VALUE;
2932
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.UNDERSCORE_WILDCARD;
3033
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.WHERE;
31-
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.modifierMap;
34+
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.modifierOperatorMap;
3235
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.prefixOperatorMap;
33-
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.JOIN;
34-
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.LEFT_JOIN;
35-
import static com.ibm.fhir.persistence.jdbc.JDBCConstants.MAX_NUM_OF_COMPOSITE_COMPONENTS;
3636

3737
import java.sql.Timestamp;
3838
import java.time.Instant;
@@ -257,7 +257,7 @@ protected JDBCOperator getOperator(QueryParameter queryParm) {
257257
operator = JDBCOperator.EQ;
258258
}
259259
} else if (modifier != null) {
260-
operator = modifierMap.get(modifier);
260+
operator = modifierOperatorMap.get(modifier);
261261
}
262262

263263
if (operator == null) {
@@ -287,7 +287,7 @@ protected JDBCOperator getOperator(QueryParameter queryParm, JDBCOperator defaul
287287
Modifier modifier = queryParm.getModifier();
288288

289289
if (modifier != null) {
290-
operator = modifierMap.get(modifier);
290+
operator = modifierOperatorMap.get(modifier);
291291
}
292292
if (operator == null) {
293293
if (defaultOverride != null) {

fhir-search/src/main/java/com/ibm/fhir/search/SearchConstants.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,13 @@ private SearchConstants() {
116116

117117
public static final String WILDCARD = "*";
118118

119-
public static final char AND_CHAR = '&';
119+
public static final char AND_CHAR = '&';
120120

121121
public static final char EQUALS_CHAR = '=';
122122

123123
public static final String JOIN_STR = ",";
124124

125-
public static final String AND_CHAR_STR = "&";
125+
public static final String AND_CHAR_STR = "&";
126126

127127
// Filter
128128
public static final String WILDCARD_FILTER = "*";
@@ -141,9 +141,10 @@ private SearchConstants() {
141141

142142
{
143143
put(SearchConstants.Type.STRING, Arrays.asList(Modifier.EXACT, Modifier.CONTAINS, Modifier.MISSING));
144-
put(SearchConstants.Type.REFERENCE, Arrays.asList(Modifier.TYPE, Modifier.MISSING));
144+
put(SearchConstants.Type.REFERENCE, Arrays.asList(Modifier.TYPE, Modifier.IDENTIFIER, Modifier.MISSING));
145145
put(SearchConstants.Type.URI, Arrays.asList(Modifier.BELOW, Modifier.ABOVE, Modifier.MISSING));
146-
put(SearchConstants.Type.TOKEN, Arrays.asList(Modifier.BELOW, Modifier.NOT, Modifier.MISSING));
146+
put(SearchConstants.Type.TOKEN, Arrays.asList(Modifier.TEXT, Modifier.NOT,
147+
Modifier.ABOVE, Modifier.BELOW, Modifier.IN, Modifier.NOT_IN, Modifier.OF_TYPE, Modifier.MISSING));
147148
put(SearchConstants.Type.NUMBER, Arrays.asList(Modifier.MISSING));
148149
put(SearchConstants.Type.DATE, Arrays.asList(Modifier.MISSING));
149150
put(SearchConstants.Type.QUANTITY, Arrays.asList(Modifier.MISSING));
@@ -235,7 +236,9 @@ public enum Modifier {
235236
ABOVE("above"),
236237
NOT("not"),
237238
NOT_IN("not-in"),
238-
TYPE("[type]");
239+
TYPE("[type]"),
240+
OF_TYPE("of-type"),
241+
IDENTIFIER("identifier");
239242

240243
private String value = null;
241244

0 commit comments

Comments
 (0)