changed $expand filter matching from left-match to contains match (#2327)
* changed $expand filter matching from left-match to contains match * changelog * pure substring match is too generous (a regression test checked for that). Tightened the match up to word boundaries. * CLean up to avoid intermittent test failure Co-authored-by: jamesagnew <jamesagnew@gmail.com>
This commit is contained in:
parent
d11515599a
commit
ba63f65115
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 2327
|
||||
title: "The $expand filter parameter was not matching the ValueSet display value in all cases. E.g. a ValueSet
|
||||
with name 'abc def ghi' would match 'abc def' and 'def' but not 'def ghi'. This has been corrected so the ValueSet
|
||||
will match the filter if any substring of the ValueSet display value matches the $expand filter."
|
|
@ -186,6 +186,7 @@ import static org.apache.commons.lang3.StringUtils.isEmpty;
|
|||
import static org.apache.commons.lang3.StringUtils.isNoneBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.lowerCase;
|
||||
import static org.apache.commons.lang3.StringUtils.startsWithIgnoreCase;
|
||||
|
||||
public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
||||
public static final int DEFAULT_FETCH_SIZE = 250;
|
||||
|
@ -644,27 +645,44 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean applyFilter(final String theInput, final String thePrefixToken) {
|
||||
public boolean applyFilter(final String theDisplay, final String theFilterDisplay) {
|
||||
|
||||
//-- safety check only, no need to apply filter
|
||||
if (theInput == null || thePrefixToken == null)
|
||||
if (theDisplay == null || theFilterDisplay == null)
|
||||
return true;
|
||||
|
||||
// -- sentence case
|
||||
if (org.apache.commons.lang3.StringUtils.startsWithIgnoreCase(theInput, thePrefixToken))
|
||||
if (startsWithIgnoreCase(theDisplay, theFilterDisplay))
|
||||
return true;
|
||||
|
||||
//-- token case
|
||||
// return true only e.g. the input is 'Body height', thePrefixToken is "he", or 'bo'
|
||||
StringTokenizer tok = new StringTokenizer(theInput);
|
||||
while (tok.hasMoreTokens()) {
|
||||
if (org.apache.commons.lang3.StringUtils.startsWithIgnoreCase(tok.nextToken(), thePrefixToken))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (startsWithByWordBoundaries(theDisplay, theFilterDisplay)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean startsWithByWordBoundaries(String theDisplay, String theFilterDisplay) {
|
||||
// return true only e.g. the input is 'Body height', theFilterDisplay is "he", or 'bo'
|
||||
StringTokenizer tok = new StringTokenizer(theDisplay);
|
||||
List<String> tokens = new ArrayList<>();
|
||||
while (tok.hasMoreTokens()) {
|
||||
String token = tok.nextToken();
|
||||
if (startsWithIgnoreCase(token, theFilterDisplay))
|
||||
return true;
|
||||
tokens.add(token);
|
||||
}
|
||||
|
||||
// Allow to search by the end of the phrase. E.g. "working proficiency" will match "Limited working proficiency"
|
||||
for (int start = 0; start <= tokens.size() - 1; ++ start) {
|
||||
for (int end = start + 1; end <= tokens.size(); ++end) {
|
||||
String sublist = String.join(" ", tokens.subList(start, end));
|
||||
if (startsWithIgnoreCase(sublist, theFilterDisplay))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public void expandValueSet(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) {
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package ca.uhn.fhir.jpa.term;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class BaseTermReadSvcImplTest {
|
||||
|
||||
private final TermReadSvcR5 mySvc = new TermReadSvcR5();
|
||||
|
||||
@Test
|
||||
void applyFilterMatchWords() {
|
||||
assertTrue(mySvc.applyFilter("abc def", "abc def"));
|
||||
assertTrue(mySvc.applyFilter("abc def", "abc"));
|
||||
assertTrue(mySvc.applyFilter("abc def", "def"));
|
||||
assertTrue(mySvc.applyFilter("abc def ghi", "abc def ghi"));
|
||||
assertTrue(mySvc.applyFilter("abc def ghi", "abc def"));
|
||||
assertTrue(mySvc.applyFilter("abc def ghi", "def ghi"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyFilterSentenceStart() {
|
||||
assertTrue(mySvc.applyFilter("manifold", "man"));
|
||||
assertTrue(mySvc.applyFilter("manifest destiny", "man"));
|
||||
assertTrue(mySvc.applyFilter("deep sight", "deep sigh"));
|
||||
assertTrue(mySvc.applyFilter("sink cottage", "sink cot"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyFilterSentenceEnd() {
|
||||
assertFalse(mySvc.applyFilter("rescue", "cue"));
|
||||
assertFalse(mySvc.applyFilter("very picky", "icky"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyFilterSubwords() {
|
||||
assertFalse(mySvc.applyFilter("splurge", "urge"));
|
||||
assertFalse(mySvc.applyFilter("sink cottage", "ink cot"));
|
||||
assertFalse(mySvc.applyFilter("sink cottage", "ink cottage"));
|
||||
assertFalse(mySvc.applyFilter("clever jump startle", "lever jump star"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue