improve nickname matcher (#3628)

* improve nickname matcher

* change log

* review feedback and fix test
This commit is contained in:
Ken Stevens 2022-05-24 13:13:09 -04:00 committed by GitHub
parent 1c70e97087
commit df76b0f8f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 51 additions and 26 deletions

View File

@ -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."

View File

@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -70,7 +71,7 @@ public class NicknameInterceptor {
toRemove.add(stringParam);
//First, attempt to expand as a formal name
String name = stringParam.getValue().toLowerCase(Locale.ROOT);
List<String> expansions = myNicknameSvc.getEquivalentNames(name);
Collection<String> expansions = myNicknameSvc.getEquivalentNames(name);
if (expansions == null) {
continue;
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.searchparam.nickname;
* #L%
*/
import javax.annotation.Nonnull;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
@ -57,11 +58,15 @@ class NicknameMap {
return myFormalToNick.size();
}
List<String> getNicknamesFromFormalNameOrNull(String theName) {
return myFormalToNick.get(theName);
@Nonnull
List<String> getNicknamesFromFormalName(String theName) {
List<String> result = myFormalToNick.get(theName);
return result == null ? new ArrayList<>() : result;
}
List<String> getFormalNamesFromNicknameOrNull(String theNickname) {
return myNicknameToFormal.get(theNickname);
@Nonnull
List<String> getFormalNamesFromNickname(String theNickname) {
List<String> result = myNicknameToFormal.get(theNickname);
return result == null ? new ArrayList<>() : result;
}
}

View File

@ -23,12 +23,15 @@ package ca.uhn.fhir.jpa.searchparam.nickname;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class NicknameSvc {
private final NicknameMap myNicknameMap = new NicknameMap();
@ -46,28 +49,27 @@ public class NicknameSvc {
return myNicknameMap.size();
}
public List<String> getEquivalentNames(String theName) {
List<String> retval = new ArrayList<>();
retval.add(theName);
public Collection<String> getEquivalentNames(String theName) {
Set<String> retval = new HashSet<>(getNicknamesFromFormalName(theName));
List<String> expansions;
expansions = getNicknamesFromFormalNameOrNull(theName);
if (expansions != null) {
retval.addAll(expansions);
} else {
expansions = getFormalNamesFromNicknameOrNull(theName);
if (expansions != null) {
retval.addAll(expansions);
if (retval.isEmpty()) {
List<String> formalNames = getFormalNamesFromNickname(theName);
retval.addAll(formalNames);
for (String formalName : formalNames) {
retval.addAll(getNicknamesFromFormalName(formalName));
}
}
retval.add(theName);
return retval;
}
List<String> getNicknamesFromFormalNameOrNull(String theName) {
return myNicknameMap.getNicknamesFromFormalNameOrNull(theName);
@Nonnull
List<String> getNicknamesFromFormalName(String theName) {
return myNicknameMap.getNicknamesFromFormalName(theName);
}
List<String> getFormalNamesFromNicknameOrNull(String theNickname) {
return myNicknameMap.getFormalNamesFromNicknameOrNull(theNickname);
@Nonnull
List<String> getFormalNamesFromNickname(String theNickname) {
return myNicknameMap.getFormalNamesFromNickname(theNickname);
}
}

View File

@ -38,7 +38,7 @@ class NicknameInterceptorTest {
// verify
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

View File

@ -24,7 +24,7 @@ kent,ken,kenny,kendrick
NicknameMap map = new NicknameMap();
map.load(new StringReader(testData));
assertEquals(7, map.size());
assertThat(map.getNicknamesFromFormalNameOrNull("kenneth"), containsInAnyOrder("ken", "kenny", "kendrick"));
assertThat(map.getFormalNamesFromNicknameOrNull("ken"), containsInAnyOrder("kendall", "kendrick", "kendrik", "kenneth", "kenny", "kent"));
assertThat(map.getNicknamesFromFormalName("kenneth"), containsInAnyOrder("ken", "kenny", "kendrick"));
assertThat(map.getFormalNamesFromNickname("ken"), containsInAnyOrder("kendall", "kendrick", "kendrik", "kenneth", "kenny", "kent"));
}
}

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.searchparam.nickname.NicknameSvc;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
@ -44,7 +45,12 @@ public class NicknameMatcher implements IMdmStringMatcher {
String leftString = theLeftString.toLowerCase(Locale.ROOT);
String rightString = theRightString.toLowerCase(Locale.ROOT);
List<String> leftNames = myNicknameSvc.getEquivalentNames(leftString);
return leftNames.contains(rightString);
Collection<String> leftNames = myNicknameSvc.getEquivalentNames(leftString);
if (leftNames.contains(rightString)) {
return true;
}
Collection<String> rightNames = myNicknameSvc.getEquivalentNames(rightString);
return rightNames.contains(leftString);
}
}

View File

@ -17,6 +17,12 @@ class NicknameMatcherTest {
assertTrue(matcher.matches("Kenneth", "Kenny"));
assertTrue(matcher.matches("Ken", "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"));
// These aren't nickname matches. If you want matches like these use a phonetic matcher