4917 refactor nicknaming hapi with version bump (#4955)
* refactoring 1 * refactoring step 2 * refactoring matches * cleanup * fixing some tests * fixing more tests * remove dbugcode * adding the nickname factory * code review fixes * review fixes * put default to prevent breaking * refactor review fixes * more review points * more review points * review points * review points * more review changes * review points * review points * adding bean * updating version --------- Co-authored-by: leif stawnyczy <leifstawnyczy@leifs-MacBook-Pro.local> Co-authored-by: leif stawnyczy <leifstawnyczy@leifs-mbp.home>
This commit is contained in:
parent
6c03a512f5
commit
0a7f97adb7
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-bom</artifactId>
|
<artifactId>hapi-fhir-bom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>HAPI FHIR BOM</name>
|
<name>HAPI FHIR BOM</name>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-cli</artifactId>
|
<artifactId>hapi-fhir-cli</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 4917
|
||||||
|
title: "Matching algorithms have been refactored to allow
|
||||||
|
greater flexibility in setting and defining nicknames
|
||||||
|
as well as allowing bean injection into matcher classes.
|
||||||
|
"
|
|
@ -11,7 +11,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.jpa.searchparam.nickname;
|
package ca.uhn.fhir.jpa.nickname;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
@ -33,19 +33,30 @@ class NicknameMap {
|
||||||
private final Map<String, List<String>> myFormalToNick = new HashMap<>();
|
private final Map<String, List<String>> myFormalToNick = new HashMap<>();
|
||||||
private final Map<String, List<String>> myNicknameToFormal = new HashMap<>();
|
private final Map<String, List<String>> myNicknameToFormal = new HashMap<>();
|
||||||
|
|
||||||
|
private final List<String> myBadRows = new ArrayList<>();
|
||||||
|
|
||||||
void load(Reader theReader) throws IOException {
|
void load(Reader theReader) throws IOException {
|
||||||
try (BufferedReader reader = new BufferedReader(theReader)) {
|
try (BufferedReader reader = new BufferedReader(theReader)) {
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
String[] parts = line.split(",");
|
String[] parts = line.split(",");
|
||||||
String key = parts[0];
|
if (parts.length > 1) {
|
||||||
List<String> values = new ArrayList<>(Arrays.asList(parts).subList(1, parts.length));
|
String key = parts[0];
|
||||||
add(key, values);
|
List<String> values = new ArrayList<>(Arrays.asList(parts).subList(1, parts.length));
|
||||||
|
add(key, values);
|
||||||
|
} else {
|
||||||
|
myBadRows.add(line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(String theKey, List<String> theValues) {
|
void clear() {
|
||||||
|
myFormalToNick.clear();
|
||||||
|
myNicknameToFormal.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(String theKey, List<String> theValues) {
|
||||||
myFormalToNick.put(theKey, theValues);
|
myFormalToNick.put(theKey, theValues);
|
||||||
for (String value : theValues) {
|
for (String value : theValues) {
|
||||||
myNicknameToFormal.putIfAbsent(value, new ArrayList<>());
|
myNicknameToFormal.putIfAbsent(value, new ArrayList<>());
|
||||||
|
@ -57,14 +68,22 @@ class NicknameMap {
|
||||||
return myFormalToNick.size();
|
return myFormalToNick.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isEmpty() {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getBadRows() {
|
||||||
|
return myBadRows;
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
List<String> getNicknamesFromFormalName(String theName) {
|
public List<String> getNicknamesFromFormalName(String theName) {
|
||||||
List<String> result = myFormalToNick.get(theName);
|
List<String> result = myFormalToNick.get(theName);
|
||||||
return result == null ? new ArrayList<>() : result;
|
return result == null ? new ArrayList<>() : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
List<String> getFormalNamesFromNickname(String theNickname) {
|
public List<String> getFormalNamesFromNickname(String theNickname) {
|
||||||
List<String> result = myNicknameToFormal.get(theNickname);
|
List<String> result = myNicknameToFormal.get(theNickname);
|
||||||
return result == null ? new ArrayList<>() : result;
|
return result == null ? new ArrayList<>() : result;
|
||||||
}
|
}
|
|
@ -17,8 +17,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.jpa.searchparam.nickname;
|
package ca.uhn.fhir.jpa.nickname;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
@ -32,22 +36,40 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nickname service is used to load nicknames
|
||||||
|
* via a file that contains rows of comma separated names that are
|
||||||
|
* "similar" or nicknames of each other.
|
||||||
|
* -
|
||||||
|
* If no nickname resource is provided, nicknames/names.csv will be used instead.
|
||||||
|
* -
|
||||||
|
* If one is to be provided, it must be provided before nickname svc is invoked
|
||||||
|
*/
|
||||||
public class NicknameSvc {
|
public class NicknameSvc {
|
||||||
private final NicknameMap myNicknameMap = new NicknameMap();
|
private static final Logger ourLog = LoggerFactory.getLogger(NicknameSvc.class);
|
||||||
|
|
||||||
public NicknameSvc() throws IOException {
|
private NicknameMap myNicknameMap;
|
||||||
Resource nicknameCsvResource = new ClassPathResource("/nickname/names.csv");
|
|
||||||
try (InputStream inputStream = nicknameCsvResource.getInputStream()) {
|
private Resource myNicknameResource;
|
||||||
try (Reader reader = new InputStreamReader(inputStream)) {
|
|
||||||
myNicknameMap.load(reader);
|
public NicknameSvc() {
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNicknameResource(Resource theNicknameResource) {
|
||||||
|
myNicknameResource = theNicknameResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
|
ensureMapInitialized();
|
||||||
return myNicknameMap.size();
|
return myNicknameMap.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getBadRows() {
|
||||||
|
ensureMapInitialized();
|
||||||
|
return myNicknameMap.getBadRows();
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<String> getEquivalentNames(String theName) {
|
public Collection<String> getEquivalentNames(String theName) {
|
||||||
Set<String> retval = new HashSet<>(getNicknamesFromFormalName(theName));
|
Set<String> retval = new HashSet<>(getNicknamesFromFormalName(theName));
|
||||||
|
|
||||||
|
@ -64,11 +86,34 @@ public class NicknameSvc {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
List<String> getNicknamesFromFormalName(String theName) {
|
List<String> getNicknamesFromFormalName(String theName) {
|
||||||
|
ensureMapInitialized();
|
||||||
return myNicknameMap.getNicknamesFromFormalName(theName);
|
return myNicknameMap.getNicknamesFromFormalName(theName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
List<String> getFormalNamesFromNickname(String theNickname) {
|
List<String> getFormalNamesFromNickname(String theNickname) {
|
||||||
|
ensureMapInitialized();
|
||||||
return myNicknameMap.getFormalNamesFromNickname(theNickname);
|
return myNicknameMap.getFormalNamesFromNickname(theNickname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureMapInitialized() {
|
||||||
|
if (myNicknameResource == null) {
|
||||||
|
myNicknameResource = new ClassPathResource("/nickname/names.csv");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myNicknameMap == null) {
|
||||||
|
myNicknameMap = new NicknameMap();
|
||||||
|
}
|
||||||
|
if (myNicknameMap.isEmpty()) {
|
||||||
|
try {
|
||||||
|
try (InputStream inputStream = myNicknameResource.getInputStream()) {
|
||||||
|
try (Reader reader = new InputStreamReader(inputStream)) {
|
||||||
|
myNicknameMap.load(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ConfigurationException(Msg.code(2234) + "Unable to load nicknames", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.jpa.searchparam.nickname;
|
package ca.uhn.fhir.jpa.nickname;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.jpa.searchparam.nickname;
|
package ca.uhn.fhir.jpa.nickname;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -95,8 +95,8 @@ import ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
||||||
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc;
|
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc;
|
||||||
import ca.uhn.fhir.jpa.provider.DiffProvider;
|
import ca.uhn.fhir.jpa.provider.DiffProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.ProcessMessageProvider;
|
|
||||||
import ca.uhn.fhir.jpa.provider.InstanceReindexProvider;
|
import ca.uhn.fhir.jpa.provider.InstanceReindexProvider;
|
||||||
|
import ca.uhn.fhir.jpa.provider.ProcessMessageProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||||
|
@ -149,7 +149,6 @@ import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.config.SearchParamConfig;
|
import ca.uhn.fhir.jpa.searchparam.config.SearchParamConfig;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.IResourceLinkResolver;
|
import ca.uhn.fhir.jpa.searchparam.extractor.IResourceLinkResolver;
|
||||||
import ca.uhn.fhir.jpa.searchparam.nickname.NicknameInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
||||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||||
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
|
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
|
||||||
|
@ -198,7 +197,6 @@ import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ -772,12 +770,6 @@ public class JpaConfig {
|
||||||
return new UnknownCodeSystemWarningValidationSupport(theFhirContext);
|
return new UnknownCodeSystemWarningValidationSupport(theFhirContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Lazy
|
|
||||||
@Bean
|
|
||||||
public NicknameInterceptor nicknameInterceptor() throws IOException {
|
|
||||||
return new NicknameInterceptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ISynchronousSearchSvc synchronousSearchSvc() {
|
public ISynchronousSearchSvc synchronousSearchSvc() {
|
||||||
return new SynchronousSearchSvcImpl();
|
return new SynchronousSearchSvcImpl();
|
||||||
|
|
|
@ -20,13 +20,11 @@
|
||||||
package ca.uhn.fhir.jpa.dao.expunge;
|
package ca.uhn.fhir.jpa.dao.expunge;
|
||||||
|
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -20,14 +20,21 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.config;
|
package ca.uhn.fhir.jpa.mdm.config;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.config.NicknameServiceConfig;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.mdm.interceptor.MdmSearchExpandingInterceptor;
|
import ca.uhn.fhir.mdm.interceptor.MdmSearchExpandingInterceptor;
|
||||||
import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
|
import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.IMatcherFactory;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherFactory;
|
||||||
import ca.uhn.fhir.mdm.svc.MdmLinkDeleteSvc;
|
import ca.uhn.fhir.mdm.svc.MdmLinkDeleteSvc;
|
||||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
|
||||||
|
@Import(NicknameServiceConfig.class)
|
||||||
@Configuration
|
@Configuration
|
||||||
public class MdmCommonConfig {
|
public class MdmCommonConfig {
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -44,4 +51,17 @@ public class MdmCommonConfig {
|
||||||
MdmLinkDeleteSvc mdmLinkDeleteSvc() {
|
MdmLinkDeleteSvc mdmLinkDeleteSvc() {
|
||||||
return new MdmLinkDeleteSvc();
|
return new MdmLinkDeleteSvc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public IMatcherFactory matcherFactory(
|
||||||
|
FhirContext theFhirContext,
|
||||||
|
IMdmSettings theSettings,
|
||||||
|
NicknameSvc theNicknameSvc
|
||||||
|
) {
|
||||||
|
return new MdmMatcherFactory(
|
||||||
|
theFhirContext,
|
||||||
|
theSettings,
|
||||||
|
theNicknameSvc
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByLinkSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchCriteriaBuilderSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchCriteriaBuilderSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.IMatcherFactory;
|
||||||
import ca.uhn.fhir.mdm.util.MdmPartitionHelper;
|
import ca.uhn.fhir.mdm.util.MdmPartitionHelper;
|
||||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
|
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
|
||||||
|
@ -205,8 +206,12 @@ public class MdmConsumerConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
MdmResourceMatcherSvc mdmResourceComparatorSvc(FhirContext theFhirContext, IMdmSettings theMdmSettings) {
|
MdmResourceMatcherSvc mdmResourceComparatorSvc(
|
||||||
return new MdmResourceMatcherSvc(theFhirContext, theMdmSettings);
|
FhirContext theFhirContext,
|
||||||
|
IMatcherFactory theIMatcherFactory,
|
||||||
|
IMdmSettings theMdmSettings
|
||||||
|
) {
|
||||||
|
return new MdmResourceMatcherSvc(theFhirContext, theIMatcherFactory, theMdmSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
|
@ -76,7 +76,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.slf4j.LoggerFactory.getLogger;
|
import static org.slf4j.LoggerFactory.getLogger;
|
||||||
|
|
||||||
@ExtendWith(SpringExtension.class)
|
@ExtendWith(SpringExtension.class)
|
||||||
@ContextConfiguration(classes = {MdmSubmitterConfig.class, MdmConsumerConfig.class, TestMdmConfigR4.class, SubscriptionProcessorConfig.class})
|
@ContextConfiguration(classes = {
|
||||||
|
MdmSubmitterConfig.class,
|
||||||
|
MdmConsumerConfig.class,
|
||||||
|
TestMdmConfigR4.class,
|
||||||
|
SubscriptionProcessorConfig.class
|
||||||
|
})
|
||||||
abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
protected static final String PARTITION_1 = "PART-1";
|
protected static final String PARTITION_1 = "PART-1";
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
|
||||||
import ca.uhn.fhir.mdm.provider.MdmControllerHelper;
|
import ca.uhn.fhir.mdm.provider.MdmControllerHelper;
|
||||||
import ca.uhn.fhir.mdm.provider.MdmProviderDstu3Plus;
|
import ca.uhn.fhir.mdm.provider.MdmProviderDstu3Plus;
|
||||||
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
|
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
|
||||||
|
import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc;
|
||||||
import ca.uhn.fhir.mdm.util.MessageHelper;
|
import ca.uhn.fhir.mdm.util.MessageHelper;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
@ -34,6 +35,8 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test {
|
||||||
@Autowired
|
@Autowired
|
||||||
protected MdmSettings myMdmSettings;
|
protected MdmSettings myMdmSettings;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
protected MdmResourceMatcherSvc myMdmResourceMatcherSvc;
|
||||||
|
@Autowired
|
||||||
private MdmControllerHelper myMdmHelper;
|
private MdmControllerHelper myMdmHelper;
|
||||||
@Autowired
|
@Autowired
|
||||||
Batch2JobHelper myBatch2JobHelper;
|
Batch2JobHelper myBatch2JobHelper;
|
||||||
|
@ -48,7 +51,7 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test {
|
||||||
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
|
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
|
||||||
myMdmSettings.setEnabled(true);
|
myMdmSettings.setEnabled(true);
|
||||||
myMdmSettings.setScriptText(json);
|
myMdmSettings.setScriptText(json);
|
||||||
myMdmResourceMatcherSvc.setMdmSettings(myMdmSettings);
|
myMdmResourceMatcherSvc.setMdmRulesJson(myMdmSettings.getMdmRules());
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
@ -62,7 +65,7 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test {
|
||||||
public void after() throws IOException {
|
public void after() throws IOException {
|
||||||
super.after();
|
super.after();
|
||||||
myMdmSettings.setScriptText(defaultScript);
|
myMdmSettings.setScriptText(defaultScript);
|
||||||
myMdmResourceMatcherSvc.setMdmSettings(myMdmSettings);
|
myMdmResourceMatcherSvc.setMdmRulesJson(myMdmSettings.getMdmRules());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void clearMdmLinks() {
|
protected void clearMdmLinks() {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException;
|
||||||
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.searchparam.nickname.NicknameInterceptor;
|
import ca.uhn.fhir.jpa.searchparam.nickname.NicknameInterceptor;
|
||||||
|
@ -18,7 +19,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -37,11 +37,15 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test {
|
||||||
@Autowired
|
@Autowired
|
||||||
MatchUrlService myMatchUrlService;
|
MatchUrlService myMatchUrlService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
NicknameSvc myNicknameSvc;
|
||||||
|
|
||||||
private NicknameInterceptor myNicknameInterceptor;
|
private NicknameInterceptor myNicknameInterceptor;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() throws IOException {
|
public void before() throws Exception {
|
||||||
myNicknameInterceptor = new NicknameInterceptor();
|
super.before();
|
||||||
|
myNicknameInterceptor = new NicknameInterceptor(myNicknameSvc);
|
||||||
myInterceptorRegistry.registerInterceptor(myNicknameInterceptor);
|
myInterceptorRegistry.registerInterceptor(myNicknameInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
@ -165,8 +165,6 @@
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<pluginManagement>
|
<pluginManagement>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package ca.uhn.fhir.jpa.searchparam.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.nickname.NicknameInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class NicknameServiceConfig {
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Bean
|
||||||
|
public NicknameInterceptor nicknameInterceptor(NicknameSvc theNicknameSvc) {
|
||||||
|
return new NicknameInterceptor(theNicknameSvc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public NicknameSvc nicknameSvc() {
|
||||||
|
return new NicknameSvc();
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,9 +47,13 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
|
|
||||||
|
@Import({
|
||||||
|
NicknameServiceConfig.class
|
||||||
|
})
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SearchParamConfig {
|
public class SearchParamConfig {
|
||||||
|
|
||||||
|
|
|
@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.searchparam.nickname;
|
||||||
|
|
||||||
import ca.uhn.fhir.interceptor.api.Hook;
|
import ca.uhn.fhir.interceptor.api.Hook;
|
||||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -39,8 +39,8 @@ public class NicknameInterceptor {
|
||||||
|
|
||||||
private final NicknameSvc myNicknameSvc;
|
private final NicknameSvc myNicknameSvc;
|
||||||
|
|
||||||
public NicknameInterceptor() throws IOException {
|
public NicknameInterceptor(NicknameSvc theNicknameSvc) {
|
||||||
myNicknameSvc = new NicknameSvc();
|
myNicknameSvc = theNicknameSvc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Hook(Pointcut.STORAGE_PRESEARCH_REGISTERED)
|
@Hook(Pointcut.STORAGE_PRESEARCH_REGISTERED)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.jpa.searchparam.nickname;
|
package ca.uhn.fhir.jpa.searchparam.nickname;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -9,13 +10,18 @@ import java.io.IOException;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
class NicknameInterceptorTest {
|
class NicknameInterceptorTest {
|
||||||
|
|
||||||
|
private NicknameInterceptor createNicknameInterceptor() {
|
||||||
|
return new NicknameInterceptor(new NicknameSvc());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExpandForward() throws IOException {
|
public void testExpandForward() throws IOException {
|
||||||
// setup
|
// setup
|
||||||
String formalName = "kenneth";
|
String formalName = "kenneth";
|
||||||
SearchParameterMap sp = new SearchParameterMap();
|
SearchParameterMap sp = new SearchParameterMap();
|
||||||
sp.add("name", new StringParam(formalName).setNicknameExpand(true));
|
sp.add("name", new StringParam(formalName).setNicknameExpand(true));
|
||||||
NicknameInterceptor svc = new NicknameInterceptor();
|
NicknameInterceptor svc = createNicknameInterceptor();
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
svc.expandNicknames(sp);
|
svc.expandNicknames(sp);
|
||||||
|
@ -31,7 +37,7 @@ class NicknameInterceptorTest {
|
||||||
String nickname = "ken";
|
String nickname = "ken";
|
||||||
SearchParameterMap sp = new SearchParameterMap();
|
SearchParameterMap sp = new SearchParameterMap();
|
||||||
sp.add("name", new StringParam(nickname).setNicknameExpand(true));
|
sp.add("name", new StringParam(nickname).setNicknameExpand(true));
|
||||||
NicknameInterceptor svc = new NicknameInterceptor();
|
NicknameInterceptor svc = createNicknameInterceptor();
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
svc.expandNicknames(sp);
|
svc.expandNicknames(sp);
|
||||||
|
@ -47,7 +53,7 @@ class NicknameInterceptorTest {
|
||||||
String unusualName = "X Æ A-12";
|
String unusualName = "X Æ A-12";
|
||||||
SearchParameterMap sp = new SearchParameterMap();
|
SearchParameterMap sp = new SearchParameterMap();
|
||||||
sp.add("name", new StringParam(unusualName).setNicknameExpand(true));
|
sp.add("name", new StringParam(unusualName).setNicknameExpand(true));
|
||||||
NicknameInterceptor svc = new NicknameInterceptor();
|
NicknameInterceptor svc = createNicknameInterceptor();
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
svc.expandNicknames(sp);
|
svc.expandNicknames(sp);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.api;
|
package ca.uhn.fhir.mdm.api;
|
||||||
|
|
||||||
import ca.uhn.fhir.mdm.dao.IMdmLinkImplFactory;
|
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
|
@ -139,7 +139,7 @@ public class MdmControllerHelper {
|
||||||
public IBaseBundle getMatchesAndPossibleMatchesForResource(IAnyResource theResource, String theResourceType, RequestDetails theRequestDetails) {
|
public IBaseBundle getMatchesAndPossibleMatchesForResource(IAnyResource theResource, String theResourceType, RequestDetails theRequestDetails) {
|
||||||
RequestPartitionId requestPartitionId;
|
RequestPartitionId requestPartitionId;
|
||||||
ReadPartitionIdRequestDetails details = ReadPartitionIdRequestDetails.forSearchType(theResourceType, null, null);
|
ReadPartitionIdRequestDetails details = ReadPartitionIdRequestDetails.forSearchType(theResourceType, null, null);
|
||||||
if (myMdmSettings.getSearchAllPartitionForMatch()){
|
if (myMdmSettings.getSearchAllPartitionForMatch()) {
|
||||||
requestPartitionId = RequestPartitionId.allPartitions();
|
requestPartitionId = RequestPartitionId.allPartitions();
|
||||||
} else {
|
} else {
|
||||||
requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, details);
|
requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, details);
|
||||||
|
|
|
@ -19,21 +19,16 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.json;
|
package ca.uhn.fhir.mdm.rules.json;
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchEvaluation;
|
|
||||||
import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum;
|
|
||||||
import ca.uhn.fhir.model.api.IModelJson;
|
import ca.uhn.fhir.model.api.IModelJson;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all business data for determining if a match exists on a particular field, given:
|
* Contains all business data for determining if a match exists on a particular field, given:
|
||||||
* <p></p>
|
* <p></p>
|
||||||
* 1. A {@link MdmMatcherEnum} which determines the actual similarity values.
|
* 1. A {@link MatchTypeEnum} which determines the actual similarity values.
|
||||||
* 2. A given resource type (e.g. Patient)
|
* 2. A given resource type (e.g. Patient)
|
||||||
* 3. A given FHIRPath expression for finding the particular primitive to be used for comparison. (e.g. name.given)
|
* 3. A given FHIRPath expression for finding the particular primitive to be used for comparison. (e.g. name.given)
|
||||||
*/
|
*/
|
||||||
|
@ -87,10 +82,6 @@ public class MdmFieldMatchJson implements IModelJson {
|
||||||
return myMatcher;
|
return myMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMatcherSupportingEmptyFields() {
|
|
||||||
return (getMatcher() == null) ? false : getMatcher().isMatchingEmptyFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MdmFieldMatchJson setMatcher(MdmMatcherJson theMatcher) {
|
public MdmFieldMatchJson setMatcher(MdmMatcherJson theMatcher) {
|
||||||
myMatcher = theMatcher;
|
myMatcher = theMatcher;
|
||||||
return this;
|
return this;
|
||||||
|
@ -105,17 +96,6 @@ public class MdmFieldMatchJson implements IModelJson {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MdmMatchEvaluation match(FhirContext theFhirContext, IBase theLeftValue, IBase theRightValue) {
|
|
||||||
if (myMatcher != null) {
|
|
||||||
boolean result = myMatcher.match(theFhirContext, theLeftValue, theRightValue);
|
|
||||||
return new MdmMatchEvaluation(result, result ? 1.0 : 0.0);
|
|
||||||
}
|
|
||||||
if (mySimilarity != null) {
|
|
||||||
return mySimilarity.match(theFhirContext, theLeftValue, theRightValue);
|
|
||||||
}
|
|
||||||
throw new InternalErrorException(Msg.code(1522) + "Field Match " + myName + " has neither a matcher nor a similarity.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFhirPath() {
|
public String getFhirPath() {
|
||||||
return myFhirPath;
|
return myFhirPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,15 +19,13 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.json;
|
package ca.uhn.fhir.mdm.rules.json;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum;
|
|
||||||
import ca.uhn.fhir.model.api.IModelJson;
|
import ca.uhn.fhir.model.api.IModelJson;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
|
|
||||||
public class MdmMatcherJson implements IModelJson {
|
public class MdmMatcherJson implements IModelJson {
|
||||||
@JsonProperty(value = "algorithm", required = true)
|
@JsonProperty(value = "algorithm", required = true)
|
||||||
MdmMatcherEnum myAlgorithm;
|
MatchTypeEnum myAlgorithm;
|
||||||
|
|
||||||
@JsonProperty(value = "identifierSystem", required = false)
|
@JsonProperty(value = "identifierSystem", required = false)
|
||||||
String myIdentifierSystem;
|
String myIdentifierSystem;
|
||||||
|
@ -38,11 +36,11 @@ public class MdmMatcherJson implements IModelJson {
|
||||||
@JsonProperty(value = "exact")
|
@JsonProperty(value = "exact")
|
||||||
boolean myExact;
|
boolean myExact;
|
||||||
|
|
||||||
public MdmMatcherEnum getAlgorithm() {
|
public MatchTypeEnum getAlgorithm() {
|
||||||
return myAlgorithm;
|
return myAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MdmMatcherJson setAlgorithm(MdmMatcherEnum theAlgorithm) {
|
public MdmMatcherJson setAlgorithm(MatchTypeEnum theAlgorithm) {
|
||||||
myAlgorithm = theAlgorithm;
|
myAlgorithm = theAlgorithm;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -64,12 +62,4 @@ public class MdmMatcherJson implements IModelJson {
|
||||||
myExact = theExact;
|
myExact = theExact;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMatchingEmptyFields() {
|
|
||||||
return myAlgorithm.isMatchingEmptyFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean match(FhirContext theFhirContext, IBase theLeftValue, IBase theRightValue) {
|
|
||||||
return myAlgorithm.match(theFhirContext, theLeftValue, theRightValue, myExact, myIdentifierSystem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Master Data Management
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
|
|
||||||
public class HapiDateMatcher implements IMdmFieldMatcher {
|
|
||||||
private final HapiDateMatcherDstu3 myHapiDateMatcherDstu3 = new HapiDateMatcherDstu3();
|
|
||||||
private final HapiDateMatcherR4 myHapiDateMatcherR4 = new HapiDateMatcherR4();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) {
|
|
||||||
switch (theFhirContext.getVersion().getVersion()) {
|
|
||||||
case DSTU3:
|
|
||||||
return myHapiDateMatcherDstu3.match(theLeftBase, theRightBase);
|
|
||||||
case R4:
|
|
||||||
return myHapiDateMatcherR4.match(theLeftBase, theRightBase);
|
|
||||||
default:
|
|
||||||
throw new UnsupportedOperationException(Msg.code(1520) + "Version not supported: " + theFhirContext.getVersion().getVersion());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Master Data Management
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
|
||||||
|
|
||||||
import org.hl7.fhir.dstu3.model.BaseDateTimeType;
|
|
||||||
import org.hl7.fhir.dstu3.model.DateTimeType;
|
|
||||||
import org.hl7.fhir.dstu3.model.DateType;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
|
|
||||||
public class HapiDateMatcherDstu3 {
|
|
||||||
// TODO KHS code duplication (tried generalizing it with generics, but it got too convoluted)
|
|
||||||
public boolean match(IBase theLeftBase, IBase theRightBase) {
|
|
||||||
if (theLeftBase instanceof BaseDateTimeType && theRightBase instanceof BaseDateTimeType) {
|
|
||||||
BaseDateTimeType leftDate = (BaseDateTimeType) theLeftBase;
|
|
||||||
BaseDateTimeType rightDate = (BaseDateTimeType) theRightBase;
|
|
||||||
int comparison = leftDate.getPrecision().compareTo(rightDate.getPrecision());
|
|
||||||
if (comparison == 0) {
|
|
||||||
return leftDate.getValueAsString().equals(rightDate.getValueAsString());
|
|
||||||
}
|
|
||||||
BaseDateTimeType leftPDate;
|
|
||||||
BaseDateTimeType rightPDate;
|
|
||||||
if (comparison > 0) {
|
|
||||||
leftPDate = leftDate;
|
|
||||||
if (rightDate instanceof DateType) {
|
|
||||||
rightPDate = new DateType(rightDate.getValue(), leftDate.getPrecision());
|
|
||||||
} else {
|
|
||||||
rightPDate = new DateTimeType(rightDate.getValue(), leftDate.getPrecision());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rightPDate = rightDate;
|
|
||||||
if (leftDate instanceof DateType) {
|
|
||||||
leftPDate = new DateType(leftDate.getValue(), rightDate.getPrecision());
|
|
||||||
} else {
|
|
||||||
leftPDate = new DateTimeType(leftDate.getValue(), rightDate.getPrecision());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return leftPDate.getValueAsString().equals(rightPDate.getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Master Data Management
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
import org.hl7.fhir.r4.model.BaseDateTimeType;
|
|
||||||
import org.hl7.fhir.r4.model.DateTimeType;
|
|
||||||
import org.hl7.fhir.r4.model.DateType;
|
|
||||||
|
|
||||||
public class HapiDateMatcherR4 {
|
|
||||||
// TODO KHS code duplication (tried generalizing it with generics, but it got too convoluted)
|
|
||||||
public boolean match(IBase theLeftBase, IBase theRightBase) {
|
|
||||||
if (theLeftBase instanceof BaseDateTimeType && theRightBase instanceof BaseDateTimeType) {
|
|
||||||
BaseDateTimeType leftDate = (BaseDateTimeType) theLeftBase;
|
|
||||||
BaseDateTimeType rightDate = (BaseDateTimeType) theRightBase;
|
|
||||||
int comparison = leftDate.getPrecision().compareTo(rightDate.getPrecision());
|
|
||||||
if (comparison == 0) {
|
|
||||||
return leftDate.getValueAsString().equals(rightDate.getValueAsString());
|
|
||||||
}
|
|
||||||
BaseDateTimeType leftPDate;
|
|
||||||
BaseDateTimeType rightPDate;
|
|
||||||
//Left date is coarser
|
|
||||||
if (comparison < 0) {
|
|
||||||
leftPDate = leftDate;
|
|
||||||
if (rightDate instanceof DateType) {
|
|
||||||
rightPDate = new DateType(rightDate.getValue(), leftDate.getPrecision());
|
|
||||||
} else {
|
|
||||||
rightPDate = new DateTimeType(rightDate.getValue(), leftDate.getPrecision());
|
|
||||||
}
|
|
||||||
//Right date is coarser
|
|
||||||
} else {
|
|
||||||
rightPDate = rightDate;
|
|
||||||
if (leftDate instanceof DateType) {
|
|
||||||
leftPDate = new DateType(leftDate.getValue(), rightDate.getPrecision());
|
|
||||||
} else {
|
|
||||||
leftPDate = new DateTimeType(leftDate.getValue(), rightDate.getPrecision());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return leftPDate.getValueAsString().equals(rightPDate.getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package ca.uhn.fhir.mdm.rules.matcher;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
|
|
||||||
|
public interface IMatcherFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the field matcher for the given MatchTypeEnum
|
||||||
|
*/
|
||||||
|
IMdmFieldMatcher getFieldMatcherForMatchType(MatchTypeEnum theMdmMatcherEnum);
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Master Data Management
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
|
||||||
|
|
||||||
public interface IMdmStringMatcher {
|
|
||||||
boolean matches(String theLeftString, String theRightString);
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Master Data Management
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum for holding all the known FHIR Element matchers that we support in HAPI. The string matchers first
|
|
||||||
* encode the string using an Apache Encoder before comparing them.
|
|
||||||
* https://commons.apache.org/proper/commons-codec/userguide.html
|
|
||||||
*/
|
|
||||||
public enum MdmMatcherEnum {
|
|
||||||
|
|
||||||
CAVERPHONE1(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE1))),
|
|
||||||
CAVERPHONE2(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE2))),
|
|
||||||
COLOGNE(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.COLOGNE))),
|
|
||||||
DOUBLE_METAPHONE(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.DOUBLE_METAPHONE))),
|
|
||||||
MATCH_RATING_APPROACH(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.MATCH_RATING_APPROACH))),
|
|
||||||
METAPHONE(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.METAPHONE))),
|
|
||||||
NYSIIS(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.NYSIIS))),
|
|
||||||
REFINED_SOUNDEX(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.REFINED_SOUNDEX))),
|
|
||||||
SOUNDEX(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.SOUNDEX))),
|
|
||||||
NICKNAME(new HapiStringMatcher(new NicknameMatcher())),
|
|
||||||
|
|
||||||
STRING(new HapiStringMatcher()),
|
|
||||||
SUBSTRING(new HapiStringMatcher(new SubstringStringMatcher())),
|
|
||||||
|
|
||||||
DATE(new HapiDateMatcher()),
|
|
||||||
NAME_ANY_ORDER(new NameMatcher(MdmNameMatchModeEnum.ANY_ORDER)),
|
|
||||||
NAME_FIRST_AND_LAST(new NameMatcher(MdmNameMatchModeEnum.FIRST_AND_LAST)),
|
|
||||||
|
|
||||||
IDENTIFIER(new IdentifierMatcher()),
|
|
||||||
|
|
||||||
EMPTY_FIELD(new EmptyFieldMatcher()),
|
|
||||||
EXTENSION_ANY_ORDER(new ExtensionMatcher()),
|
|
||||||
NUMERIC(new HapiStringMatcher(new NumericMatcher()));
|
|
||||||
|
|
||||||
private final IMdmFieldMatcher myMdmFieldMatcher;
|
|
||||||
|
|
||||||
MdmMatcherEnum(IMdmFieldMatcher theMdmFieldMatcher) {
|
|
||||||
myMdmFieldMatcher = theMdmFieldMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether two FHIR elements match according using the provided {@link IMdmFieldMatcher}
|
|
||||||
*
|
|
||||||
* @param theFhirContext
|
|
||||||
* @param theLeftBase left FHIR element to compare
|
|
||||||
* @param theRightBase right FHIR element to compare
|
|
||||||
* @param theExact used by String matchers. If "false" then the string is normalized (case, accents) before comparing. If "true" then an exact string comparison is performed.
|
|
||||||
* @param theIdentifierSystem used optionally by the IDENTIFIER matcher, when present, only matches the identifiers if they belong to this system.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public boolean match(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) {
|
|
||||||
return myMdmFieldMatcher.matches(theFhirContext, theLeftBase, theRightBase, theExact, theIdentifierSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if this matcher supports checks on empty fields
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* Returns true of this matcher supports empty fields and false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isMatchingEmptyFields() {
|
|
||||||
return this == EMPTY_FIELD;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package ca.uhn.fhir.mdm.rules.matcher;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
|
||||||
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.EmptyFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.ExtensionMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.HapiDateMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.HapiStringMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.IdentifierMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.MdmNameMatchModeEnum;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.NameMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.NicknameMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.NumericMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.PhoneticEncoderMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.SubstringStringMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
public class MdmMatcherFactory implements IMatcherFactory {
|
||||||
|
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
||||||
|
|
||||||
|
private final FhirContext myFhirContext;
|
||||||
|
private final IMdmSettings myMdmSettings;
|
||||||
|
|
||||||
|
private final NicknameSvc myNicknameSvc;
|
||||||
|
|
||||||
|
public MdmMatcherFactory(
|
||||||
|
FhirContext theFhirContext,
|
||||||
|
IMdmSettings theSettings,
|
||||||
|
NicknameSvc theNicknameSvc
|
||||||
|
) {
|
||||||
|
myFhirContext = theFhirContext;
|
||||||
|
myMdmSettings = theSettings;
|
||||||
|
myNicknameSvc = theNicknameSvc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMdmFieldMatcher getFieldMatcherForMatchType(MatchTypeEnum theMdmMatcherEnum) {
|
||||||
|
String matchTypeName;
|
||||||
|
if (theMdmMatcherEnum != null) {
|
||||||
|
switch (theMdmMatcherEnum) {
|
||||||
|
case CAVERPHONE1:
|
||||||
|
return new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE1);
|
||||||
|
case CAVERPHONE2:
|
||||||
|
return new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE2);
|
||||||
|
case COLOGNE:
|
||||||
|
return new PhoneticEncoderMatcher(PhoneticEncoderEnum.COLOGNE);
|
||||||
|
case DOUBLE_METAPHONE:
|
||||||
|
return new PhoneticEncoderMatcher(PhoneticEncoderEnum.DOUBLE_METAPHONE);
|
||||||
|
case MATCH_RATING_APPROACH:
|
||||||
|
return new PhoneticEncoderMatcher(PhoneticEncoderEnum.MATCH_RATING_APPROACH);
|
||||||
|
case METAPHONE:
|
||||||
|
return new PhoneticEncoderMatcher(PhoneticEncoderEnum.METAPHONE);
|
||||||
|
case NYSIIS:
|
||||||
|
return new PhoneticEncoderMatcher(PhoneticEncoderEnum.NYSIIS);
|
||||||
|
case REFINED_SOUNDEX:
|
||||||
|
return new PhoneticEncoderMatcher(PhoneticEncoderEnum.REFINED_SOUNDEX);
|
||||||
|
case SOUNDEX:
|
||||||
|
return new PhoneticEncoderMatcher(PhoneticEncoderEnum.SOUNDEX);
|
||||||
|
case NICKNAME:
|
||||||
|
return new NicknameMatcher(myNicknameSvc);
|
||||||
|
case STRING:
|
||||||
|
return new HapiStringMatcher();
|
||||||
|
case SUBSTRING:
|
||||||
|
return new SubstringStringMatcher();
|
||||||
|
case DATE:
|
||||||
|
return new HapiDateMatcher(myFhirContext);
|
||||||
|
case NAME_ANY_ORDER:
|
||||||
|
return new NameMatcher(myFhirContext, MdmNameMatchModeEnum.ANY_ORDER);
|
||||||
|
case NAME_FIRST_AND_LAST:
|
||||||
|
return new NameMatcher(myFhirContext, MdmNameMatchModeEnum.FIRST_AND_LAST);
|
||||||
|
case IDENTIFIER:
|
||||||
|
return new IdentifierMatcher();
|
||||||
|
case EXTENSION_ANY_ORDER:
|
||||||
|
return new ExtensionMatcher();
|
||||||
|
case NUMERIC:
|
||||||
|
return new NumericMatcher();
|
||||||
|
case EMPTY_FIELD:
|
||||||
|
return new EmptyFieldMatcher();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
matchTypeName = theMdmMatcherEnum.name();
|
||||||
|
} else {
|
||||||
|
matchTypeName = "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is odd, but it's a valid code path
|
||||||
|
ourLog.warn("Unrecognized field type {}. Returning null", matchTypeName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Master Data Management
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
|
||||||
|
|
||||||
public class SubstringStringMatcher implements IMdmStringMatcher {
|
|
||||||
@Override
|
|
||||||
public boolean matches(String theLeftString, String theRightString) {
|
|
||||||
return theLeftString.startsWith(theRightString) || theRightString.startsWith(theLeftString);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.r4.model.DateTimeType;
|
||||||
|
import org.hl7.fhir.r4.model.DateType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper class for datetimes of ambiguous fhir version
|
||||||
|
*/
|
||||||
|
public class DateTimeWrapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The precision of this datetime object
|
||||||
|
*/
|
||||||
|
private final TemporalPrecisionEnum myPrecision;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The string value with current precision
|
||||||
|
*/
|
||||||
|
private final String myValueAsString;
|
||||||
|
|
||||||
|
public DateTimeWrapper(FhirContext theFhirContext, IBase theDate) {
|
||||||
|
if (theDate instanceof org.hl7.fhir.dstu3.model.BaseDateTimeType) {
|
||||||
|
org.hl7.fhir.dstu3.model.BaseDateTimeType dstu3Date = (org.hl7.fhir.dstu3.model.BaseDateTimeType) theDate;
|
||||||
|
myPrecision = dstu3Date.getPrecision();
|
||||||
|
myValueAsString = dstu3Date.getValueAsString();
|
||||||
|
} else if (theDate instanceof org.hl7.fhir.r4.model.BaseDateTimeType) {
|
||||||
|
org.hl7.fhir.r4.model.BaseDateTimeType r4Date = (org.hl7.fhir.r4.model.BaseDateTimeType) theDate;
|
||||||
|
myPrecision = r4Date.getPrecision();
|
||||||
|
myValueAsString = r4Date.getValueAsString();
|
||||||
|
} else {
|
||||||
|
// we should consider changing this error so we don't need the fhir context at all
|
||||||
|
throw new UnsupportedOperationException(Msg.code(1520) + "Version not supported: " + theFhirContext.getVersion().getVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemporalPrecisionEnum getPrecision() {
|
||||||
|
return myPrecision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValueAsStringWithPrecision(TemporalPrecisionEnum thePrecision) {
|
||||||
|
// we are using an R4 DateTypes because all datetime strings are the same for all fhir versions
|
||||||
|
// (and so it won't matter here)
|
||||||
|
// we just want the string at a specific precision
|
||||||
|
DateTimeType dateTimeType = new DateTimeType(myValueAsString);
|
||||||
|
dateTimeType.setPrecision(thePrecision);
|
||||||
|
return dateTimeType.getValueAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValueAsString() {
|
||||||
|
return myValueAsString;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,9 +17,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
|
||||||
public class EmptyFieldMatcher implements IMdmFieldMatcher {
|
public class EmptyFieldMatcher implements IMdmFieldMatcher {
|
||||||
|
@ -28,7 +29,7 @@ public class EmptyFieldMatcher implements IMdmFieldMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) {
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) {
|
||||||
for (IBase b : new IBase[] {theLeftBase, theRightBase}) {
|
for (IBase b : new IBase[] {theLeftBase, theRightBase}) {
|
||||||
if (b != null && !b.isEmpty()) {
|
if (b != null && !b.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -36,4 +37,9 @@ public class EmptyFieldMatcher implements IMdmFieldMatcher {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMatchingEmptyFields() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,9 +17,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
import ca.uhn.fhir.util.ExtensionUtil;
|
import ca.uhn.fhir.util.ExtensionUtil;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||||
|
@ -30,7 +31,7 @@ import java.util.List;
|
||||||
public class ExtensionMatcher implements IMdmFieldMatcher {
|
public class ExtensionMatcher implements IMdmFieldMatcher {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) {
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) {
|
||||||
if (!(theLeftBase instanceof IBaseHasExtensions && theRightBase instanceof IBaseHasExtensions)) {
|
if (!(theLeftBase instanceof IBaseHasExtensions && theRightBase instanceof IBaseHasExtensions)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Master Data Management
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
|
||||||
|
public class HapiDateMatcher implements IMdmFieldMatcher {
|
||||||
|
|
||||||
|
private final FhirContext myFhirContext;
|
||||||
|
|
||||||
|
public HapiDateMatcher(FhirContext theFhirContext) {
|
||||||
|
myFhirContext = theFhirContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) {
|
||||||
|
DateTimeWrapper left = new DateTimeWrapper(myFhirContext, theLeftBase);
|
||||||
|
DateTimeWrapper right = new DateTimeWrapper(myFhirContext, theRightBase);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we use the precision to determine how we should equate.
|
||||||
|
* We should use the same precision (the precision of the less
|
||||||
|
* precise date)
|
||||||
|
*/
|
||||||
|
int comparison = left.getPrecision().compareTo(right.getPrecision());
|
||||||
|
|
||||||
|
String leftString;
|
||||||
|
String rightString;
|
||||||
|
if (comparison == 0) {
|
||||||
|
// same precision
|
||||||
|
leftString = left.getValueAsString();
|
||||||
|
rightString = right.getValueAsString();
|
||||||
|
} else if (comparison > 0) {
|
||||||
|
// left date is more precise than right date
|
||||||
|
leftString = left.getValueAsStringWithPrecision(right.getPrecision());
|
||||||
|
rightString = right.getValueAsString();
|
||||||
|
} else {
|
||||||
|
// right date is more precise than left date
|
||||||
|
rightString = right.getValueAsStringWithPrecision(left.getPrecision());
|
||||||
|
leftString = left.getValueAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return leftString.equals(rightString);
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,33 +17,26 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.util.StringMatcherUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similarity measure for two IBase fields whose similarity can be measured by their String representations.
|
* Similarity measure for two IBase fields whose similarity can be measured by their String representations.
|
||||||
*/
|
*/
|
||||||
public class HapiStringMatcher extends BaseHapiStringMetric implements IMdmFieldMatcher {
|
public class HapiStringMatcher implements IMdmFieldMatcher {
|
||||||
private final IMdmStringMatcher myStringMatcher;
|
|
||||||
|
|
||||||
public HapiStringMatcher(IMdmStringMatcher theStringMatcher) {
|
|
||||||
myStringMatcher = theStringMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HapiStringMatcher() {
|
|
||||||
myStringMatcher = String::equals;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) {
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theExtraMatchParams) {
|
||||||
if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) {
|
if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) {
|
||||||
String leftString = extractString((IPrimitiveType<?>) theLeftBase, theExact);
|
String leftString = StringMatcherUtils.extractString((IPrimitiveType<?>) theLeftBase, theExtraMatchParams.getExact());
|
||||||
String rightString = extractString((IPrimitiveType<?>) theRightBase, theExact);
|
String rightString = StringMatcherUtils.extractString((IPrimitiveType<?>) theRightBase, theExtraMatchParams.getExact());
|
||||||
|
|
||||||
return myStringMatcher.matches(leftString, rightString);
|
return leftString.equals(rightString);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
|
@ -17,25 +17,34 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
import ca.uhn.fhir.util.CanonicalIdentifier;
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
import ca.uhn.fhir.mdm.util.IdentifierUtil;
|
import ca.uhn.fhir.mdm.util.IdentifierUtil;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.util.CanonicalIdentifier;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
|
||||||
public class IdentifierMatcher implements IMdmFieldMatcher {
|
public class IdentifierMatcher implements IMdmFieldMatcher {
|
||||||
|
|
||||||
|
private boolean isEmpty(StringDt theValue) {
|
||||||
|
if (theValue == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return theValue.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if the two fhir identifiers are the same. If @param theIdentifierSystem is not null, then the
|
* @return true if the two fhir identifiers are the same. If @param theIdentifierSystem is not null, then the
|
||||||
* matcher only returns true if the identifier systems also match this system.
|
* matcher only returns true if the identifier systems also match this system.
|
||||||
* @throws UnsupportedOperationException if either Base is not an Identifier instance
|
* @throws UnsupportedOperationException if either Base is not an Identifier instance
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) {
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) {
|
||||||
CanonicalIdentifier left = IdentifierUtil.identifierDtFromIdentifier(theLeftBase);
|
CanonicalIdentifier left = IdentifierUtil.identifierDtFromIdentifier(theLeftBase);
|
||||||
if (theIdentifierSystem != null) {
|
if (theParams.getIdentifierSystem() != null) {
|
||||||
if (!theIdentifierSystem.equals(left.getSystemElement().getValueAsString())) {
|
if (!theParams.getIdentifierSystem().equals(left.getSystemElement().getValueAsString())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,11 +54,4 @@ public class IdentifierMatcher implements IMdmFieldMatcher {
|
||||||
}
|
}
|
||||||
return left.equals(right);
|
return left.equals(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEmpty(StringDt theValue) {
|
|
||||||
if (theValue == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return theValue.isEmpty();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
public enum MdmNameMatchModeEnum {
|
public enum MdmNameMatchModeEnum {
|
||||||
ANY_ORDER,
|
ANY_ORDER,
|
|
@ -17,9 +17,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
import ca.uhn.fhir.mdm.util.NameUtil;
|
import ca.uhn.fhir.mdm.util.NameUtil;
|
||||||
import ca.uhn.fhir.util.StringUtil;
|
import ca.uhn.fhir.util.StringUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -35,24 +37,27 @@ public class NameMatcher implements IMdmFieldMatcher {
|
||||||
|
|
||||||
private final MdmNameMatchModeEnum myMatchMode;
|
private final MdmNameMatchModeEnum myMatchMode;
|
||||||
|
|
||||||
public NameMatcher(MdmNameMatchModeEnum theMatchMode) {
|
private final FhirContext myFhirContext;
|
||||||
|
|
||||||
|
public NameMatcher(FhirContext theFhirContext, MdmNameMatchModeEnum theMatchMode) {
|
||||||
myMatchMode = theMatchMode;
|
myMatchMode = theMatchMode;
|
||||||
|
myFhirContext = theFhirContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) {
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) {
|
||||||
String leftFamilyName = NameUtil.extractFamilyName(theFhirContext, theLeftBase);
|
String leftFamilyName = NameUtil.extractFamilyName(myFhirContext, theLeftBase);
|
||||||
String rightFamilyName = NameUtil.extractFamilyName(theFhirContext, theRightBase);
|
String rightFamilyName = NameUtil.extractFamilyName(myFhirContext, theRightBase);
|
||||||
if (StringUtils.isEmpty(leftFamilyName) || StringUtils.isEmpty(rightFamilyName)) {
|
if (StringUtils.isEmpty(leftFamilyName) || StringUtils.isEmpty(rightFamilyName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean match = false;
|
boolean match = false;
|
||||||
|
|
||||||
List<String> leftGivenNames = NameUtil.extractGivenNames(theFhirContext, theLeftBase);
|
List<String> leftGivenNames = NameUtil.extractGivenNames(myFhirContext, theLeftBase);
|
||||||
List<String> rightGivenNames = NameUtil.extractGivenNames(theFhirContext, theRightBase);
|
List<String> rightGivenNames = NameUtil.extractGivenNames(myFhirContext, theRightBase);
|
||||||
|
|
||||||
if (!theExact) {
|
if (!theParams.getExact()) {
|
||||||
leftFamilyName = StringUtil.normalizeStringForSearchIndexing(leftFamilyName);
|
leftFamilyName = StringUtil.normalizeStringForSearchIndexing(leftFamilyName);
|
||||||
rightFamilyName = StringUtil.normalizeStringForSearchIndexing(rightFamilyName);
|
rightFamilyName = StringUtil.normalizeStringForSearchIndexing(rightFamilyName);
|
||||||
leftGivenNames = leftGivenNames.stream().map(StringUtil::normalizeStringForSearchIndexing).collect(Collectors.toList());
|
leftGivenNames = leftGivenNames.stream().map(StringUtil::normalizeStringForSearchIndexing).collect(Collectors.toList());
|
|
@ -17,28 +17,25 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
import ca.uhn.fhir.jpa.searchparam.nickname.NicknameSvc;
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.util.StringMatcherUtils;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public class NicknameMatcher implements IMdmStringMatcher {
|
public class NicknameMatcher implements IMdmFieldMatcher {
|
||||||
private final NicknameSvc myNicknameSvc;
|
private final NicknameSvc myNicknameSvc;
|
||||||
|
|
||||||
public NicknameMatcher() {
|
public NicknameMatcher(NicknameSvc theNicknameSvc) {
|
||||||
try {
|
myNicknameSvc = theNicknameSvc;
|
||||||
myNicknameSvc = new NicknameSvc();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ConfigurationException(Msg.code(2234) + "Unable to load nicknames", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(String theLeftString, String theRightString) {
|
public boolean matches(String theLeftString, String theRightString) {
|
||||||
String leftString = theLeftString.toLowerCase(Locale.ROOT);
|
String leftString = theLeftString.toLowerCase(Locale.ROOT);
|
||||||
String rightString = theRightString.toLowerCase(Locale.ROOT);
|
String rightString = theRightString.toLowerCase(Locale.ROOT);
|
||||||
|
@ -51,4 +48,15 @@ public class NicknameMatcher implements IMdmStringMatcher {
|
||||||
Collection<String> rightNames = myNicknameSvc.getEquivalentNames(rightString);
|
Collection<String> rightNames = myNicknameSvc.getEquivalentNames(rightString);
|
||||||
return rightNames.contains(leftString);
|
return rightNames.contains(leftString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) {
|
||||||
|
if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) {
|
||||||
|
String leftString = StringMatcherUtils.extractString((IPrimitiveType<?>) theLeftBase, theParams.getExact());
|
||||||
|
String rightString = StringMatcherUtils.extractString((IPrimitiveType<?>) theRightBase, theParams.getExact());
|
||||||
|
|
||||||
|
return matches(leftString, rightString);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,19 +17,27 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.phonetic.NumericEncoder;
|
import ca.uhn.fhir.context.phonetic.NumericEncoder;
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.util.StringMatcherUtils;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
// Useful for numerical identifiers like phone numbers, address parts etc.
|
// Useful for numerical identifiers like phone numbers, address parts etc.
|
||||||
// This should not be used where decimals are important. A new "quantity matcher" should be added to handle cases like that.
|
// This should not be used where decimals are important. A new "quantity matcher" should be added to handle cases like that.
|
||||||
public class NumericMatcher implements IMdmStringMatcher {
|
public class NumericMatcher implements IMdmFieldMatcher {
|
||||||
private final NumericEncoder encoder = new NumericEncoder();
|
private final NumericEncoder encoder = new NumericEncoder();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(String theLeftString, String theRightString) {
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) {
|
||||||
String left = encoder.encode(theLeftString);
|
if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) {
|
||||||
String right = encoder.encode(theRightString);
|
String left = encoder.encode(StringMatcherUtils.extractString((IPrimitiveType<?>) theLeftBase, theParams.getExact()));
|
||||||
return left.equals(right);
|
String right = encoder.encode(StringMatcherUtils.extractString((IPrimitiveType<?>) theRightBase, theParams.getExact()));
|
||||||
|
return left.equals(right);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,15 +17,20 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
|
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
|
||||||
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
|
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.util.StringMatcherUtils;
|
||||||
import ca.uhn.fhir.util.PhoneticEncoderUtil;
|
import ca.uhn.fhir.util.PhoneticEncoderUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class PhoneticEncoderMatcher implements IMdmStringMatcher {
|
public class PhoneticEncoderMatcher implements IMdmFieldMatcher {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(PhoneticEncoderMatcher.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(PhoneticEncoderMatcher.class);
|
||||||
|
|
||||||
private final IPhoneticEncoder myStringEncoder;
|
private final IPhoneticEncoder myStringEncoder;
|
||||||
|
@ -34,8 +39,16 @@ public class PhoneticEncoderMatcher implements IMdmStringMatcher {
|
||||||
myStringEncoder = PhoneticEncoderUtil.getEncoder(thePhoneticEnum.name());
|
myStringEncoder = PhoneticEncoderUtil.getEncoder(thePhoneticEnum.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(String theLeftString, String theRightString) {
|
public boolean matches(String theLeftString, String theRightString) {
|
||||||
return myStringEncoder.encode(theLeftString).equals(myStringEncoder.encode(theRightString));
|
return myStringEncoder.encode(theLeftString).equals(myStringEncoder.encode(theRightString));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) {
|
||||||
|
String leftString = StringMatcherUtils.extractString((IPrimitiveType<?>) theLeftBase, theParams.getExact());
|
||||||
|
String rightString = StringMatcherUtils.extractString((IPrimitiveType<?>) theRightBase, theParams.getExact());
|
||||||
|
|
||||||
|
return matches(leftString, rightString);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Master Data Management
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.util.StringMatcherUtils;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
public class SubstringStringMatcher implements IMdmFieldMatcher {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) {
|
||||||
|
if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) {
|
||||||
|
String left = StringMatcherUtils.extractString((IPrimitiveType<?>) theLeftBase, theParams.getExact());
|
||||||
|
String right = StringMatcherUtils.extractString((IPrimitiveType<?>) theRightBase, theParams.getExact());
|
||||||
|
return left.startsWith(right) || right.startsWith(left);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,14 +17,27 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.models;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Measure how similar two IBase (resource fields) are to one another. 1.0 means identical. 0.0 means completely different.
|
* Measure how similar two IBase (resource fields) are to one another. 1.0 means identical. 0.0 means completely different.
|
||||||
*/
|
*/
|
||||||
public interface IMdmFieldMatcher {
|
public interface IMdmFieldMatcher {
|
||||||
boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem);
|
/**
|
||||||
|
* Checks if theLeftBase and theRightBase match, returning true if they do
|
||||||
|
* and false otherwise.
|
||||||
|
*/
|
||||||
|
boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if matcher can/will match empty (null) fields,
|
||||||
|
* false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean isMatchingEmptyFields() {
|
||||||
|
// false because most people are overriding this
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,17 +17,37 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher.models;
|
||||||
|
|
||||||
import ca.uhn.fhir.util.StringUtil;
|
/**
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
* Enum for holding all the known FHIR Element matchers that we support in HAPI. The string matchers first
|
||||||
|
* encode the string using an Apache Encoder before comparing them.
|
||||||
|
* https://commons.apache.org/proper/commons-codec/userguide.html
|
||||||
|
*/
|
||||||
|
public enum MatchTypeEnum {
|
||||||
|
|
||||||
|
CAVERPHONE1,
|
||||||
|
CAVERPHONE2,
|
||||||
|
COLOGNE,
|
||||||
|
DOUBLE_METAPHONE,
|
||||||
|
MATCH_RATING_APPROACH,
|
||||||
|
METAPHONE,
|
||||||
|
NYSIIS,
|
||||||
|
REFINED_SOUNDEX,
|
||||||
|
SOUNDEX,
|
||||||
|
NICKNAME,
|
||||||
|
|
||||||
|
STRING,
|
||||||
|
SUBSTRING,
|
||||||
|
|
||||||
|
DATE,
|
||||||
|
NAME_ANY_ORDER,
|
||||||
|
NAME_FIRST_AND_LAST,
|
||||||
|
|
||||||
|
IDENTIFIER,
|
||||||
|
|
||||||
|
EMPTY_FIELD,
|
||||||
|
EXTENSION_ANY_ORDER,
|
||||||
|
NUMERIC;
|
||||||
|
|
||||||
public abstract class BaseHapiStringMetric {
|
|
||||||
protected String extractString(IPrimitiveType<?> thePrimitive, boolean theExact) {
|
|
||||||
String theString = thePrimitive.getValueAsString();
|
|
||||||
if (theExact) {
|
|
||||||
return theString;
|
|
||||||
}
|
|
||||||
return StringUtil.normalizeStringForSearchIndexing(theString);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package ca.uhn.fhir.mdm.rules.matcher.util;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.util.StringUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
public class StringMatcherUtils {
|
||||||
|
public static String extractString(IPrimitiveType<?> thePrimitive, boolean theExact) {
|
||||||
|
String theString = thePrimitive.getValueAsString();
|
||||||
|
if (theExact) {
|
||||||
|
return theString;
|
||||||
|
}
|
||||||
|
return StringUtil.normalizeStringForSearchIndexing(theString);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@
|
||||||
package ca.uhn.fhir.mdm.rules.similarity;
|
package ca.uhn.fhir.mdm.rules.similarity;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.mdm.rules.matcher.BaseHapiStringMetric;
|
import ca.uhn.fhir.mdm.rules.matcher.util.StringMatcherUtils;
|
||||||
import info.debatty.java.stringsimilarity.interfaces.NormalizedStringSimilarity;
|
import info.debatty.java.stringsimilarity.interfaces.NormalizedStringSimilarity;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
@ -28,7 +28,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
/**
|
/**
|
||||||
* Similarity measure for two IBase fields whose similarity can be measured by their String representations.
|
* Similarity measure for two IBase fields whose similarity can be measured by their String representations.
|
||||||
*/
|
*/
|
||||||
public class HapiStringSimilarity extends BaseHapiStringMetric implements IMdmFieldSimilarity {
|
public class HapiStringSimilarity implements IMdmFieldSimilarity {
|
||||||
private final NormalizedStringSimilarity myStringSimilarity;
|
private final NormalizedStringSimilarity myStringSimilarity;
|
||||||
|
|
||||||
public HapiStringSimilarity(NormalizedStringSimilarity theStringSimilarity) {
|
public HapiStringSimilarity(NormalizedStringSimilarity theStringSimilarity) {
|
||||||
|
@ -38,8 +38,8 @@ public class HapiStringSimilarity extends BaseHapiStringMetric implements IMdmFi
|
||||||
@Override
|
@Override
|
||||||
public double similarity(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact) {
|
public double similarity(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact) {
|
||||||
if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) {
|
if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) {
|
||||||
String leftString = extractString((IPrimitiveType<?>) theLeftBase, theExact);
|
String leftString = StringMatcherUtils.extractString((IPrimitiveType<?>) theLeftBase, theExact);
|
||||||
String rightString = extractString((IPrimitiveType<?>) theRightBase, theExact);
|
String rightString = StringMatcherUtils.extractString((IPrimitiveType<?>) theRightBase, theExact);
|
||||||
|
|
||||||
return myStringSimilarity.similarity(leftString, rightString);
|
return myStringSimilarity.similarity(leftString, rightString);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,16 @@ package ca.uhn.fhir.mdm.rules.svc;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.fhirpath.IFhirPath;
|
import ca.uhn.fhir.fhirpath.IFhirPath;
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchEvaluation;
|
import ca.uhn.fhir.mdm.api.MdmMatchEvaluation;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmSimilarityJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.IMatcherFactory;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
@ -48,7 +55,16 @@ public class MdmResourceFieldMatcher {
|
||||||
private final String myName;
|
private final String myName;
|
||||||
private final boolean myIsFhirPathExpression;
|
private final boolean myIsFhirPathExpression;
|
||||||
|
|
||||||
public MdmResourceFieldMatcher(FhirContext theFhirContext, MdmFieldMatchJson theMdmFieldMatchJson, MdmRulesJson theMdmRulesJson) {
|
private final IMatcherFactory myIMatcherFactory;
|
||||||
|
|
||||||
|
public MdmResourceFieldMatcher(
|
||||||
|
FhirContext theFhirContext,
|
||||||
|
IMatcherFactory theIMatcherFactory,
|
||||||
|
MdmFieldMatchJson theMdmFieldMatchJson,
|
||||||
|
MdmRulesJson theMdmRulesJson
|
||||||
|
) {
|
||||||
|
myIMatcherFactory = theIMatcherFactory;
|
||||||
|
|
||||||
myFhirContext = theFhirContext;
|
myFhirContext = theFhirContext;
|
||||||
myMdmFieldMatchJson = theMdmFieldMatchJson;
|
myMdmFieldMatchJson = theMdmFieldMatchJson;
|
||||||
myResourceType = theMdmFieldMatchJson.getResourceType();
|
myResourceType = theMdmFieldMatchJson.getResourceType();
|
||||||
|
@ -70,7 +86,6 @@ public class MdmResourceFieldMatcher {
|
||||||
* @param theRightResource the second {@link IBaseResource}
|
* @param theRightResource the second {@link IBaseResource}
|
||||||
* @return A boolean indicating whether they match.
|
* @return A boolean indicating whether they match.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public MdmMatchEvaluation match(IBaseResource theLeftResource, IBaseResource theRightResource) {
|
public MdmMatchEvaluation match(IBaseResource theLeftResource, IBaseResource theRightResource) {
|
||||||
validate(theLeftResource);
|
validate(theLeftResource);
|
||||||
validate(theRightResource);
|
validate(theRightResource);
|
||||||
|
@ -90,12 +105,12 @@ public class MdmResourceFieldMatcher {
|
||||||
return match(leftValues, rightValues);
|
return match(leftValues, rightValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private MdmMatchEvaluation match(List<IBase> theLeftValues, List<IBase> theRightValues) {
|
private MdmMatchEvaluation match(List<IBase> theLeftValues, List<IBase> theRightValues) {
|
||||||
MdmMatchEvaluation retval = new MdmMatchEvaluation(false, 0.0);
|
MdmMatchEvaluation retval = new MdmMatchEvaluation(false, 0.0);
|
||||||
|
|
||||||
boolean isMatchingEmptyFieldValues = (theLeftValues.isEmpty() && theRightValues.isEmpty());
|
boolean isMatchingEmptyFieldValues = (theLeftValues.isEmpty() && theRightValues.isEmpty());
|
||||||
if (isMatchingEmptyFieldValues && myMdmFieldMatchJson.isMatcherSupportingEmptyFields()) {
|
IMdmFieldMatcher matcher = getFieldMatcher();
|
||||||
|
if (isMatchingEmptyFieldValues && (matcher != null && matcher.isMatchingEmptyFields())) {
|
||||||
return match((IBase) null, (IBase) null);
|
return match((IBase) null, (IBase) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +125,18 @@ public class MdmResourceFieldMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MdmMatchEvaluation match(IBase theLeftValue, IBase theRightValue) {
|
private MdmMatchEvaluation match(IBase theLeftValue, IBase theRightValue) {
|
||||||
return myMdmFieldMatchJson.match(myFhirContext, theLeftValue, theRightValue);
|
IMdmFieldMatcher matcher = getFieldMatcher();
|
||||||
|
if (matcher != null) {
|
||||||
|
boolean isMatches = matcher.matches(theLeftValue, theRightValue, myMdmFieldMatchJson.getMatcher());
|
||||||
|
return new MdmMatchEvaluation(isMatches, isMatches ? 1.0 : 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MdmSimilarityJson similarity = myMdmFieldMatchJson.getSimilarity();
|
||||||
|
if (similarity != null) {
|
||||||
|
return similarity.match(myFhirContext, theLeftValue, theRightValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InternalErrorException(Msg.code(1522) + "Field Match " + myName + " has neither a matcher nor a similarity.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate(IBaseResource theResource) {
|
private void validate(IBaseResource theResource) {
|
||||||
|
@ -136,4 +162,17 @@ public class MdmResourceFieldMatcher {
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return myName;
|
return myName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IMdmFieldMatcher getFieldMatcher() {
|
||||||
|
MdmMatcherJson matcherJson = myMdmFieldMatchJson.getMatcher();
|
||||||
|
MatchTypeEnum matchTypeEnum = null;
|
||||||
|
if (matcherJson != null) {
|
||||||
|
matchTypeEnum = matcherJson.getAlgorithm();
|
||||||
|
}
|
||||||
|
if (matchTypeEnum == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return myIMatcherFactory.getFieldMatcherForMatchType(matchTypeEnum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.IMatcherFactory;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -48,16 +50,18 @@ public class MdmResourceMatcherSvc {
|
||||||
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
||||||
|
|
||||||
private final FhirContext myFhirContext;
|
private final FhirContext myFhirContext;
|
||||||
private MdmRulesJson myMdmRulesJson;
|
private final IMatcherFactory myMatcherFactory;
|
||||||
private final List<MdmResourceFieldMatcher> myFieldMatchers = new ArrayList<>();
|
private final List<MdmResourceFieldMatcher> myFieldMatchers = new ArrayList<>();
|
||||||
|
|
||||||
public MdmResourceMatcherSvc(FhirContext theFhirContext, IMdmSettings theMdmSettings) {
|
private MdmRulesJson myMdmRulesJson;
|
||||||
|
|
||||||
|
public MdmResourceMatcherSvc(
|
||||||
|
FhirContext theFhirContext,
|
||||||
|
IMatcherFactory theIMatcherFactory,
|
||||||
|
IMdmSettings theMdmSettings
|
||||||
|
) {
|
||||||
myFhirContext = theFhirContext;
|
myFhirContext = theFhirContext;
|
||||||
|
myMatcherFactory = theIMatcherFactory;
|
||||||
setMdmSettings(theMdmSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMdmSettings(IMdmSettings theMdmSettings) {
|
|
||||||
myMdmRulesJson = theMdmSettings.getMdmRules();
|
myMdmRulesJson = theMdmSettings.getMdmRules();
|
||||||
|
|
||||||
addFieldMatchers();
|
addFieldMatchers();
|
||||||
|
@ -69,7 +73,7 @@ public class MdmResourceMatcherSvc {
|
||||||
}
|
}
|
||||||
myFieldMatchers.clear();
|
myFieldMatchers.clear();
|
||||||
for (MdmFieldMatchJson matchFieldJson : myMdmRulesJson.getMatchFields()) {
|
for (MdmFieldMatchJson matchFieldJson : myMdmRulesJson.getMatchFields()) {
|
||||||
myFieldMatchers.add(new MdmResourceFieldMatcher( myFhirContext, matchFieldJson, myMdmRulesJson));
|
myFieldMatchers.add(new MdmResourceFieldMatcher(myFhirContext, myMatcherFactory, matchFieldJson, myMdmRulesJson));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,9 +81,8 @@ public class MdmResourceMatcherSvc {
|
||||||
* Given two {@link IBaseResource}s, perform all comparisons on them to determine an {@link MdmMatchResultEnum}, indicating
|
* Given two {@link IBaseResource}s, perform all comparisons on them to determine an {@link MdmMatchResultEnum}, indicating
|
||||||
* to what level the two resources are considered to be matching.
|
* to what level the two resources are considered to be matching.
|
||||||
*
|
*
|
||||||
* @param theLeftResource The first {@link IBaseResource}.
|
* @param theLeftResource The first {@link IBaseResource}.
|
||||||
* @param theRightResource The second {@link IBaseResource}
|
* @param theRightResource The second {@link IBaseResource}
|
||||||
*
|
|
||||||
* @return an {@link MdmMatchResultEnum} indicating the result of the comparison.
|
* @return an {@link MdmMatchResultEnum} indicating the result of the comparison.
|
||||||
*/
|
*/
|
||||||
public MdmMatchOutcome getMatchResult(IBaseResource theLeftResource, IBaseResource theRightResource) {
|
public MdmMatchOutcome getMatchResult(IBaseResource theLeftResource, IBaseResource theRightResource) {
|
||||||
|
@ -91,8 +94,8 @@ public class MdmResourceMatcherSvc {
|
||||||
MdmMatchResultEnum matchResultEnum = myMdmRulesJson.getMatchResult(matchResult.getVector());
|
MdmMatchResultEnum matchResultEnum = myMdmRulesJson.getMatchResult(matchResult.getVector());
|
||||||
matchResult.setMatchResultEnum(matchResultEnum);
|
matchResult.setMatchResultEnum(matchResultEnum);
|
||||||
if (ourLog.isDebugEnabled()) {
|
if (ourLog.isDebugEnabled()) {
|
||||||
ourLog.debug("{} {}: {}", matchResult.getMatchResultEnum(), theRightResource.getIdElement().toUnqualifiedVersionless(), matchResult);
|
ourLog.debug("{} {}: {}", matchResult.getMatchResultEnum(), theRightResource.getIdElement().toUnqualifiedVersionless(), matchResult);
|
||||||
if (ourLog.isTraceEnabled()) {
|
if (ourLog.isTraceEnabled()) {
|
||||||
ourLog.trace("Field matcher results:\n{}", myMdmRulesJson.getDetailedFieldMatchResultWithSuccessInformation(matchResult.getVector()));
|
ourLog.trace("Field matcher results:\n{}", myMdmRulesJson.getDetailedFieldMatchResultWithSuccessInformation(matchResult.getVector()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,11 +108,11 @@ public class MdmResourceMatcherSvc {
|
||||||
* start with a binary representation of the value 0 for long: 0000
|
* start with a binary representation of the value 0 for long: 0000
|
||||||
* first_name matches, so the value `1` is bitwise-ORed to the current value (0) in right-most position.
|
* first_name matches, so the value `1` is bitwise-ORed to the current value (0) in right-most position.
|
||||||
* `0001`
|
* `0001`
|
||||||
*
|
* <p>
|
||||||
* Next, we look at the second field comparator, and see if it matches. If it does, we left-shift 1 by the index
|
* Next, we look at the second field comparator, and see if it matches. If it does, we left-shift 1 by the index
|
||||||
* of the comparator, in this case also 1.
|
* of the comparator, in this case also 1.
|
||||||
* `0010`
|
* `0010`
|
||||||
*
|
* <p>
|
||||||
* Then, we bitwise-or it with the current retval:
|
* Then, we bitwise-or it with the current retval:
|
||||||
* 0001|0010 = 0011
|
* 0001|0010 = 0011
|
||||||
* The binary string is now `0011`, which when you return it as a long becomes `3`.
|
* The binary string is now `0011`, which when you return it as a long becomes `3`.
|
||||||
|
@ -146,11 +149,16 @@ public class MdmResourceMatcherSvc {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isValidResourceType(String theResourceType, String theFieldComparatorType) {
|
||||||
private boolean isValidResourceType(String theResourceType, String theFieldComparatorType) {
|
|
||||||
return (
|
return (
|
||||||
theFieldComparatorType.equalsIgnoreCase(MdmConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE)
|
theFieldComparatorType.equalsIgnoreCase(MdmConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE)
|
||||||
|| theFieldComparatorType.equalsIgnoreCase(theResourceType)
|
|| theFieldComparatorType.equalsIgnoreCase(theResourceType)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setMdmRulesJson(MdmRulesJson theMdmRulesJson) {
|
||||||
|
myMdmRulesJson = theMdmRulesJson;
|
||||||
|
addFieldMatchers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
package ca.uhn.fhir.mdm;
|
package ca.uhn.fhir.mdm;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
|
import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
|
||||||
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
|
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.IMatcherFactory;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherFactory;
|
||||||
import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc;
|
import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc;
|
||||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
@ -20,6 +25,20 @@ public abstract class BaseR4Test {
|
||||||
protected static final FhirContext ourFhirContext = FhirContext.forR4();
|
protected static final FhirContext ourFhirContext = FhirContext.forR4();
|
||||||
protected ISearchParamRegistry mySearchParamRetriever = mock(ISearchParamRegistry.class);
|
protected ISearchParamRegistry mySearchParamRetriever = mock(ISearchParamRegistry.class);
|
||||||
|
|
||||||
|
protected IMatcherFactory myIMatcherFactory;
|
||||||
|
|
||||||
|
protected IMdmSettings myMdmSettings;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
myMdmSettings = mock(IMdmSettings.class);
|
||||||
|
myIMatcherFactory = new MdmMatcherFactory(
|
||||||
|
ourFhirContext,
|
||||||
|
myMdmSettings,
|
||||||
|
new NicknameSvc()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected Patient buildJohn() {
|
protected Patient buildJohn() {
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.addName().addGiven("John");
|
patient.addName().addGiven("John");
|
||||||
|
@ -35,7 +54,10 @@ public abstract class BaseR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MdmResourceMatcherSvc buildMatcher(MdmRulesJson theMdmRulesJson) {
|
protected MdmResourceMatcherSvc buildMatcher(MdmRulesJson theMdmRulesJson) {
|
||||||
return new MdmResourceMatcherSvc(ourFhirContext, new MdmSettings(new MdmRuleValidator(ourFhirContext, mySearchParamRetriever)).setMdmRules(theMdmRulesJson));
|
return new MdmResourceMatcherSvc(ourFhirContext,
|
||||||
|
myIMatcherFactory,
|
||||||
|
new MdmSettings(new MdmRuleValidator(ourFhirContext, mySearchParamRetriever)).setMdmRules(theMdmRulesJson)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertMatch(MdmMatchResultEnum theExpectedMatchEnum, MdmMatchOutcome theMatchResult) {
|
protected void assertMatch(MdmMatchResultEnum theExpectedMatchEnum, MdmMatchOutcome theMatchResult) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package ca.uhn.fhir.mdm.rules.json;
|
package ca.uhn.fhir.mdm.rules.json;
|
||||||
|
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum;
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -27,7 +27,7 @@ public class VectorMatchResultMapTest {
|
||||||
@Test
|
@Test
|
||||||
public void testMatchBeforePossibleMatch() {
|
public void testMatchBeforePossibleMatch() {
|
||||||
MdmRulesJson mdmRulesJson = new MdmRulesJson();
|
MdmRulesJson mdmRulesJson = new MdmRulesJson();
|
||||||
MdmMatcherJson matcherJson = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.STRING);
|
MdmMatcherJson matcherJson = new MdmMatcherJson().setAlgorithm(MatchTypeEnum.STRING);
|
||||||
mdmRulesJson.addMatchField(new MdmFieldMatchJson().setName("given").setResourceType("Patient").setResourcePath("name.given").setMatcher(matcherJson));
|
mdmRulesJson.addMatchField(new MdmFieldMatchJson().setName("given").setResourceType("Patient").setResourcePath("name.given").setMatcher(matcherJson));
|
||||||
mdmRulesJson.addMatchField(new MdmFieldMatchJson().setName("family").setResourceType("Patient").setResourcePath("name.family").setMatcher(matcherJson));
|
mdmRulesJson.addMatchField(new MdmFieldMatchJson().setName("family").setResourceType("Patient").setResourcePath("name.family").setMatcher(matcherJson));
|
||||||
mdmRulesJson.addMatchField(new MdmFieldMatchJson().setName("prefix").setResourceType("Patient").setResourcePath("name.prefix").setMatcher(matcherJson));
|
mdmRulesJson.addMatchField(new MdmFieldMatchJson().setName("prefix").setResourceType("Patient").setResourcePath("name.prefix").setMatcher(matcherJson));
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
|
||||||
public abstract class BaseMatcherR4Test {
|
public abstract class BaseMatcherR4Test {
|
||||||
protected static final FhirContext ourFhirContext = FhirContext.forR4();
|
protected static final FhirContext ourFhirContext = FhirContext.forR4();
|
||||||
|
|
||||||
|
protected MdmMatcherJson myMdmMatcherJson;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
myMdmMatcherJson = new MdmMatcherJson();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.HapiDateMatcher;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
import org.hl7.fhir.r4.model.DateTimeType;
|
import org.hl7.fhir.r4.model.DateTimeType;
|
||||||
import org.hl7.fhir.r4.model.DateType;
|
import org.hl7.fhir.r4.model.DateType;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
@ -14,6 +16,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class DateMatcherR4Test extends BaseMatcherR4Test {
|
public class DateMatcherR4Test extends BaseMatcherR4Test {
|
||||||
|
|
||||||
|
private HapiDateMatcher myDateMatcher;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
super.before();
|
||||||
|
myDateMatcher = new HapiDateMatcher(ourFhirContext);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExactDatePrecision() {
|
public void testExactDatePrecision() {
|
||||||
Calendar cal = new GregorianCalendar(2020, 6, 15);
|
Calendar cal = new GregorianCalendar(2020, 6, 15);
|
||||||
|
@ -43,7 +53,8 @@ public class DateMatcherR4Test extends BaseMatcherR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean dateMatch(Date theDate, Date theSameMonth, TemporalPrecisionEnum theTheDay) {
|
private boolean dateMatch(Date theDate, Date theSameMonth, TemporalPrecisionEnum theTheDay) {
|
||||||
return MdmMatcherEnum.DATE.match(ourFhirContext, new DateType(theDate, theTheDay), new DateType(theSameMonth, theTheDay), true, null);
|
myMdmMatcherJson.setExact(true);
|
||||||
|
return myDateMatcher.matches(new DateType(theDate, theTheDay), new DateType(theSameMonth, theTheDay), myMdmMatcherJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -87,12 +98,11 @@ public class DateMatcherR4Test extends BaseMatcherR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean dateTimeMatch(Date theDate, Date theSecondDate, TemporalPrecisionEnum thePrecision, TemporalPrecisionEnum theSecondPrecision) {
|
private boolean dateTimeMatch(Date theDate, Date theSecondDate, TemporalPrecisionEnum thePrecision, TemporalPrecisionEnum theSecondPrecision) {
|
||||||
return MdmMatcherEnum.DATE.match(
|
myMdmMatcherJson.setExact(true);
|
||||||
ourFhirContext,
|
return myDateMatcher.matches(
|
||||||
new DateTimeType(theDate, thePrecision),
|
new DateTimeType(theDate, thePrecision),
|
||||||
new DateTimeType(theSecondDate, theSecondPrecision),
|
new DateTimeType(theSecondDate, theSecondPrecision),
|
||||||
true,
|
myMdmMatcherJson
|
||||||
null
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package ca.uhn.fhir.mdm.rules.matcher;
|
||||||
|
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.EmptyFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -16,16 +18,16 @@ public class EmptyFieldMatcherTest extends BaseMatcherR4Test {
|
||||||
StringDt leftEmpty = new StringDt("");
|
StringDt leftEmpty = new StringDt("");
|
||||||
StringDt rightEmpty = new StringDt("");
|
StringDt rightEmpty = new StringDt("");
|
||||||
StringDt right = new StringDt("a value");
|
StringDt right = new StringDt("a value");
|
||||||
MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.EMPTY_FIELD);
|
|
||||||
MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher);
|
|
||||||
|
|
||||||
assertTrue(fieldMatch.match(ourFhirContext, null, null).match);
|
EmptyFieldMatcher fieldMatch = new EmptyFieldMatcher();
|
||||||
assertTrue(fieldMatch.match(ourFhirContext, null, rightEmpty).match);
|
|
||||||
assertTrue(fieldMatch.match(ourFhirContext, leftEmpty, null).match);
|
assertTrue(fieldMatch.matches(null, null, null));
|
||||||
assertTrue(fieldMatch.match(ourFhirContext, leftEmpty, rightEmpty).match);
|
assertTrue(fieldMatch.matches(null, rightEmpty, null));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, null, right).match);
|
assertTrue(fieldMatch.matches(leftEmpty, null, null));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, left, null).match);
|
assertTrue(fieldMatch.matches(leftEmpty, rightEmpty, null));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, left, right).match);
|
assertFalse(fieldMatch.matches(null, right, null));
|
||||||
|
assertFalse(fieldMatch.matches(left, null, null));
|
||||||
|
assertFalse(fieldMatch.matches(left, right, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,28 @@
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher;
|
||||||
|
|
||||||
|
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.ExtensionMatcher;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.r4.model.IntegerType;
|
import org.hl7.fhir.r4.model.IntegerType;
|
||||||
import org.hl7.fhir.r4.model.Organization;
|
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class ExtensionMatcherR4Test extends BaseMatcherR4Test {
|
public class ExtensionMatcherR4Test extends BaseMatcherR4Test {
|
||||||
|
|
||||||
|
private ExtensionMatcher myExtensionMatcher;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
super.before();
|
||||||
|
|
||||||
|
myExtensionMatcher = new ExtensionMatcher();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPatientWithMatchingExtension(){
|
public void testPatientWithMatchingExtension(){
|
||||||
Patient patient1 = new Patient();
|
Patient patient1 = new Patient();
|
||||||
|
@ -19,7 +31,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test {
|
||||||
patient1.addExtension("asd",new StringType("Patient1"));
|
patient1.addExtension("asd",new StringType("Patient1"));
|
||||||
patient2.addExtension("asd",new StringType("Patient1"));
|
patient2.addExtension("asd",new StringType("Patient1"));
|
||||||
|
|
||||||
assertTrue(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null));
|
assertTrue(match(patient1, patient2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -30,7 +42,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test {
|
||||||
patient1.addExtension("asd",new StringType("Patient1"));
|
patient1.addExtension("asd",new StringType("Patient1"));
|
||||||
patient2.addExtension("asd",new StringType("Patient2"));
|
patient2.addExtension("asd",new StringType("Patient2"));
|
||||||
|
|
||||||
assertFalse(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null));
|
assertFalse(match(patient1, patient2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -41,7 +53,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test {
|
||||||
patient1.addExtension("asd",new StringType("Patient1"));
|
patient1.addExtension("asd",new StringType("Patient1"));
|
||||||
patient2.addExtension("asd1",new StringType("Patient1"));
|
patient2.addExtension("asd1",new StringType("Patient1"));
|
||||||
|
|
||||||
assertFalse(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null));
|
assertFalse(match(patient1, patient2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -54,7 +66,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test {
|
||||||
patient2.addExtension("asd",new StringType("Patient1"));
|
patient2.addExtension("asd",new StringType("Patient1"));
|
||||||
patient2.addExtension("asdasd", new StringType("some value"));
|
patient2.addExtension("asdasd", new StringType("some value"));
|
||||||
|
|
||||||
assertTrue(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null));
|
assertTrue(match(patient1, patient2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -65,7 +77,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test {
|
||||||
patient1.addExtension("asd", new IntegerType(123));
|
patient1.addExtension("asd", new IntegerType(123));
|
||||||
patient2.addExtension("asd", new IntegerType(123));
|
patient2.addExtension("asd", new IntegerType(123));
|
||||||
|
|
||||||
assertTrue(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null));
|
assertTrue(match(patient1, patient2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -73,7 +85,10 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test {
|
||||||
Patient patient1 = new Patient();
|
Patient patient1 = new Patient();
|
||||||
Patient patient2 = new Patient();
|
Patient patient2 = new Patient();
|
||||||
|
|
||||||
assertFalse(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null));
|
assertFalse(match(patient1, patient2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean match(IBase theFirst, IBase theSecond) {
|
||||||
|
return myExtensionMatcher.matches(theFirst, theSecond, myMdmMatcherJson);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher;
|
||||||
|
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.IdentifierMatcher;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.r4.model.Identifier;
|
import org.hl7.fhir.r4.model.Identifier;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
@ -14,15 +15,20 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test {
|
||||||
private static final String MATCHING_VALUE = "matchme";
|
private static final String MATCHING_VALUE = "matchme";
|
||||||
private static final String OTHER_VALUE = "strange";
|
private static final String OTHER_VALUE = "strange";
|
||||||
|
|
||||||
|
private IdentifierMatcher myIdentifierMatcher;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
super.before();
|
||||||
|
myIdentifierMatcher = new IdentifierMatcher();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIdentifierMatch() {
|
public void testIdentifierMatch() {
|
||||||
Identifier left = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE);
|
Identifier left = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE);
|
||||||
Identifier right = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE);
|
Identifier right = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE);
|
||||||
|
|
||||||
MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER);
|
assertTrue(match(left, right));
|
||||||
MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher);
|
|
||||||
|
|
||||||
assertTrue(fieldMatch.match(ourFhirContext, left, right).match);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -33,17 +39,15 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test {
|
||||||
Identifier rightNoSystem = new Identifier().setValue(MATCHING_VALUE);
|
Identifier rightNoSystem = new Identifier().setValue(MATCHING_VALUE);
|
||||||
Identifier rightNoValue = new Identifier().setSystem(MATCHING_SYSTEM);
|
Identifier rightNoValue = new Identifier().setSystem(MATCHING_SYSTEM);
|
||||||
|
|
||||||
MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER);
|
|
||||||
MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher);
|
|
||||||
|
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, left, rightWrongSystem).match);
|
assertFalse(match(left, rightWrongSystem));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, left, rightWrongValue).match);
|
assertFalse(match(left, rightWrongValue));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, left, rightNoSystem).match);
|
assertFalse(match(left, rightNoSystem));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, left, rightNoValue).match);
|
assertFalse(match(left, rightNoValue));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, rightWrongSystem, left).match);
|
assertFalse(match(rightWrongSystem, left));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, rightWrongValue, left).match);
|
assertFalse(match(rightWrongValue, left));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, rightNoSystem, left).match);
|
assertFalse(match(rightNoSystem, left));
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, rightNoValue, left).match);
|
assertFalse(match(rightNoValue, left));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -51,10 +55,9 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test {
|
||||||
Identifier left = new Identifier().setSystem(MATCHING_SYSTEM);
|
Identifier left = new Identifier().setSystem(MATCHING_SYSTEM);
|
||||||
Identifier right = new Identifier().setSystem(MATCHING_SYSTEM);
|
Identifier right = new Identifier().setSystem(MATCHING_SYSTEM);
|
||||||
|
|
||||||
MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER).setIdentifierSystem(MATCHING_SYSTEM);
|
myMdmMatcherJson.setIdentifierSystem(MATCHING_SYSTEM);
|
||||||
MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher);
|
|
||||||
|
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, left, right).match);
|
assertFalse(match(left, right));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -62,10 +65,9 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test {
|
||||||
Identifier left = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE);
|
Identifier left = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE);
|
||||||
Identifier right = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE);
|
Identifier right = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE);
|
||||||
|
|
||||||
MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER).setIdentifierSystem(MATCHING_SYSTEM);
|
myMdmMatcherJson.setIdentifierSystem(MATCHING_SYSTEM);
|
||||||
MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher);
|
|
||||||
|
|
||||||
assertTrue(fieldMatch.match(ourFhirContext, left, right).match);
|
assertTrue(match(left, right));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -73,9 +75,12 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test {
|
||||||
Identifier left = new Identifier().setSystem(OTHER_SYSTEM).setValue(MATCHING_VALUE);
|
Identifier left = new Identifier().setSystem(OTHER_SYSTEM).setValue(MATCHING_VALUE);
|
||||||
Identifier right = new Identifier().setSystem(OTHER_SYSTEM).setValue(MATCHING_VALUE);
|
Identifier right = new Identifier().setSystem(OTHER_SYSTEM).setValue(MATCHING_VALUE);
|
||||||
|
|
||||||
MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER).setIdentifierSystem(MATCHING_SYSTEM);
|
myMdmMatcherJson.setIdentifierSystem(MATCHING_SYSTEM);
|
||||||
MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher);
|
|
||||||
|
|
||||||
assertFalse(fieldMatch.match(ourFhirContext, left, right).match);
|
assertFalse(match(left, right));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean match(IBase theFirst, IBase theSecond) {
|
||||||
|
return myIdentifierMatcher.matches(theFirst, theSecond, myMdmMatcherJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,48 @@
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.NicknameMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
class NicknameMatcherTest {
|
class NicknameMatcherTest {
|
||||||
IMdmStringMatcher matcher = new NicknameMatcher();
|
IMdmFieldMatcher matcher;
|
||||||
|
|
||||||
|
NicknameSvc myNicknameSvc = new NicknameSvc();
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void begin() {
|
||||||
|
matcher = new NicknameMatcher(myNicknameSvc);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatches() {
|
public void testMatches() {
|
||||||
assertTrue(matcher.matches("Ken", "ken"));
|
Assertions.assertTrue(match("Ken", "ken"));
|
||||||
assertTrue(matcher.matches("ken", "Ken"));
|
Assertions.assertTrue(match("ken", "Ken"));
|
||||||
assertTrue(matcher.matches("Ken", "Ken"));
|
Assertions.assertTrue(match("Ken", "Ken"));
|
||||||
assertTrue(matcher.matches("Kenneth", "Ken"));
|
Assertions.assertTrue(match("Kenneth", "Ken"));
|
||||||
assertTrue(matcher.matches("Kenneth", "Kenny"));
|
Assertions.assertTrue(match("Kenneth", "Kenny"));
|
||||||
assertTrue(matcher.matches("Ken", "Kenneth"));
|
Assertions.assertTrue(match("Ken", "Kenneth"));
|
||||||
assertTrue(matcher.matches("Kenny", "Kenneth"));
|
Assertions.assertTrue(match("Kenny", "Kenneth"));
|
||||||
assertTrue(matcher.matches("Jim", "Jimmy"));
|
Assertions.assertTrue(match("Jim", "Jimmy"));
|
||||||
assertTrue(matcher.matches("Jimmy", "Jim"));
|
Assertions.assertTrue(match("Jimmy", "Jim"));
|
||||||
assertTrue(matcher.matches("Jim", "James"));
|
Assertions.assertTrue(match("Jim", "James"));
|
||||||
assertTrue(matcher.matches("Jimmy", "James"));
|
Assertions.assertTrue(match("Jimmy", "James"));
|
||||||
assertTrue(matcher.matches("James", "Jimmy"));
|
Assertions.assertTrue(match("James", "Jimmy"));
|
||||||
assertTrue(matcher.matches("James", "Jim"));
|
Assertions.assertTrue(match("James", "Jim"));
|
||||||
|
|
||||||
assertFalse(matcher.matches("Ken", "Bob"));
|
Assertions.assertFalse(match("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
|
||||||
assertFalse(matcher.matches("Allen", "Allan"));
|
Assertions.assertFalse(match("Allen", "Allan"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean match(String theFirst, String theSecond) {
|
||||||
|
MdmMatcherJson json = new MdmMatcherJson();
|
||||||
|
json.setExact(true);
|
||||||
|
return matcher.matches(new StringType(theFirst), new StringType(theSecond), json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,106 +1,140 @@
|
||||||
package ca.uhn.fhir.mdm.rules.matcher;
|
package ca.uhn.fhir.mdm.rules.matcher;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.nickname.NicknameSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher;
|
||||||
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.DateType;
|
import org.hl7.fhir.r4.model.DateType;
|
||||||
import org.hl7.fhir.r4.model.Enumeration;
|
import org.hl7.fhir.r4.model.Enumeration;
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
public class StringMatcherR4Test extends BaseMatcherR4Test {
|
public class StringMatcherR4Test extends BaseMatcherR4Test {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(StringMatcherR4Test.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(StringMatcherR4Test.class);
|
||||||
public static final String LEFT_NAME = "namadega";
|
public static final String LEFT_NAME = "namadega";
|
||||||
public static final String RIGHT_NAME = "namaedga";
|
public static final String RIGHT_NAME = "namaedga";
|
||||||
|
|
||||||
|
private IMatcherFactory myIMatcherFactory;
|
||||||
|
|
||||||
|
private IMdmSettings myMdmSettings;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
super.before();
|
||||||
|
|
||||||
|
myMdmSettings = mock(IMdmSettings.class);
|
||||||
|
|
||||||
|
myIMatcherFactory = new MdmMatcherFactory(
|
||||||
|
ourFhirContext,
|
||||||
|
myMdmSettings,
|
||||||
|
new NicknameSvc()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nonnull IMdmFieldMatcher getFieldMatcher(MatchTypeEnum theMatchTypeEnum) {
|
||||||
|
return myIMatcherFactory.getFieldMatcherForMatchType(theMatchTypeEnum);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNamadega() {
|
public void testNamadega() {
|
||||||
String left = LEFT_NAME;
|
String left = LEFT_NAME;
|
||||||
String right = RIGHT_NAME;
|
String right = RIGHT_NAME;
|
||||||
assertTrue(match(MdmMatcherEnum.COLOGNE, left, right));
|
assertTrue(match(MatchTypeEnum.COLOGNE, left, right));
|
||||||
assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, left, right));
|
assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, left, right));
|
||||||
assertTrue(match(MdmMatcherEnum.MATCH_RATING_APPROACH, left, right));
|
assertTrue(match(MatchTypeEnum.MATCH_RATING_APPROACH, left, right));
|
||||||
assertTrue(match(MdmMatcherEnum.METAPHONE, left, right));
|
assertTrue(match(MatchTypeEnum.METAPHONE, left, right));
|
||||||
assertTrue(match(MdmMatcherEnum.SOUNDEX, left, right));
|
assertTrue(match(MatchTypeEnum.SOUNDEX, left, right));
|
||||||
assertTrue(match(MdmMatcherEnum.METAPHONE, left, right));
|
|
||||||
|
|
||||||
assertFalse(match(MdmMatcherEnum.CAVERPHONE1, left, right));
|
assertFalse(match(MatchTypeEnum.CAVERPHONE1, left, right));
|
||||||
assertFalse(match(MdmMatcherEnum.CAVERPHONE2, left, right));
|
assertFalse(match(MatchTypeEnum.CAVERPHONE2, left, right));
|
||||||
assertFalse(match(MdmMatcherEnum.NYSIIS, left, right));
|
assertFalse(match(MatchTypeEnum.NYSIIS, left, right));
|
||||||
assertFalse(match(MdmMatcherEnum.REFINED_SOUNDEX, left, right));
|
assertFalse(match(MatchTypeEnum.REFINED_SOUNDEX, left, right));
|
||||||
assertFalse(match(MdmMatcherEnum.STRING, left, right));
|
assertFalse(match(MatchTypeEnum.STRING, left, right));
|
||||||
assertFalse(match(MdmMatcherEnum.SUBSTRING, left, right));
|
assertFalse(match(MatchTypeEnum.SUBSTRING, left, right));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNumeric() {
|
public void testNumeric() {
|
||||||
assertTrue(match(MdmMatcherEnum.NUMERIC, "4169671111", "(416) 967-1111"));
|
assertTrue(match(MatchTypeEnum.NUMERIC, "4169671111", "(416) 967-1111"));
|
||||||
assertFalse(match(MdmMatcherEnum.NUMERIC, "5169671111", "(416) 967-1111"));
|
assertFalse(match(MatchTypeEnum.NUMERIC, "5169671111", "(416) 967-1111"));
|
||||||
assertFalse(match(MdmMatcherEnum.NUMERIC, "4169671111", "(416) 967-1111x123"));
|
assertFalse(match(MatchTypeEnum.NUMERIC, "4169671111", "(416) 967-1111x123"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMetaphone() {
|
public void testMetaphone() {
|
||||||
assertTrue(match(MdmMatcherEnum.METAPHONE, "Durie", "dury"));
|
assertTrue(match(MatchTypeEnum.METAPHONE, "Durie", "dury"));
|
||||||
assertTrue(match(MdmMatcherEnum.METAPHONE, "Balo", "ballo"));
|
assertTrue(match(MatchTypeEnum.METAPHONE, "Balo", "ballo"));
|
||||||
assertTrue(match(MdmMatcherEnum.METAPHONE, "Hans Peter", "Hanspeter"));
|
assertTrue(match(MatchTypeEnum.METAPHONE, "Hans Peter", "Hanspeter"));
|
||||||
assertTrue(match(MdmMatcherEnum.METAPHONE, "Lawson", "Law son"));
|
assertTrue(match(MatchTypeEnum.METAPHONE, "Lawson", "Law son"));
|
||||||
|
|
||||||
assertFalse(match(MdmMatcherEnum.METAPHONE, "Allsop", "Allsob"));
|
assertFalse(match(MatchTypeEnum.METAPHONE, "Allsop", "Allsob"));
|
||||||
assertFalse(match(MdmMatcherEnum.METAPHONE, "Gevne", "Geve"));
|
assertFalse(match(MatchTypeEnum.METAPHONE, "Gevne", "Geve"));
|
||||||
assertFalse(match(MdmMatcherEnum.METAPHONE, "Bruce", "Bruch"));
|
assertFalse(match(MatchTypeEnum.METAPHONE, "Bruce", "Bruch"));
|
||||||
assertFalse(match(MdmMatcherEnum.METAPHONE, "Smith", "Schmidt"));
|
assertFalse(match(MatchTypeEnum.METAPHONE, "Smith", "Schmidt"));
|
||||||
assertFalse(match(MdmMatcherEnum.METAPHONE, "Jyothi", "Jyoti"));
|
assertFalse(match(MatchTypeEnum.METAPHONE, "Jyothi", "Jyoti"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDoubleMetaphone() {
|
public void testDoubleMetaphone() {
|
||||||
assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Durie", "dury"));
|
assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Durie", "dury"));
|
||||||
assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Balo", "ballo"));
|
assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Balo", "ballo"));
|
||||||
assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Hans Peter", "Hanspeter"));
|
assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Hans Peter", "Hanspeter"));
|
||||||
assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Lawson", "Law son"));
|
assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Lawson", "Law son"));
|
||||||
assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Allsop", "Allsob"));
|
assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Allsop", "Allsob"));
|
||||||
|
|
||||||
assertFalse(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Gevne", "Geve"));
|
assertFalse(match(MatchTypeEnum.DOUBLE_METAPHONE, "Gevne", "Geve"));
|
||||||
assertFalse(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Bruce", "Bruch"));
|
assertFalse(match(MatchTypeEnum.DOUBLE_METAPHONE, "Bruce", "Bruch"));
|
||||||
assertFalse(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Smith", "Schmidt"));
|
assertFalse(match(MatchTypeEnum.DOUBLE_METAPHONE, "Smith", "Schmidt"));
|
||||||
assertFalse(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Jyothi", "Jyoti"));
|
assertFalse(match(MatchTypeEnum.DOUBLE_METAPHONE, "Jyothi", "Jyoti"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNormalizeCase() {
|
public void testNormalizeCase() {
|
||||||
assertTrue(match(MdmMatcherEnum.STRING, "joe", "JoE"));
|
assertTrue(match(MatchTypeEnum.STRING, "joe", "JoE"));
|
||||||
assertTrue(match(MdmMatcherEnum.STRING, "MCTAVISH", "McTavish"));
|
assertTrue(match(MatchTypeEnum.STRING, "MCTAVISH", "McTavish"));
|
||||||
|
|
||||||
assertFalse(match(MdmMatcherEnum.STRING, "joey", "joe"));
|
assertFalse(match(MatchTypeEnum.STRING, "joey", "joe"));
|
||||||
assertFalse(match(MdmMatcherEnum.STRING, "joe", "joey"));
|
assertFalse(match(MatchTypeEnum.STRING, "joe", "joey"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExactString() {
|
public void testExactString() {
|
||||||
assertTrue(MdmMatcherEnum.STRING.match(ourFhirContext, new StringType("Jilly"), new StringType("Jilly"), true, null));
|
myMdmMatcherJson.setExact(true);
|
||||||
|
|
||||||
assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new StringType("MCTAVISH"), new StringType("McTavish"), true, null));
|
assertTrue(getFieldMatcher(MatchTypeEnum.STRING).matches(new StringType("Jilly"), new StringType("Jilly"), myMdmMatcherJson));
|
||||||
assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new StringType("Durie"), new StringType("dury"), true, null));
|
|
||||||
|
assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(new StringType("MCTAVISH"), new StringType("McTavish"), myMdmMatcherJson));
|
||||||
|
assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(new StringType("Durie"), new StringType("dury"), myMdmMatcherJson));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExactBoolean() {
|
public void testExactBoolean() {
|
||||||
assertTrue(MdmMatcherEnum.STRING.match(ourFhirContext, new BooleanType(true), new BooleanType(true), true, null));
|
myMdmMatcherJson.setExact(true);
|
||||||
|
|
||||||
assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new BooleanType(true), new BooleanType(false), true, null));
|
assertTrue(getFieldMatcher(MatchTypeEnum.STRING).matches(new BooleanType(true), new BooleanType(true), myMdmMatcherJson));
|
||||||
assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new BooleanType(false), new BooleanType(true), true, null));
|
|
||||||
|
assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(new BooleanType(true), new BooleanType(false), myMdmMatcherJson));
|
||||||
|
assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(new BooleanType(false), new BooleanType(true), myMdmMatcherJson));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExactDateString() {
|
public void testExactDateString() {
|
||||||
assertTrue(MdmMatcherEnum.STRING.match(ourFhirContext, new DateType("1965-08-09"), new DateType("1965-08-09"), true, null));
|
myMdmMatcherJson.setExact(true);
|
||||||
|
|
||||||
assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new DateType("1965-08-09"), new DateType("1965-09-08"), true, null));
|
assertTrue(getFieldMatcher(MatchTypeEnum.STRING).matches(new DateType("1965-08-09"), new DateType("1965-08-09"), myMdmMatcherJson));
|
||||||
|
|
||||||
|
assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(new DateType("1965-08-09"), new DateType("1965-09-08"), myMdmMatcherJson));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,52 +146,55 @@ public class StringMatcherR4Test extends BaseMatcherR4Test {
|
||||||
Enumeration<Enumerations.AdministrativeGender> female = new Enumeration<Enumerations.AdministrativeGender>(new Enumerations.AdministrativeGenderEnumFactory());
|
Enumeration<Enumerations.AdministrativeGender> female = new Enumeration<Enumerations.AdministrativeGender>(new Enumerations.AdministrativeGenderEnumFactory());
|
||||||
female.setValue(Enumerations.AdministrativeGender.FEMALE);
|
female.setValue(Enumerations.AdministrativeGender.FEMALE);
|
||||||
|
|
||||||
assertTrue(MdmMatcherEnum.STRING.match(ourFhirContext, male, male, true, null));
|
myMdmMatcherJson.setExact(true);
|
||||||
|
|
||||||
assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, male, female, true, null));
|
assertTrue(getFieldMatcher(MatchTypeEnum.STRING).matches(male, male, myMdmMatcherJson));
|
||||||
|
|
||||||
|
assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(male, female, myMdmMatcherJson));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSoundex() {
|
public void testSoundex() {
|
||||||
assertTrue(match(MdmMatcherEnum.SOUNDEX, "Gail", "Gale"));
|
assertTrue(match(MatchTypeEnum.SOUNDEX, "Gail", "Gale"));
|
||||||
assertTrue(match(MdmMatcherEnum.SOUNDEX, "John", "Jon"));
|
assertTrue(match(MatchTypeEnum.SOUNDEX, "John", "Jon"));
|
||||||
assertTrue(match(MdmMatcherEnum.SOUNDEX, "Thom", "Tom"));
|
assertTrue(match(MatchTypeEnum.SOUNDEX, "Thom", "Tom"));
|
||||||
|
|
||||||
assertFalse(match(MdmMatcherEnum.SOUNDEX, "Fred", "Frank"));
|
assertFalse(match(MatchTypeEnum.SOUNDEX, "Fred", "Frank"));
|
||||||
assertFalse(match(MdmMatcherEnum.SOUNDEX, "Thomas", "Tom"));
|
assertFalse(match(MatchTypeEnum.SOUNDEX, "Thomas", "Tom"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCaverphone1() {
|
public void testCaverphone1() {
|
||||||
assertTrue(match(MdmMatcherEnum.CAVERPHONE1, "Gail", "Gael"));
|
assertTrue(match(MatchTypeEnum.CAVERPHONE1, "Gail", "Gael"));
|
||||||
assertTrue(match(MdmMatcherEnum.CAVERPHONE1, "John", "Jon"));
|
assertTrue(match(MatchTypeEnum.CAVERPHONE1, "John", "Jon"));
|
||||||
|
|
||||||
assertFalse(match(MdmMatcherEnum.CAVERPHONE1, "Gail", "Gale"));
|
assertFalse(match(MatchTypeEnum.CAVERPHONE1, "Gail", "Gale"));
|
||||||
assertFalse(match(MdmMatcherEnum.CAVERPHONE1, "Fred", "Frank"));
|
assertFalse(match(MatchTypeEnum.CAVERPHONE1, "Fred", "Frank"));
|
||||||
assertFalse(match(MdmMatcherEnum.CAVERPHONE1, "Thomas", "Tom"));
|
assertFalse(match(MatchTypeEnum.CAVERPHONE1, "Thomas", "Tom"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCaverphone2() {
|
public void testCaverphone2() {
|
||||||
assertTrue(match(MdmMatcherEnum.CAVERPHONE2, "Gail", "Gael"));
|
assertTrue(match(MatchTypeEnum.CAVERPHONE2, "Gail", "Gael"));
|
||||||
assertTrue(match(MdmMatcherEnum.CAVERPHONE2, "John", "Jon"));
|
assertTrue(match(MatchTypeEnum.CAVERPHONE2, "John", "Jon"));
|
||||||
assertTrue(match(MdmMatcherEnum.CAVERPHONE2, "Gail", "Gale"));
|
assertTrue(match(MatchTypeEnum.CAVERPHONE2, "Gail", "Gale"));
|
||||||
|
|
||||||
assertFalse(match(MdmMatcherEnum.CAVERPHONE2, "Fred", "Frank"));
|
assertFalse(match(MatchTypeEnum.CAVERPHONE2, "Fred", "Frank"));
|
||||||
assertFalse(match(MdmMatcherEnum.CAVERPHONE2, "Thomas", "Tom"));
|
assertFalse(match(MatchTypeEnum.CAVERPHONE2, "Thomas", "Tom"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNormalizeSubstring() {
|
public void testNormalizeSubstring() {
|
||||||
assertTrue(match(MdmMatcherEnum.SUBSTRING, "BILLY", "Bill"));
|
assertTrue(match(MatchTypeEnum.SUBSTRING, "BILLY", "Bill"));
|
||||||
assertTrue(match(MdmMatcherEnum.SUBSTRING, "Bill", "Billy"));
|
assertTrue(match(MatchTypeEnum.SUBSTRING, "Bill", "Billy"));
|
||||||
assertTrue(match(MdmMatcherEnum.SUBSTRING, "FRED", "Frederik"));
|
assertTrue(match(MatchTypeEnum.SUBSTRING, "FRED", "Frederik"));
|
||||||
|
|
||||||
assertFalse(match(MdmMatcherEnum.SUBSTRING, "Fred", "Friederik"));
|
assertFalse(match(MatchTypeEnum.SUBSTRING, "Fred", "Friederik"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean match(MdmMatcherEnum theMatcher, String theLeft, String theRight) {
|
private boolean match(MatchTypeEnum theMatcher, String theLeft, String theRight) {
|
||||||
return theMatcher.match(ourFhirContext, new StringType(theLeft), new StringType(theRight), false, null);
|
return getFieldMatcher(theMatcher)
|
||||||
|
.matches(new StringType(theLeft), new StringType(theRight), myMdmMatcherJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ public abstract class BaseMdmRulesR4Test extends BaseR4Test {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
|
super.before();
|
||||||
myMdmRulesJson = new MdmRulesJson();
|
myMdmRulesJson = new MdmRulesJson();
|
||||||
|
|
||||||
ArrayList<String> myLegalMdmTypes = new ArrayList<>();
|
ArrayList<String> myLegalMdmTypes = new ArrayList<>();
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum;
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
import org.hl7.fhir.r4.model.HumanName;
|
import org.hl7.fhir.r4.model.HumanName;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
@ -20,7 +20,7 @@ import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class CustomResourceMatcherR4Test extends BaseR4Test {
|
public class CustomResourceMatcherR4Test extends BaseR4Test {
|
||||||
|
|
||||||
public static final String FIELD_EXACT_MATCH_NAME = MdmMatcherEnum.NAME_ANY_ORDER.name();
|
public static final String FIELD_EXACT_MATCH_NAME = MatchTypeEnum.NAME_ANY_ORDER.name();
|
||||||
private static Patient ourJohnHenry;
|
private static Patient ourJohnHenry;
|
||||||
private static Patient ourJohnHENRY;
|
private static Patient ourJohnHENRY;
|
||||||
private static Patient ourJaneHenry;
|
private static Patient ourJaneHenry;
|
||||||
|
@ -32,6 +32,7 @@ public class CustomResourceMatcherR4Test extends BaseR4Test {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
|
super.before();
|
||||||
when(mySearchParamRetriever.getActiveSearchParam("Patient", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
when(mySearchParamRetriever.getActiveSearchParam("Patient", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
||||||
when(mySearchParamRetriever.getActiveSearchParam("Practitioner", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
when(mySearchParamRetriever.getActiveSearchParam("Practitioner", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
||||||
when(mySearchParamRetriever.getActiveSearchParam("Medication", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
when(mySearchParamRetriever.getActiveSearchParam("Medication", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
||||||
|
@ -40,7 +41,8 @@ public class CustomResourceMatcherR4Test extends BaseR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExactNameAnyOrder() {
|
public void testExactNameAnyOrder() {
|
||||||
MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MdmMatcherEnum.NAME_ANY_ORDER, true));
|
MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MatchTypeEnum.NAME_ANY_ORDER, true));
|
||||||
|
|
||||||
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
||||||
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn));
|
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn));
|
||||||
assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJOHN));
|
assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJOHN));
|
||||||
|
@ -53,7 +55,8 @@ public class CustomResourceMatcherR4Test extends BaseR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNormalizedNameAnyOrder() {
|
public void testNormalizedNameAnyOrder() {
|
||||||
MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MdmMatcherEnum.NAME_ANY_ORDER, false));
|
MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MatchTypeEnum.NAME_ANY_ORDER, false));
|
||||||
|
|
||||||
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
||||||
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn));
|
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn));
|
||||||
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJOHN));
|
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJOHN));
|
||||||
|
@ -66,7 +69,8 @@ public class CustomResourceMatcherR4Test extends BaseR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExactNameFirstAndLast() {
|
public void testExactNameFirstAndLast() {
|
||||||
MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MdmMatcherEnum.NAME_FIRST_AND_LAST, true));
|
MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MatchTypeEnum.NAME_FIRST_AND_LAST, true));
|
||||||
|
|
||||||
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
||||||
assertMatchResult(MdmMatchResultEnum.MATCH, 1L, 1.0, false, false, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
assertMatchResult(MdmMatchResultEnum.MATCH, 1L, 1.0, false, false, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
||||||
assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn));
|
assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn));
|
||||||
|
@ -80,7 +84,8 @@ public class CustomResourceMatcherR4Test extends BaseR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNormalizedNameFirstAndLast() {
|
public void testNormalizedNameFirstAndLast() {
|
||||||
MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MdmMatcherEnum.NAME_FIRST_AND_LAST, false));
|
MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MatchTypeEnum.NAME_FIRST_AND_LAST, false));
|
||||||
|
|
||||||
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry));
|
||||||
assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn));
|
assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn));
|
||||||
assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJOHN));
|
assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJOHN));
|
||||||
|
@ -91,7 +96,7 @@ public class CustomResourceMatcherR4Test extends BaseR4Test {
|
||||||
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourBillyJohnHenry));
|
assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourBillyJohnHenry));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MdmRulesJson buildNameRules(MdmMatcherEnum theAlgorithm, boolean theExact) {
|
private MdmRulesJson buildNameRules(MatchTypeEnum theAlgorithm, boolean theExact) {
|
||||||
MdmMatcherJson matcherJson = new MdmMatcherJson().setAlgorithm(theAlgorithm).setExact(theExact);
|
MdmMatcherJson matcherJson = new MdmMatcherJson().setAlgorithm(theAlgorithm).setExact(theExact);
|
||||||
MdmFieldMatchJson nameAnyOrderFieldMatch = new MdmFieldMatchJson()
|
MdmFieldMatchJson nameAnyOrderFieldMatch = new MdmFieldMatchJson()
|
||||||
.setName(FIELD_EXACT_MATCH_NAME)
|
.setName(FIELD_EXACT_MATCH_NAME)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum;
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
import org.hl7.fhir.r4.model.HumanName;
|
import org.hl7.fhir.r4.model.HumanName;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -46,10 +46,9 @@ public class FhirPathResourceMatcherR4Test extends BaseMdmRulesR4Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFhirPathOrderedMatches() {
|
public void testFhirPathOrderedMatches() {
|
||||||
MdmResourceMatcherSvc matcherSvc = buildMatcher(buildOrderedGivenNameRules(MdmMatcherEnum.STRING));
|
MdmResourceMatcherSvc matcherSvc = buildMatcher(buildOrderedGivenNameRules(MatchTypeEnum.STRING));
|
||||||
|
|
||||||
myLeft = new Patient();
|
myLeft = new Patient();
|
||||||
HumanName name = myLeft.addName();
|
HumanName name = myLeft.addName();
|
||||||
|
@ -63,6 +62,7 @@ public class FhirPathResourceMatcherR4Test extends BaseMdmRulesR4Test {
|
||||||
name2.addGiven("Gary");
|
name2.addGiven("Gary");
|
||||||
myRight.setId("Patient/2");
|
myRight.setId("Patient/2");
|
||||||
|
|
||||||
|
// test
|
||||||
MdmMatchOutcome result = matcherSvc.match(myLeft, myRight);
|
MdmMatchOutcome result = matcherSvc.match(myLeft, myRight);
|
||||||
assertMatchResult(MdmMatchResultEnum.NO_MATCH, 0L, 0.0, false, false, result);
|
assertMatchResult(MdmMatchResultEnum.NO_MATCH, 0L, 0.0, false, false, result);
|
||||||
|
|
||||||
|
@ -85,12 +85,12 @@ public class FhirPathResourceMatcherR4Test extends BaseMdmRulesR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStringMatchResult() {
|
public void testStringMatchResult() {
|
||||||
MdmResourceMatcherSvc matcherSvc = buildMatcher(buildOrderedGivenNameRules(MdmMatcherEnum.STRING));
|
MdmResourceMatcherSvc matcherSvc = buildMatcher(buildOrderedGivenNameRules(MatchTypeEnum.STRING));
|
||||||
MdmMatchOutcome result = matcherSvc.match(myLeft, myRight);
|
MdmMatchOutcome result = matcherSvc.match(myLeft, myRight);
|
||||||
assertMatchResult(MdmMatchResultEnum.NO_MATCH, 0L, 0.0, false, false, result);
|
assertMatchResult(MdmMatchResultEnum.NO_MATCH, 0L, 0.0, false, false, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MdmRulesJson buildOrderedGivenNameRules(MdmMatcherEnum theMatcherEnum) {
|
protected MdmRulesJson buildOrderedGivenNameRules(MatchTypeEnum theMatcherEnum) {
|
||||||
MdmFieldMatchJson firstGivenNameMatchField = new MdmFieldMatchJson()
|
MdmFieldMatchJson firstGivenNameMatchField = new MdmFieldMatchJson()
|
||||||
.setName(PATIENT_GIVEN_FIRST)
|
.setName(PATIENT_GIVEN_FIRST)
|
||||||
.setResourceType("Patient")
|
.setResourceType("Patient")
|
||||||
|
|
|
@ -3,10 +3,8 @@ package ca.uhn.fhir.mdm.rules.svc;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmSimilarityJson;
|
import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.EmptyFieldMatcher;
|
||||||
import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum;
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
import ca.uhn.fhir.mdm.rules.similarity.MdmSimilarityEnum;
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
|
||||||
import org.hl7.fhir.r4.model.Encounter;
|
import org.hl7.fhir.r4.model.Encounter;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -14,12 +12,12 @@ import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.core.StringStartsWith.startsWith;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class MdmResourceFieldMatcherR4Test extends BaseMdmRulesR4Test {
|
public class MdmResourceFieldMatcherR4Test extends BaseMdmRulesR4Test {
|
||||||
protected MdmResourceFieldMatcher myComparator;
|
protected MdmResourceFieldMatcher myComparator;
|
||||||
|
@ -30,7 +28,13 @@ public class MdmResourceFieldMatcherR4Test extends BaseMdmRulesR4Test {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
super.before();
|
super.before();
|
||||||
myComparator = new MdmResourceFieldMatcher(ourFhirContext, myGivenNameMatchField, myMdmRulesJson);
|
|
||||||
|
myComparator = new MdmResourceFieldMatcher(
|
||||||
|
ourFhirContext,
|
||||||
|
myIMatcherFactory,
|
||||||
|
myGivenNameMatchField,
|
||||||
|
myMdmRulesJson
|
||||||
|
);
|
||||||
myJohn = buildJohn();
|
myJohn = buildJohn();
|
||||||
myJohny = buildJohny();
|
myJohny = buildJohny();
|
||||||
}
|
}
|
||||||
|
@ -44,8 +48,13 @@ public class MdmResourceFieldMatcherR4Test extends BaseMdmRulesR4Test {
|
||||||
.setName("empty-given")
|
.setName("empty-given")
|
||||||
.setResourceType("Patient")
|
.setResourceType("Patient")
|
||||||
.setResourcePath("name.given")
|
.setResourcePath("name.given")
|
||||||
.setMatcher(new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.EMPTY_FIELD));
|
.setMatcher(new MdmMatcherJson().setAlgorithm(MatchTypeEnum.EMPTY_FIELD));
|
||||||
myComparator = new MdmResourceFieldMatcher(ourFhirContext, myGivenNameMatchField, myMdmRulesJson);
|
myComparator = new MdmResourceFieldMatcher(
|
||||||
|
ourFhirContext,
|
||||||
|
myIMatcherFactory,
|
||||||
|
myGivenNameMatchField,
|
||||||
|
myMdmRulesJson
|
||||||
|
);
|
||||||
|
|
||||||
assertFalse(myComparator.match(myJohn, myJohny).match);
|
assertFalse(myComparator.match(myJohn, myJohny).match);
|
||||||
|
|
||||||
|
@ -90,6 +99,9 @@ public class MdmResourceFieldMatcherR4Test extends BaseMdmRulesR4Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - what is this supposed to test?
|
||||||
|
// it relies on matcher being null (is this a reasonable assumption?)
|
||||||
|
// and falls through to similarity check
|
||||||
@Test
|
@Test
|
||||||
public void testMatch() {
|
public void testMatch() {
|
||||||
assertTrue(myComparator.match(myJohn, myJohny).match);
|
assertTrue(myComparator.match(myJohn, myJohny).match);
|
||||||
|
|
|
@ -36,7 +36,6 @@ public class MdmResourceMatcherSvcR4Test extends BaseMdmRulesR4Test {
|
||||||
public void testCompareFirstNameMatch() {
|
public void testCompareFirstNameMatch() {
|
||||||
MdmMatchOutcome result = myMdmResourceMatcherSvc.match(myJohn, myJohny);
|
MdmMatchOutcome result = myMdmResourceMatcherSvc.match(myJohn, myJohny);
|
||||||
assertMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH, 1L, 0.816, false, false, result);
|
assertMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH, 1L, 0.816, false, false, result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson;
|
||||||
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum;
|
import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum;
|
||||||
import org.hl7.fhir.r4.model.HumanName;
|
import org.hl7.fhir.r4.model.HumanName;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -55,19 +55,21 @@ public class ResourceMatcherR4Test extends BaseMdmRulesR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMetaphoneMatchResult() {
|
public void testMetaphoneMatchResult() {
|
||||||
MdmResourceMatcherSvc matcherSvc = buildMatcher(buildNamePhoneRules(MdmMatcherEnum.METAPHONE));
|
MdmResourceMatcherSvc matcherSvc = buildMatcher(buildNamePhoneRules(MatchTypeEnum.METAPHONE));
|
||||||
MdmMatchOutcome result = matcherSvc.match(myLeft, myRight);
|
|
||||||
assertMatchResult(MdmMatchResultEnum.MATCH, 7L, 3.0, false, false, result);
|
MdmMatchOutcome result = matcherSvc.match(myLeft, myRight);
|
||||||
|
assertMatchResult(MdmMatchResultEnum.MATCH, 7L, 3.0, false, false, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStringMatchResult() {
|
public void testStringMatchResult() {
|
||||||
MdmResourceMatcherSvc matcherSvc = buildMatcher(buildNamePhoneRules(MdmMatcherEnum.STRING));
|
MdmResourceMatcherSvc matcherSvc = buildMatcher(buildNamePhoneRules(MatchTypeEnum.STRING));
|
||||||
|
|
||||||
MdmMatchOutcome result = matcherSvc.match(myLeft, myRight);
|
MdmMatchOutcome result = matcherSvc.match(myLeft, myRight);
|
||||||
assertMatchResult(MdmMatchResultEnum.NO_MATCH, 5L, 2.0, false, false, result);
|
assertMatchResult(MdmMatchResultEnum.NO_MATCH, 5L, 2.0, false, false, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MdmRulesJson buildNamePhoneRules(MdmMatcherEnum theMatcherEnum) {
|
protected MdmRulesJson buildNamePhoneRules(MatchTypeEnum theMatcherEnum) {
|
||||||
MdmFieldMatchJson lastNameMatchField = new MdmFieldMatchJson()
|
MdmFieldMatchJson lastNameMatchField = new MdmFieldMatchJson()
|
||||||
.setName(PATIENT_FAMILY)
|
.setName(PATIENT_FAMILY)
|
||||||
.setResourceType("Patient")
|
.setResourceType("Patient")
|
||||||
|
@ -84,7 +86,7 @@ public class ResourceMatcherR4Test extends BaseMdmRulesR4Test {
|
||||||
.setName(PATIENT_PHONE)
|
.setName(PATIENT_PHONE)
|
||||||
.setResourceType("Patient")
|
.setResourceType("Patient")
|
||||||
.setResourcePath("telecom.value")
|
.setResourcePath("telecom.value")
|
||||||
.setMatcher(new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.STRING));
|
.setMatcher(new MdmMatcherJson().setAlgorithm(MatchTypeEnum.STRING));
|
||||||
|
|
||||||
MdmRulesJson retval = new MdmRulesJson();
|
MdmRulesJson retval = new MdmRulesJson();
|
||||||
retval.setVersion("test version");
|
retval.setVersion("test version");
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
|
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.7.3-SNAPSHOT</version>
|
<version>6.7.6-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue