improve nickname matcher (#3628)
* improve nickname matcher * change log * review feedback and fix test
This commit is contained in:
parent
1c70e97087
commit
df76b0f8f1
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 3628
|
||||||
|
title: "Nickname matching only matched formal names to a nickname. This has been broadened to match nicknames to other
|
||||||
|
nicknames for the same formal name."
|
|
@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -70,7 +71,7 @@ public class NicknameInterceptor {
|
||||||
toRemove.add(stringParam);
|
toRemove.add(stringParam);
|
||||||
//First, attempt to expand as a formal name
|
//First, attempt to expand as a formal name
|
||||||
String name = stringParam.getValue().toLowerCase(Locale.ROOT);
|
String name = stringParam.getValue().toLowerCase(Locale.ROOT);
|
||||||
List<String> expansions = myNicknameSvc.getEquivalentNames(name);
|
Collection<String> expansions = myNicknameSvc.getEquivalentNames(name);
|
||||||
if (expansions == null) {
|
if (expansions == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.searchparam.nickname;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
@ -57,11 +58,15 @@ class NicknameMap {
|
||||||
return myFormalToNick.size();
|
return myFormalToNick.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getNicknamesFromFormalNameOrNull(String theName) {
|
@Nonnull
|
||||||
return myFormalToNick.get(theName);
|
List<String> getNicknamesFromFormalName(String theName) {
|
||||||
|
List<String> result = myFormalToNick.get(theName);
|
||||||
|
return result == null ? new ArrayList<>() : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getFormalNamesFromNicknameOrNull(String theNickname) {
|
@Nonnull
|
||||||
return myNicknameToFormal.get(theNickname);
|
List<String> getFormalNamesFromNickname(String theNickname) {
|
||||||
|
List<String> result = myNicknameToFormal.get(theNickname);
|
||||||
|
return result == null ? new ArrayList<>() : result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,15 @@ package ca.uhn.fhir.jpa.searchparam.nickname;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.ArrayList;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class NicknameSvc {
|
public class NicknameSvc {
|
||||||
private final NicknameMap myNicknameMap = new NicknameMap();
|
private final NicknameMap myNicknameMap = new NicknameMap();
|
||||||
|
@ -46,28 +49,27 @@ public class NicknameSvc {
|
||||||
return myNicknameMap.size();
|
return myNicknameMap.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getEquivalentNames(String theName) {
|
public Collection<String> getEquivalentNames(String theName) {
|
||||||
List<String> retval = new ArrayList<>();
|
Set<String> retval = new HashSet<>(getNicknamesFromFormalName(theName));
|
||||||
retval.add(theName);
|
|
||||||
|
|
||||||
List<String> expansions;
|
if (retval.isEmpty()) {
|
||||||
expansions = getNicknamesFromFormalNameOrNull(theName);
|
List<String> formalNames = getFormalNamesFromNickname(theName);
|
||||||
if (expansions != null) {
|
retval.addAll(formalNames);
|
||||||
retval.addAll(expansions);
|
for (String formalName : formalNames) {
|
||||||
} else {
|
retval.addAll(getNicknamesFromFormalName(formalName));
|
||||||
expansions = getFormalNamesFromNicknameOrNull(theName);
|
|
||||||
if (expansions != null) {
|
|
||||||
retval.addAll(expansions);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
retval.add(theName);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getNicknamesFromFormalNameOrNull(String theName) {
|
@Nonnull
|
||||||
return myNicknameMap.getNicknamesFromFormalNameOrNull(theName);
|
List<String> getNicknamesFromFormalName(String theName) {
|
||||||
|
return myNicknameMap.getNicknamesFromFormalName(theName);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getFormalNamesFromNicknameOrNull(String theNickname) {
|
@Nonnull
|
||||||
return myNicknameMap.getFormalNamesFromNicknameOrNull(theNickname);
|
List<String> getFormalNamesFromNickname(String theNickname) {
|
||||||
|
return myNicknameMap.getFormalNamesFromNickname(theNickname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ class NicknameInterceptorTest {
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
String newSearch = sp.toNormalizedQueryString(null);
|
String newSearch = sp.toNormalizedQueryString(null);
|
||||||
assertEquals("?name=ken,kendall,kendrick,kendrik,kenneth,kenny,kent,mckenna", newSearch);
|
assertEquals("?name=ken,kendall,kendrick,kendrik,kenna,kenneth,kenny,kent,mckenna,meaka", newSearch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -24,7 +24,7 @@ kent,ken,kenny,kendrick
|
||||||
NicknameMap map = new NicknameMap();
|
NicknameMap map = new NicknameMap();
|
||||||
map.load(new StringReader(testData));
|
map.load(new StringReader(testData));
|
||||||
assertEquals(7, map.size());
|
assertEquals(7, map.size());
|
||||||
assertThat(map.getNicknamesFromFormalNameOrNull("kenneth"), containsInAnyOrder("ken", "kenny", "kendrick"));
|
assertThat(map.getNicknamesFromFormalName("kenneth"), containsInAnyOrder("ken", "kenny", "kendrick"));
|
||||||
assertThat(map.getFormalNamesFromNicknameOrNull("ken"), containsInAnyOrder("kendall", "kendrick", "kendrik", "kenneth", "kenny", "kent"));
|
assertThat(map.getFormalNamesFromNickname("ken"), containsInAnyOrder("kendall", "kendrick", "kendrik", "kenneth", "kenny", "kent"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.searchparam.nickname.NicknameSvc;
|
import ca.uhn.fhir.jpa.searchparam.nickname.NicknameSvc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@ -44,7 +45,12 @@ public class NicknameMatcher implements IMdmStringMatcher {
|
||||||
String leftString = theLeftString.toLowerCase(Locale.ROOT);
|
String leftString = theLeftString.toLowerCase(Locale.ROOT);
|
||||||
String rightString = theRightString.toLowerCase(Locale.ROOT);
|
String rightString = theRightString.toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
List<String> leftNames = myNicknameSvc.getEquivalentNames(leftString);
|
Collection<String> leftNames = myNicknameSvc.getEquivalentNames(leftString);
|
||||||
return leftNames.contains(rightString);
|
if (leftNames.contains(rightString)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<String> rightNames = myNicknameSvc.getEquivalentNames(rightString);
|
||||||
|
return rightNames.contains(leftString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,12 @@ class NicknameMatcherTest {
|
||||||
assertTrue(matcher.matches("Kenneth", "Kenny"));
|
assertTrue(matcher.matches("Kenneth", "Kenny"));
|
||||||
assertTrue(matcher.matches("Ken", "Kenneth"));
|
assertTrue(matcher.matches("Ken", "Kenneth"));
|
||||||
assertTrue(matcher.matches("Kenny", "Kenneth"));
|
assertTrue(matcher.matches("Kenny", "Kenneth"));
|
||||||
|
assertTrue(matcher.matches("Jim", "Jimmy"));
|
||||||
|
assertTrue(matcher.matches("Jimmy", "Jim"));
|
||||||
|
assertTrue(matcher.matches("Jim", "James"));
|
||||||
|
assertTrue(matcher.matches("Jimmy", "James"));
|
||||||
|
assertTrue(matcher.matches("James", "Jimmy"));
|
||||||
|
assertTrue(matcher.matches("James", "Jim"));
|
||||||
|
|
||||||
assertFalse(matcher.matches("Ken", "Bob"));
|
assertFalse(matcher.matches("Ken", "Bob"));
|
||||||
// These aren't nickname matches. If you want matches like these use a phonetic matcher
|
// These aren't nickname matches. If you want matches like these use a phonetic matcher
|
||||||
|
|
Loading…
Reference in New Issue