diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4917-refactoring-matchers.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4917-refactoring-matchers.yaml deleted file mode 100644 index 7d0fb27a49c..00000000000 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4917-refactoring-matchers.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -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. - " diff --git a/hapi-fhir-jpa/src/main/resources/.gitkeep b/hapi-fhir-jpa/src/main/resources/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/hapi-fhir-jpa/src/test/java/ca/uhn/fhir/jpa/.gitkeep b/hapi-fhir-jpa/src/test/java/ca/uhn/fhir/jpa/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index d3868db6f81..2678c339d5b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -95,8 +95,8 @@ import ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl; import ca.uhn.fhir.jpa.partition.PartitionManagementProvider; import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc; import ca.uhn.fhir.jpa.provider.DiffProvider; -import ca.uhn.fhir.jpa.provider.InstanceReindexProvider; import ca.uhn.fhir.jpa.provider.ProcessMessageProvider; +import ca.uhn.fhir.jpa.provider.InstanceReindexProvider; import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; @@ -149,6 +149,7 @@ import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.config.SearchParamConfig; 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.sp.ISearchParamPresenceSvc; import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl; @@ -197,6 +198,7 @@ import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import javax.annotation.Nullable; +import java.io.IOException; import java.util.Date; @Configuration @@ -770,6 +772,12 @@ public class JpaConfig { return new UnknownCodeSystemWarningValidationSupport(theFhirContext); } + @Lazy + @Bean + public NicknameInterceptor nicknameInterceptor() throws IOException { + return new NicknameInterceptor(); + } + @Bean public ISynchronousSearchSvc synchronousSearchSvc() { return new SynchronousSearchSvcImpl(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java index e5b74026fe5..cfb343f9f41 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java @@ -20,11 +20,13 @@ package ca.uhn.fhir.jpa.dao.expunge; 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.stereotype.Service; import javax.annotation.Nonnull; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; @Service diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmCommonConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmCommonConfig.java index a39afe5c2dd..9f9d90aad92 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmCommonConfig.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmCommonConfig.java @@ -20,21 +20,14 @@ package ca.uhn.fhir.jpa.mdm.config; 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.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.rest.server.util.ISearchParamRegistry; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Lazy; -@Import(NicknameServiceConfig.class) @Configuration public class MdmCommonConfig { @Bean @@ -51,17 +44,4 @@ public class MdmCommonConfig { MdmLinkDeleteSvc mdmLinkDeleteSvc() { return new MdmLinkDeleteSvc(); } - - @Bean - public IMatcherFactory matcherFactory( - FhirContext theFhirContext, - IMdmSettings theSettings, - NicknameSvc theNicknameSvc - ) { - return new MdmMatcherFactory( - theFhirContext, - theSettings, - theNicknameSvc - ); - } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java index 1593d5c27a4..4007b24ed7d 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java @@ -48,7 +48,6 @@ 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.MdmCandidateSearchSvc; 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.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory; @@ -206,12 +205,8 @@ public class MdmConsumerConfig { } @Bean - MdmResourceMatcherSvc mdmResourceComparatorSvc( - FhirContext theFhirContext, - IMatcherFactory theIMatcherFactory, - IMdmSettings theMdmSettings - ) { - return new MdmResourceMatcherSvc(theFhirContext, theIMatcherFactory, theMdmSettings); + MdmResourceMatcherSvc mdmResourceComparatorSvc(FhirContext theFhirContext, IMdmSettings theMdmSettings) { + return new MdmResourceMatcherSvc(theFhirContext, theMdmSettings); } @Bean diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java index 7ee9b49dd1f..a20df1252a8 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java @@ -76,12 +76,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.slf4j.LoggerFactory.getLogger; @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 { protected static final String PARTITION_1 = "PART-1"; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java index 3b8cb72dc10..526ca53fb6d 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java @@ -7,7 +7,6 @@ import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; import ca.uhn.fhir.mdm.provider.MdmControllerHelper; import ca.uhn.fhir.mdm.provider.MdmProviderDstu3Plus; import ca.uhn.fhir.mdm.rules.config.MdmSettings; -import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc; import ca.uhn.fhir.mdm.util.MessageHelper; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -35,8 +34,6 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test { @Autowired protected MdmSettings myMdmSettings; @Autowired - protected MdmResourceMatcherSvc myMdmResourceMatcherSvc; - @Autowired private MdmControllerHelper myMdmHelper; @Autowired Batch2JobHelper myBatch2JobHelper; @@ -51,7 +48,7 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test { String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8); myMdmSettings.setEnabled(true); myMdmSettings.setScriptText(json); - myMdmResourceMatcherSvc.setMdmRulesJson(myMdmSettings.getMdmRules()); + myMdmResourceMatcherSvc.setMdmSettings(myMdmSettings); } @BeforeEach @@ -65,7 +62,7 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test { public void after() throws IOException { super.after(); myMdmSettings.setScriptText(defaultScript); - myMdmResourceMatcherSvc.setMdmRulesJson(myMdmSettings.getMdmRules()); + myMdmResourceMatcherSvc.setMdmSettings(myMdmSettings); } protected void clearMdmLinks() { diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java index 8afd790406a..eb6dcfd6847 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java @@ -4,7 +4,6 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc; 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.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.nickname.NicknameInterceptor; @@ -19,6 +18,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -37,15 +37,11 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test { @Autowired MatchUrlService myMatchUrlService; - @Autowired - NicknameSvc myNicknameSvc; - private NicknameInterceptor myNicknameInterceptor; @BeforeEach - public void before() throws Exception { - super.before(); - myNicknameInterceptor = new NicknameInterceptor(myNicknameSvc); + public void before() throws IOException { + myNicknameInterceptor = new NicknameInterceptor(); myInterceptorRegistry.registerInterceptor(myNicknameInterceptor); } diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index b0c0a166de6..df64ae24d8d 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -165,6 +165,8 @@ test + + diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java deleted file mode 100644 index cd4b41f78c3..00000000000 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -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(); - } -} diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java index 23031121380..fa2b85d238f 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java @@ -47,13 +47,9 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Scope; -@Import({ - NicknameServiceConfig.class -}) @Configuration public class SearchParamConfig { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java index 23d9e4b39ab..3739c3f0d22 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.searchparam.nickname; import ca.uhn.fhir.interceptor.api.Hook; 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.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.StringParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -39,8 +39,8 @@ public class NicknameInterceptor { private final NicknameSvc myNicknameSvc; - public NicknameInterceptor(NicknameSvc theNicknameSvc) { - myNicknameSvc = theNicknameSvc; + public NicknameInterceptor() throws IOException { + myNicknameSvc = new NicknameSvc(); } @Hook(Pointcut.STORAGE_PRESEARCH_REGISTERED) diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameMap.java similarity index 72% rename from hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameMap.java rename to hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameMap.java index a0d6632c6bc..fc5900e5816 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameMap.java @@ -17,7 +17,7 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.jpa.nickname; +package ca.uhn.fhir.jpa.searchparam.nickname; import javax.annotation.Nonnull; import java.io.BufferedReader; @@ -33,30 +33,19 @@ class NicknameMap { private final Map> myFormalToNick = new HashMap<>(); private final Map> myNicknameToFormal = new HashMap<>(); - private final List myBadRows = new ArrayList<>(); - void load(Reader theReader) throws IOException { try (BufferedReader reader = new BufferedReader(theReader)) { String line; while ((line = reader.readLine()) != null) { String[] parts = line.split(","); - if (parts.length > 1) { - String key = parts[0]; - List values = new ArrayList<>(Arrays.asList(parts).subList(1, parts.length)); - add(key, values); - } else { - myBadRows.add(line); - } + String key = parts[0]; + List values = new ArrayList<>(Arrays.asList(parts).subList(1, parts.length)); + add(key, values); } } } - void clear() { - myFormalToNick.clear(); - myNicknameToFormal.clear(); - } - - void add(String theKey, List theValues) { + private void add(String theKey, List theValues) { myFormalToNick.put(theKey, theValues); for (String value : theValues) { myNicknameToFormal.putIfAbsent(value, new ArrayList<>()); @@ -68,22 +57,14 @@ class NicknameMap { return myFormalToNick.size(); } - boolean isEmpty() { - return size() == 0; - } - - List getBadRows() { - return myBadRows; - } - @Nonnull - public List getNicknamesFromFormalName(String theName) { + List getNicknamesFromFormalName(String theName) { List result = myFormalToNick.get(theName); return result == null ? new ArrayList<>() : result; } @Nonnull - public List getFormalNamesFromNickname(String theNickname) { + List getFormalNamesFromNickname(String theNickname) { List result = myNicknameToFormal.get(theNickname); return result == null ? new ArrayList<>() : result; } diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameSvc.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameSvc.java similarity index 53% rename from hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameSvc.java rename to hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameSvc.java index c99c307f5fa..e500dcc8466 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameSvc.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameSvc.java @@ -17,12 +17,8 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.jpa.nickname; +package ca.uhn.fhir.jpa.searchparam.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.Resource; @@ -36,40 +32,22 @@ import java.util.HashSet; import java.util.List; 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 { - private static final Logger ourLog = LoggerFactory.getLogger(NicknameSvc.class); + private final NicknameMap myNicknameMap = new NicknameMap(); - private NicknameMap myNicknameMap; - - private Resource myNicknameResource; - - public NicknameSvc() { - - } - - public void setNicknameResource(Resource theNicknameResource) { - myNicknameResource = theNicknameResource; + public NicknameSvc() throws IOException { + Resource nicknameCsvResource = new ClassPathResource("/nickname/names.csv"); + try (InputStream inputStream = nicknameCsvResource.getInputStream()) { + try (Reader reader = new InputStreamReader(inputStream)) { + myNicknameMap.load(reader); + } + } } public int size() { - ensureMapInitialized(); return myNicknameMap.size(); } - public List getBadRows() { - ensureMapInitialized(); - return myNicknameMap.getBadRows(); - } - public Collection getEquivalentNames(String theName) { Set retval = new HashSet<>(getNicknamesFromFormalName(theName)); @@ -86,34 +64,11 @@ public class NicknameSvc { @Nonnull List getNicknamesFromFormalName(String theName) { - ensureMapInitialized(); return myNicknameMap.getNicknamesFromFormalName(theName); } @Nonnull List getFormalNamesFromNickname(String theNickname) { - ensureMapInitialized(); 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); - } - } - } } diff --git a/hapi-fhir-jpa/src/main/resources/nickname/License.txt b/hapi-fhir-jpaserver-searchparam/src/main/resources/nickname/License.txt similarity index 100% rename from hapi-fhir-jpa/src/main/resources/nickname/License.txt rename to hapi-fhir-jpaserver-searchparam/src/main/resources/nickname/License.txt diff --git a/hapi-fhir-jpa/src/main/resources/nickname/README.md b/hapi-fhir-jpaserver-searchparam/src/main/resources/nickname/README.md similarity index 100% rename from hapi-fhir-jpa/src/main/resources/nickname/README.md rename to hapi-fhir-jpaserver-searchparam/src/main/resources/nickname/README.md diff --git a/hapi-fhir-jpa/src/main/resources/nickname/README.txt b/hapi-fhir-jpaserver-searchparam/src/main/resources/nickname/README.txt similarity index 100% rename from hapi-fhir-jpa/src/main/resources/nickname/README.txt rename to hapi-fhir-jpaserver-searchparam/src/main/resources/nickname/README.txt diff --git a/hapi-fhir-jpa/src/main/resources/nickname/names.csv b/hapi-fhir-jpaserver-searchparam/src/main/resources/nickname/names.csv similarity index 100% rename from hapi-fhir-jpa/src/main/resources/nickname/names.csv rename to hapi-fhir-jpaserver-searchparam/src/main/resources/nickname/names.csv diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptorTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptorTest.java index 877f6df833f..862366ce6cc 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptorTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptorTest.java @@ -1,6 +1,5 @@ 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.rest.param.StringParam; import org.junit.jupiter.api.Test; @@ -10,18 +9,13 @@ import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; class NicknameInterceptorTest { - - private NicknameInterceptor createNicknameInterceptor() { - return new NicknameInterceptor(new NicknameSvc()); - } - @Test public void testExpandForward() throws IOException { // setup String formalName = "kenneth"; SearchParameterMap sp = new SearchParameterMap(); sp.add("name", new StringParam(formalName).setNicknameExpand(true)); - NicknameInterceptor svc = createNicknameInterceptor(); + NicknameInterceptor svc = new NicknameInterceptor(); // execute svc.expandNicknames(sp); @@ -37,7 +31,7 @@ class NicknameInterceptorTest { String nickname = "ken"; SearchParameterMap sp = new SearchParameterMap(); sp.add("name", new StringParam(nickname).setNicknameExpand(true)); - NicknameInterceptor svc = createNicknameInterceptor(); + NicknameInterceptor svc = new NicknameInterceptor(); // execute svc.expandNicknames(sp); @@ -53,7 +47,7 @@ class NicknameInterceptorTest { String unusualName = "X Æ A-12"; SearchParameterMap sp = new SearchParameterMap(); sp.add("name", new StringParam(unusualName).setNicknameExpand(true)); - NicknameInterceptor svc = createNicknameInterceptor(); + NicknameInterceptor svc = new NicknameInterceptor(); // execute svc.expandNicknames(sp); diff --git a/hapi-fhir-jpa/src/test/java/ca/uhn/fhir/jpa/nickname/NicknameMapTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameMapTest.java similarity index 95% rename from hapi-fhir-jpa/src/test/java/ca/uhn/fhir/jpa/nickname/NicknameMapTest.java rename to hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameMapTest.java index 9adf090d0b8..524142a9ccb 100644 --- a/hapi-fhir-jpa/src/test/java/ca/uhn/fhir/jpa/nickname/NicknameMapTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameMapTest.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.nickname; +package ca.uhn.fhir.jpa.searchparam.nickname; import org.junit.jupiter.api.Test; diff --git a/hapi-fhir-jpa/src/test/java/ca/uhn/fhir/jpa/nickname/NicknameSvcTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameSvcTest.java similarity index 86% rename from hapi-fhir-jpa/src/test/java/ca/uhn/fhir/jpa/nickname/NicknameSvcTest.java rename to hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameSvcTest.java index 180f5a53ad8..80d24b88559 100644 --- a/hapi-fhir-jpa/src/test/java/ca/uhn/fhir/jpa/nickname/NicknameSvcTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameSvcTest.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.nickname; +package ca.uhn.fhir.jpa.searchparam.nickname; import org.junit.jupiter.api.Test; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSettings.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSettings.java index 293594f5c8e..50d9ed78ad4 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSettings.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSettings.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.mdm.api; +import ca.uhn.fhir.mdm.dao.IMdmLinkImplFactory; import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; import java.util.stream.Collectors; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java index 2849ae94408..ac3097cc844 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java @@ -139,7 +139,7 @@ public class MdmControllerHelper { public IBaseBundle getMatchesAndPossibleMatchesForResource(IAnyResource theResource, String theResourceType, RequestDetails theRequestDetails) { RequestPartitionId requestPartitionId; ReadPartitionIdRequestDetails details = ReadPartitionIdRequestDetails.forSearchType(theResourceType, null, null); - if (myMdmSettings.getSearchAllPartitionForMatch()) { + if (myMdmSettings.getSearchAllPartitionForMatch()){ requestPartitionId = RequestPartitionId.allPartitions(); } else { requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, details); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmFieldMatchJson.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmFieldMatchJson.java index d809d0f122b..70059c9f37b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmFieldMatchJson.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmFieldMatchJson.java @@ -19,16 +19,21 @@ */ package ca.uhn.fhir.mdm.rules.json; -import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum; +import ca.uhn.fhir.i18n.Msg; +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.rest.server.exceptions.InternalErrorException; import com.fasterxml.jackson.annotation.JsonProperty; +import org.hl7.fhir.instance.model.api.IBase; import javax.annotation.Nonnull; /** * Contains all business data for determining if a match exists on a particular field, given: *

- * 1. A {@link MatchTypeEnum} which determines the actual similarity values. + * 1. A {@link MdmMatcherEnum} which determines the actual similarity values. * 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) */ @@ -82,6 +87,10 @@ public class MdmFieldMatchJson implements IModelJson { return myMatcher; } + public boolean isMatcherSupportingEmptyFields() { + return (getMatcher() == null) ? false : getMatcher().isMatchingEmptyFields(); + } + public MdmFieldMatchJson setMatcher(MdmMatcherJson theMatcher) { myMatcher = theMatcher; return this; @@ -96,6 +105,17 @@ public class MdmFieldMatchJson implements IModelJson { 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() { return myFhirPath; } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmMatcherJson.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmMatcherJson.java index e78824cbe9b..18332a2a1c4 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmMatcherJson.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmMatcherJson.java @@ -19,13 +19,15 @@ */ package ca.uhn.fhir.mdm.rules.json; -import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; +import org.hl7.fhir.instance.model.api.IBase; public class MdmMatcherJson implements IModelJson { @JsonProperty(value = "algorithm", required = true) - MatchTypeEnum myAlgorithm; + MdmMatcherEnum myAlgorithm; @JsonProperty(value = "identifierSystem", required = false) String myIdentifierSystem; @@ -36,11 +38,11 @@ public class MdmMatcherJson implements IModelJson { @JsonProperty(value = "exact") boolean myExact; - public MatchTypeEnum getAlgorithm() { + public MdmMatcherEnum getAlgorithm() { return myAlgorithm; } - public MdmMatcherJson setAlgorithm(MatchTypeEnum theAlgorithm) { + public MdmMatcherJson setAlgorithm(MdmMatcherEnum theAlgorithm) { myAlgorithm = theAlgorithm; return this; } @@ -62,4 +64,12 @@ public class MdmMatcherJson implements IModelJson { myExact = theExact; 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); + } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/models/MatchTypeEnum.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/BaseHapiStringMetric.java similarity index 53% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/models/MatchTypeEnum.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/BaseHapiStringMetric.java index 7f721c20822..e4485019ade 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/models/MatchTypeEnum.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/BaseHapiStringMetric.java @@ -17,37 +17,17 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.models; +package ca.uhn.fhir.mdm.rules.matcher; -/** - * 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; +import ca.uhn.fhir.util.StringUtil; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +public abstract class BaseHapiStringMetric { + protected String extractString(IPrimitiveType thePrimitive, boolean theExact) { + String theString = thePrimitive.getValueAsString(); + if (theExact) { + return theString; + } + return StringUtil.normalizeStringForSearchIndexing(theString); + } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/EmptyFieldMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/EmptyFieldMatcher.java similarity index 74% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/EmptyFieldMatcher.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/EmptyFieldMatcher.java index 02618e097d3..a3341ebf9fe 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/EmptyFieldMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/EmptyFieldMatcher.java @@ -17,10 +17,9 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers; +package ca.uhn.fhir.mdm.rules.matcher; -import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson; -import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher; +import ca.uhn.fhir.context.FhirContext; import org.hl7.fhir.instance.model.api.IBase; public class EmptyFieldMatcher implements IMdmFieldMatcher { @@ -29,7 +28,7 @@ public class EmptyFieldMatcher implements IMdmFieldMatcher { } @Override - public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) { + public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) { for (IBase b : new IBase[] {theLeftBase, theRightBase}) { if (b != null && !b.isEmpty()) { return false; @@ -37,9 +36,4 @@ public class EmptyFieldMatcher implements IMdmFieldMatcher { } return true; } - - @Override - public boolean isMatchingEmptyFields() { - return true; - } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/ExtensionMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/ExtensionMatcher.java similarity index 85% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/ExtensionMatcher.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/ExtensionMatcher.java index 4b96a838bc6..fea4d7314eb 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/ExtensionMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/ExtensionMatcher.java @@ -17,10 +17,9 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers; +package ca.uhn.fhir.mdm.rules.matcher; -import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson; -import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.util.ExtensionUtil; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseExtension; @@ -31,7 +30,7 @@ import java.util.List; public class ExtensionMatcher implements IMdmFieldMatcher { @Override - public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) { + public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) { if (!(theLeftBase instanceof IBaseHasExtensions && theRightBase instanceof IBaseHasExtensions)) { return false; } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiDateMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiDateMatcher.java new file mode 100644 index 00000000000..8c923f2c167 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiDateMatcher.java @@ -0,0 +1,41 @@ +/*- + * #%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()); + } + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiDateMatcherDstu3.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiDateMatcherDstu3.java new file mode 100644 index 00000000000..2682856f291 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiDateMatcherDstu3.java @@ -0,0 +1,59 @@ +/*- + * #%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; + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiDateMatcherR4.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiDateMatcherR4.java new file mode 100644 index 00000000000..ade01e59c30 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiDateMatcherR4.java @@ -0,0 +1,61 @@ +/*- + * #%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; + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/HapiStringMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiStringMatcher.java similarity index 57% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/HapiStringMatcher.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiStringMatcher.java index 4168051754b..9d5b9d6b674 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/HapiStringMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/HapiStringMatcher.java @@ -17,26 +17,33 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers; +package ca.uhn.fhir.mdm.rules.matcher; -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.context.FhirContext; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IPrimitiveType; /** * Similarity measure for two IBase fields whose similarity can be measured by their String representations. */ -public class HapiStringMatcher implements IMdmFieldMatcher { +public class HapiStringMatcher extends BaseHapiStringMetric implements IMdmFieldMatcher { + private final IMdmStringMatcher myStringMatcher; + + public HapiStringMatcher(IMdmStringMatcher theStringMatcher) { + myStringMatcher = theStringMatcher; + } + + public HapiStringMatcher() { + myStringMatcher = String::equals; + } @Override - public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theExtraMatchParams) { + public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) { if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) { - String leftString = StringMatcherUtils.extractString((IPrimitiveType) theLeftBase, theExtraMatchParams.getExact()); - String rightString = StringMatcherUtils.extractString((IPrimitiveType) theRightBase, theExtraMatchParams.getExact()); + String leftString = extractString((IPrimitiveType) theLeftBase, theExact); + String rightString = extractString((IPrimitiveType) theRightBase, theExact); - return leftString.equals(rightString); + return myStringMatcher.matches(leftString, rightString); } return false; } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IMatcherFactory.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IMatcherFactory.java deleted file mode 100644 index bb9ae94d9de..00000000000 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IMatcherFactory.java +++ /dev/null @@ -1,12 +0,0 @@ -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); -} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/models/IMdmFieldMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IMdmFieldMatcher.java similarity index 64% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/models/IMdmFieldMatcher.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IMdmFieldMatcher.java index 745a9df85f3..c9603d9b224 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/models/IMdmFieldMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IMdmFieldMatcher.java @@ -17,27 +17,14 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.models; +package ca.uhn.fhir.mdm.rules.matcher; -import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson; +import ca.uhn.fhir.context.FhirContext; 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. */ public interface IMdmFieldMatcher { - /** - * 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; - } + boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem); } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IMdmStringMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IMdmStringMatcher.java new file mode 100644 index 00000000000..638c2741d3c --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IMdmStringMatcher.java @@ -0,0 +1,24 @@ +/*- + * #%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); +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/IdentifierMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IdentifierMatcher.java similarity index 80% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/IdentifierMatcher.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IdentifierMatcher.java index a3f82ae7c19..94662737cbc 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/IdentifierMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/IdentifierMatcher.java @@ -17,34 +17,25 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers; +package ca.uhn.fhir.mdm.rules.matcher; -import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson; -import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.util.CanonicalIdentifier; import ca.uhn.fhir.mdm.util.IdentifierUtil; import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.util.CanonicalIdentifier; import org.hl7.fhir.instance.model.api.IBase; 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 * matcher only returns true if the identifier systems also match this system. * @throws UnsupportedOperationException if either Base is not an Identifier instance */ @Override - public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) { + public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) { CanonicalIdentifier left = IdentifierUtil.identifierDtFromIdentifier(theLeftBase); - if (theParams.getIdentifierSystem() != null) { - if (!theParams.getIdentifierSystem().equals(left.getSystemElement().getValueAsString())) { + if (theIdentifierSystem != null) { + if (!theIdentifierSystem.equals(left.getSystemElement().getValueAsString())) { return false; } } @@ -54,4 +45,11 @@ public class IdentifierMatcher implements IMdmFieldMatcher { } return left.equals(right); } + + private boolean isEmpty(StringDt theValue) { + if (theValue == null) { + return true; + } + return theValue.isEmpty(); + } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/MdmMatcherEnum.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/MdmMatcherEnum.java new file mode 100644 index 00000000000..17e2dd1e7f4 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/MdmMatcherEnum.java @@ -0,0 +1,86 @@ +/*- + * #%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; + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/MdmMatcherFactory.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/MdmMatcherFactory.java deleted file mode 100644 index b5509dd506b..00000000000 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/MdmMatcherFactory.java +++ /dev/null @@ -1,96 +0,0 @@ -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; - } -} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/MdmNameMatchModeEnum.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/MdmNameMatchModeEnum.java similarity index 93% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/MdmNameMatchModeEnum.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/MdmNameMatchModeEnum.java index 1f62d634b2b..aff7121d24b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/MdmNameMatchModeEnum.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/MdmNameMatchModeEnum.java @@ -17,7 +17,7 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers; +package ca.uhn.fhir.mdm.rules.matcher; public enum MdmNameMatchModeEnum { ANY_ORDER, diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/NameMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/NameMatcher.java similarity index 72% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/NameMatcher.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/NameMatcher.java index 15cafbdabbc..c87a47c5246 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/NameMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/NameMatcher.java @@ -17,11 +17,9 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers; +package ca.uhn.fhir.mdm.rules.matcher; 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.util.StringUtil; import org.apache.commons.lang3.StringUtils; @@ -37,27 +35,24 @@ public class NameMatcher implements IMdmFieldMatcher { private final MdmNameMatchModeEnum myMatchMode; - private final FhirContext myFhirContext; - - public NameMatcher(FhirContext theFhirContext, MdmNameMatchModeEnum theMatchMode) { + public NameMatcher(MdmNameMatchModeEnum theMatchMode) { myMatchMode = theMatchMode; - myFhirContext = theFhirContext; } @Override - public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) { - String leftFamilyName = NameUtil.extractFamilyName(myFhirContext, theLeftBase); - String rightFamilyName = NameUtil.extractFamilyName(myFhirContext, theRightBase); + public boolean matches(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) { + String leftFamilyName = NameUtil.extractFamilyName(theFhirContext, theLeftBase); + String rightFamilyName = NameUtil.extractFamilyName(theFhirContext, theRightBase); if (StringUtils.isEmpty(leftFamilyName) || StringUtils.isEmpty(rightFamilyName)) { return false; } boolean match = false; - List leftGivenNames = NameUtil.extractGivenNames(myFhirContext, theLeftBase); - List rightGivenNames = NameUtil.extractGivenNames(myFhirContext, theRightBase); + List leftGivenNames = NameUtil.extractGivenNames(theFhirContext, theLeftBase); + List rightGivenNames = NameUtil.extractGivenNames(theFhirContext, theRightBase); - if (!theParams.getExact()) { + if (!theExact) { leftFamilyName = StringUtil.normalizeStringForSearchIndexing(leftFamilyName); rightFamilyName = StringUtil.normalizeStringForSearchIndexing(rightFamilyName); leftGivenNames = leftGivenNames.stream().map(StringUtil::normalizeStringForSearchIndexing).collect(Collectors.toList()); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/NicknameMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/NicknameMatcher.java similarity index 55% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/NicknameMatcher.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/NicknameMatcher.java index f95e94127df..335acdc5636 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/NicknameMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/NicknameMatcher.java @@ -17,25 +17,28 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers; +package ca.uhn.fhir.mdm.rules.matcher; -import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson; -import ca.uhn.fhir.mdm.rules.matcher.models.IMdmFieldMatcher; -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 ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.searchparam.nickname.NicknameSvc; +import java.io.IOException; import java.util.Collection; import java.util.Locale; -public class NicknameMatcher implements IMdmFieldMatcher { +public class NicknameMatcher implements IMdmStringMatcher { private final NicknameSvc myNicknameSvc; - public NicknameMatcher(NicknameSvc theNicknameSvc) { - myNicknameSvc = theNicknameSvc; + public NicknameMatcher() { + try { + 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) { String leftString = theLeftString.toLowerCase(Locale.ROOT); String rightString = theRightString.toLowerCase(Locale.ROOT); @@ -48,15 +51,4 @@ public class NicknameMatcher implements IMdmFieldMatcher { Collection rightNames = myNicknameSvc.getEquivalentNames(rightString); 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; - } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/NumericMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/NumericMatcher.java similarity index 53% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/NumericMatcher.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/NumericMatcher.java index c9a3777f6dd..d7ece7966d4 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/NumericMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/NumericMatcher.java @@ -17,27 +17,19 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers; +package ca.uhn.fhir.mdm.rules.matcher; 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. // 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 IMdmFieldMatcher { +public class NumericMatcher implements IMdmStringMatcher { private final NumericEncoder encoder = new NumericEncoder(); @Override - public boolean matches(IBase theLeftBase, IBase theRightBase, MdmMatcherJson theParams) { - if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) { - String left = encoder.encode(StringMatcherUtils.extractString((IPrimitiveType) theLeftBase, theParams.getExact())); - String right = encoder.encode(StringMatcherUtils.extractString((IPrimitiveType) theRightBase, theParams.getExact())); - return left.equals(right); - } - return false; + public boolean matches(String theLeftString, String theRightString) { + String left = encoder.encode(theLeftString); + String right = encoder.encode(theRightString); + return left.equals(right); } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/PhoneticEncoderMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/PhoneticEncoderMatcher.java similarity index 64% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/PhoneticEncoderMatcher.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/PhoneticEncoderMatcher.java index efbb3372e1f..cd158a443d7 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/PhoneticEncoderMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/PhoneticEncoderMatcher.java @@ -17,20 +17,15 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.rules.matcher.fieldmatchers; +package ca.uhn.fhir.mdm.rules.matcher; import ca.uhn.fhir.context.phonetic.IPhoneticEncoder; 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 org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class PhoneticEncoderMatcher implements IMdmFieldMatcher { +public class PhoneticEncoderMatcher implements IMdmStringMatcher { private static final Logger ourLog = LoggerFactory.getLogger(PhoneticEncoderMatcher.class); private final IPhoneticEncoder myStringEncoder; @@ -39,16 +34,8 @@ public class PhoneticEncoderMatcher implements IMdmFieldMatcher { myStringEncoder = PhoneticEncoderUtil.getEncoder(thePhoneticEnum.name()); } + @Override public boolean matches(String theLeftString, String 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); - } - } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/SubstringStringMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/SubstringStringMatcher.java new file mode 100644 index 00000000000..28fc98f18b6 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/SubstringStringMatcher.java @@ -0,0 +1,27 @@ +/*- + * #%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); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java deleted file mode 100644 index a78a3800b68..00000000000 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java +++ /dev/null @@ -1,56 +0,0 @@ -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; - } -} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/HapiDateMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/HapiDateMatcher.java deleted file mode 100644 index 8504120433e..00000000000 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/HapiDateMatcher.java +++ /dev/null @@ -1,65 +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.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); - } -} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/SubstringStringMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/SubstringStringMatcher.java deleted file mode 100644 index dc72b446b03..00000000000 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/SubstringStringMatcher.java +++ /dev/null @@ -1,39 +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.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; - } -} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/util/StringMatcherUtils.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/util/StringMatcherUtils.java deleted file mode 100644 index 3924745f128..00000000000 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/util/StringMatcherUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -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); - } -} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/similarity/HapiStringSimilarity.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/similarity/HapiStringSimilarity.java index c5e50effeae..d2e7d894a6b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/similarity/HapiStringSimilarity.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/similarity/HapiStringSimilarity.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.mdm.rules.similarity; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.mdm.rules.matcher.util.StringMatcherUtils; +import ca.uhn.fhir.mdm.rules.matcher.BaseHapiStringMetric; import info.debatty.java.stringsimilarity.interfaces.NormalizedStringSimilarity; import org.hl7.fhir.instance.model.api.IBase; 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. */ -public class HapiStringSimilarity implements IMdmFieldSimilarity { +public class HapiStringSimilarity extends BaseHapiStringMetric implements IMdmFieldSimilarity { private final NormalizedStringSimilarity myStringSimilarity; public HapiStringSimilarity(NormalizedStringSimilarity theStringSimilarity) { @@ -38,8 +38,8 @@ public class HapiStringSimilarity implements IMdmFieldSimilarity { @Override public double similarity(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact) { if (theLeftBase instanceof IPrimitiveType && theRightBase instanceof IPrimitiveType) { - String leftString = StringMatcherUtils.extractString((IPrimitiveType) theLeftBase, theExact); - String rightString = StringMatcherUtils.extractString((IPrimitiveType) theRightBase, theExact); + String leftString = extractString((IPrimitiveType) theLeftBase, theExact); + String rightString = extractString((IPrimitiveType) theRightBase, theExact); return myStringSimilarity.similarity(leftString, rightString); } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceFieldMatcher.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceFieldMatcher.java index 9dd3fda09ec..55c125d1bc4 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceFieldMatcher.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceFieldMatcher.java @@ -21,16 +21,9 @@ package ca.uhn.fhir.mdm.rules.svc; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.fhirpath.IFhirPath; -import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.mdm.api.MdmMatchEvaluation; 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.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 org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; @@ -55,16 +48,7 @@ public class MdmResourceFieldMatcher { private final String myName; private final boolean myIsFhirPathExpression; - private final IMatcherFactory myIMatcherFactory; - - public MdmResourceFieldMatcher( - FhirContext theFhirContext, - IMatcherFactory theIMatcherFactory, - MdmFieldMatchJson theMdmFieldMatchJson, - MdmRulesJson theMdmRulesJson - ) { - myIMatcherFactory = theIMatcherFactory; - + public MdmResourceFieldMatcher(FhirContext theFhirContext, MdmFieldMatchJson theMdmFieldMatchJson, MdmRulesJson theMdmRulesJson) { myFhirContext = theFhirContext; myMdmFieldMatchJson = theMdmFieldMatchJson; myResourceType = theMdmFieldMatchJson.getResourceType(); @@ -86,6 +70,7 @@ public class MdmResourceFieldMatcher { * @param theRightResource the second {@link IBaseResource} * @return A boolean indicating whether they match. */ + @SuppressWarnings("rawtypes") public MdmMatchEvaluation match(IBaseResource theLeftResource, IBaseResource theRightResource) { validate(theLeftResource); validate(theRightResource); @@ -105,12 +90,12 @@ public class MdmResourceFieldMatcher { return match(leftValues, rightValues); } + @SuppressWarnings("rawtypes") private MdmMatchEvaluation match(List theLeftValues, List theRightValues) { MdmMatchEvaluation retval = new MdmMatchEvaluation(false, 0.0); boolean isMatchingEmptyFieldValues = (theLeftValues.isEmpty() && theRightValues.isEmpty()); - IMdmFieldMatcher matcher = getFieldMatcher(); - if (isMatchingEmptyFieldValues && (matcher != null && matcher.isMatchingEmptyFields())) { + if (isMatchingEmptyFieldValues && myMdmFieldMatchJson.isMatcherSupportingEmptyFields()) { return match((IBase) null, (IBase) null); } @@ -125,18 +110,7 @@ public class MdmResourceFieldMatcher { } private MdmMatchEvaluation match(IBase theLeftValue, IBase 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."); + return myMdmFieldMatchJson.match(myFhirContext, theLeftValue, theRightValue); } private void validate(IBaseResource theResource) { @@ -162,17 +136,4 @@ public class MdmResourceFieldMatcher { public String getName() { 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); - } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceMatcherSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceMatcherSvc.java index 418849cb363..5dc9e46fbf4 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceMatcherSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceMatcherSvc.java @@ -30,8 +30,6 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson; 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.slf4j.Logger; import org.springframework.stereotype.Service; @@ -50,18 +48,16 @@ public class MdmResourceMatcherSvc { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); private final FhirContext myFhirContext; - private final IMatcherFactory myMatcherFactory; + private MdmRulesJson myMdmRulesJson; private final List myFieldMatchers = new ArrayList<>(); - private MdmRulesJson myMdmRulesJson; - - public MdmResourceMatcherSvc( - FhirContext theFhirContext, - IMatcherFactory theIMatcherFactory, - IMdmSettings theMdmSettings - ) { + public MdmResourceMatcherSvc(FhirContext theFhirContext, IMdmSettings theMdmSettings) { myFhirContext = theFhirContext; - myMatcherFactory = theIMatcherFactory; + + setMdmSettings(theMdmSettings); + } + + public void setMdmSettings(IMdmSettings theMdmSettings) { myMdmRulesJson = theMdmSettings.getMdmRules(); addFieldMatchers(); @@ -73,7 +69,7 @@ public class MdmResourceMatcherSvc { } myFieldMatchers.clear(); for (MdmFieldMatchJson matchFieldJson : myMdmRulesJson.getMatchFields()) { - myFieldMatchers.add(new MdmResourceFieldMatcher(myFhirContext, myMatcherFactory, matchFieldJson, myMdmRulesJson)); + myFieldMatchers.add(new MdmResourceFieldMatcher( myFhirContext, matchFieldJson, myMdmRulesJson)); } } @@ -81,8 +77,9 @@ public class MdmResourceMatcherSvc { * 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. * - * @param theLeftResource The first {@link IBaseResource}. + * @param theLeftResource The first {@link IBaseResource}. * @param theRightResource The second {@link IBaseResource} + * * @return an {@link MdmMatchResultEnum} indicating the result of the comparison. */ public MdmMatchOutcome getMatchResult(IBaseResource theLeftResource, IBaseResource theRightResource) { @@ -94,8 +91,8 @@ public class MdmResourceMatcherSvc { MdmMatchResultEnum matchResultEnum = myMdmRulesJson.getMatchResult(matchResult.getVector()); matchResult.setMatchResultEnum(matchResultEnum); if (ourLog.isDebugEnabled()) { - ourLog.debug("{} {}: {}", matchResult.getMatchResultEnum(), theRightResource.getIdElement().toUnqualifiedVersionless(), matchResult); - if (ourLog.isTraceEnabled()) { + ourLog.debug("{} {}: {}", matchResult.getMatchResultEnum(), theRightResource.getIdElement().toUnqualifiedVersionless(), matchResult); + if (ourLog.isTraceEnabled()) { ourLog.trace("Field matcher results:\n{}", myMdmRulesJson.getDetailedFieldMatchResultWithSuccessInformation(matchResult.getVector())); } } @@ -108,11 +105,11 @@ public class MdmResourceMatcherSvc { * 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. * `0001` - *

+ * * 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. * `0010` - *

+ * * Then, we bitwise-or it with the current retval: * 0001|0010 = 0011 * The binary string is now `0011`, which when you return it as a long becomes `3`. @@ -149,16 +146,11 @@ public class MdmResourceMatcherSvc { return retVal; } - private boolean isValidResourceType(String theResourceType, String theFieldComparatorType) { + + private boolean isValidResourceType(String theResourceType, String theFieldComparatorType) { return ( theFieldComparatorType.equalsIgnoreCase(MdmConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE) - || theFieldComparatorType.equalsIgnoreCase(theResourceType) + || theFieldComparatorType.equalsIgnoreCase(theResourceType) ); } - - @VisibleForTesting - public void setMdmRulesJson(MdmRulesJson theMdmRulesJson) { - myMdmRulesJson = theMdmRulesJson; - addFieldMatchers(); - } } diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/BaseR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/BaseR4Test.java index c4e50665674..494cd93992d 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/BaseR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/BaseR4Test.java @@ -1,19 +1,14 @@ package ca.uhn.fhir.mdm; 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.MdmMatchResultEnum; import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator; import ca.uhn.fhir.mdm.rules.config.MdmSettings; 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.rest.server.util.ISearchParamRegistry; import org.hl7.fhir.r4.model.Patient; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @@ -25,20 +20,6 @@ public abstract class BaseR4Test { protected static final FhirContext ourFhirContext = FhirContext.forR4(); 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() { Patient patient = new Patient(); patient.addName().addGiven("John"); @@ -54,10 +35,7 @@ public abstract class BaseR4Test { } protected MdmResourceMatcherSvc buildMatcher(MdmRulesJson theMdmRulesJson) { - return new MdmResourceMatcherSvc(ourFhirContext, - myIMatcherFactory, - new MdmSettings(new MdmRuleValidator(ourFhirContext, mySearchParamRetriever)).setMdmRules(theMdmRulesJson) - ); + return new MdmResourceMatcherSvc(ourFhirContext, new MdmSettings(new MdmRuleValidator(ourFhirContext, mySearchParamRetriever)).setMdmRules(theMdmRulesJson)); } protected void assertMatch(MdmMatchResultEnum theExpectedMatchEnum, MdmMatchOutcome theMatchResult) { diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/json/VectorMatchResultMapTest.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/json/VectorMatchResultMapTest.java index 19d105e6cd4..e129cf2f835 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/json/VectorMatchResultMapTest.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/json/VectorMatchResultMapTest.java @@ -1,7 +1,7 @@ package ca.uhn.fhir.mdm.rules.json; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum; +import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -27,7 +27,7 @@ public class VectorMatchResultMapTest { @Test public void testMatchBeforePossibleMatch() { MdmRulesJson mdmRulesJson = new MdmRulesJson(); - MdmMatcherJson matcherJson = new MdmMatcherJson().setAlgorithm(MatchTypeEnum.STRING); + MdmMatcherJson matcherJson = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.STRING); 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("prefix").setResourceType("Patient").setResourcePath("name.prefix").setMatcher(matcherJson)); diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/BaseMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/BaseMatcherR4Test.java index 5a19e8956be..f6243442212 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/BaseMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/BaseMatcherR4Test.java @@ -1,16 +1,7 @@ package ca.uhn.fhir.mdm.rules.matcher; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson; -import org.junit.jupiter.api.BeforeEach; public abstract class BaseMatcherR4Test { protected static final FhirContext ourFhirContext = FhirContext.forR4(); - - protected MdmMatcherJson myMdmMatcherJson; - - @BeforeEach - public void before() { - myMdmMatcherJson = new MdmMatcherJson(); - } } diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/DateMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/DateMatcherR4Test.java index 9b79f81a578..113c8ba6a84 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/DateMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/DateMatcherR4Test.java @@ -1,10 +1,8 @@ package ca.uhn.fhir.mdm.rules.matcher; -import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.HapiDateMatcher; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Calendar; @@ -16,14 +14,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class DateMatcherR4Test extends BaseMatcherR4Test { - private HapiDateMatcher myDateMatcher; - - @BeforeEach - public void before() { - super.before(); - myDateMatcher = new HapiDateMatcher(ourFhirContext); - } - @Test public void testExactDatePrecision() { Calendar cal = new GregorianCalendar(2020, 6, 15); @@ -53,8 +43,7 @@ public class DateMatcherR4Test extends BaseMatcherR4Test { } private boolean dateMatch(Date theDate, Date theSameMonth, TemporalPrecisionEnum theTheDay) { - myMdmMatcherJson.setExact(true); - return myDateMatcher.matches(new DateType(theDate, theTheDay), new DateType(theSameMonth, theTheDay), myMdmMatcherJson); + return MdmMatcherEnum.DATE.match(ourFhirContext, new DateType(theDate, theTheDay), new DateType(theSameMonth, theTheDay), true, null); } @Test @@ -98,11 +87,12 @@ public class DateMatcherR4Test extends BaseMatcherR4Test { } private boolean dateTimeMatch(Date theDate, Date theSecondDate, TemporalPrecisionEnum thePrecision, TemporalPrecisionEnum theSecondPrecision) { - myMdmMatcherJson.setExact(true); - return myDateMatcher.matches( + return MdmMatcherEnum.DATE.match( + ourFhirContext, new DateTimeType(theDate, thePrecision), new DateTimeType(theSecondDate, theSecondPrecision), - myMdmMatcherJson + true, + null ); } } diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/EmptyFieldMatcherTest.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/EmptyFieldMatcherTest.java index 67ba9232427..f1ef1982b22 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/EmptyFieldMatcherTest.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/EmptyFieldMatcherTest.java @@ -2,8 +2,6 @@ package ca.uhn.fhir.mdm.rules.matcher; import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson; 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 org.junit.jupiter.api.Test; @@ -18,16 +16,16 @@ public class EmptyFieldMatcherTest extends BaseMatcherR4Test { StringDt leftEmpty = new StringDt(""); StringDt rightEmpty = new StringDt(""); StringDt right = new StringDt("a value"); + MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.EMPTY_FIELD); + MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher); - EmptyFieldMatcher fieldMatch = new EmptyFieldMatcher(); - - assertTrue(fieldMatch.matches(null, null, null)); - assertTrue(fieldMatch.matches(null, rightEmpty, null)); - assertTrue(fieldMatch.matches(leftEmpty, null, null)); - assertTrue(fieldMatch.matches(leftEmpty, rightEmpty, null)); - assertFalse(fieldMatch.matches(null, right, null)); - assertFalse(fieldMatch.matches(left, null, null)); - assertFalse(fieldMatch.matches(left, right, null)); + assertTrue(fieldMatch.match(ourFhirContext, null, null).match); + assertTrue(fieldMatch.match(ourFhirContext, null, rightEmpty).match); + assertTrue(fieldMatch.match(ourFhirContext, leftEmpty, null).match); + assertTrue(fieldMatch.match(ourFhirContext, leftEmpty, rightEmpty).match); + assertFalse(fieldMatch.match(ourFhirContext, null, right).match); + assertFalse(fieldMatch.match(ourFhirContext, left, null).match); + assertFalse(fieldMatch.match(ourFhirContext, left, right).match); } diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/ExtensionMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/ExtensionMatcherR4Test.java index 569e1b133fe..c5252b1e016 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/ExtensionMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/ExtensionMatcherR4Test.java @@ -1,28 +1,16 @@ 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.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class ExtensionMatcherR4Test extends BaseMatcherR4Test { - - private ExtensionMatcher myExtensionMatcher; - - @BeforeEach - public void before() { - super.before(); - - myExtensionMatcher = new ExtensionMatcher(); - } - @Test public void testPatientWithMatchingExtension(){ Patient patient1 = new Patient(); @@ -31,7 +19,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test { patient1.addExtension("asd",new StringType("Patient1")); patient2.addExtension("asd",new StringType("Patient1")); - assertTrue(match(patient1, patient2)); + assertTrue(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null)); } @Test @@ -42,7 +30,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test { patient1.addExtension("asd",new StringType("Patient1")); patient2.addExtension("asd",new StringType("Patient2")); - assertFalse(match(patient1, patient2)); + assertFalse(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null)); } @Test @@ -53,7 +41,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test { patient1.addExtension("asd",new StringType("Patient1")); patient2.addExtension("asd1",new StringType("Patient1")); - assertFalse(match(patient1, patient2)); + assertFalse(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null)); } @Test @@ -66,7 +54,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test { patient2.addExtension("asd",new StringType("Patient1")); patient2.addExtension("asdasd", new StringType("some value")); - assertTrue(match(patient1, patient2)); + assertTrue(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null)); } @Test @@ -77,7 +65,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test { patient1.addExtension("asd", new IntegerType(123)); patient2.addExtension("asd", new IntegerType(123)); - assertTrue(match(patient1, patient2)); + assertTrue(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null)); } @Test @@ -85,10 +73,7 @@ public class ExtensionMatcherR4Test extends BaseMatcherR4Test { Patient patient1 = new Patient(); Patient patient2 = new Patient(); - assertFalse(match(patient1, patient2)); + assertFalse(MdmMatcherEnum.EXTENSION_ANY_ORDER.match(ourFhirContext, patient1, patient2, false, null)); } - private boolean match(IBase theFirst, IBase theSecond) { - return myExtensionMatcher.matches(theFirst, theSecond, myMdmMatcherJson); - } } diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/IdentifierMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/IdentifierMatcherR4Test.java index 221eb0d8756..66666446f8a 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/IdentifierMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/IdentifierMatcherR4Test.java @@ -1,9 +1,8 @@ package ca.uhn.fhir.mdm.rules.matcher; -import ca.uhn.fhir.mdm.rules.matcher.fieldmatchers.IdentifierMatcher; -import org.hl7.fhir.instance.model.api.IBase; +import ca.uhn.fhir.mdm.rules.json.MdmFieldMatchJson; +import ca.uhn.fhir.mdm.rules.json.MdmMatcherJson; import org.hl7.fhir.r4.model.Identifier; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -15,20 +14,15 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test { private static final String MATCHING_VALUE = "matchme"; private static final String OTHER_VALUE = "strange"; - private IdentifierMatcher myIdentifierMatcher; - - @BeforeEach - public void before() { - super.before(); - myIdentifierMatcher = new IdentifierMatcher(); - } - @Test public void testIdentifierMatch() { Identifier left = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE); Identifier right = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE); - assertTrue(match(left, right)); + MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER); + MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher); + + assertTrue(fieldMatch.match(ourFhirContext, left, right).match); } @Test @@ -39,15 +33,17 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test { Identifier rightNoSystem = new Identifier().setValue(MATCHING_VALUE); Identifier rightNoValue = new Identifier().setSystem(MATCHING_SYSTEM); + MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER); + MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher); - assertFalse(match(left, rightWrongSystem)); - assertFalse(match(left, rightWrongValue)); - assertFalse(match(left, rightNoSystem)); - assertFalse(match(left, rightNoValue)); - assertFalse(match(rightWrongSystem, left)); - assertFalse(match(rightWrongValue, left)); - assertFalse(match(rightNoSystem, left)); - assertFalse(match(rightNoValue, left)); + assertFalse(fieldMatch.match(ourFhirContext, left, rightWrongSystem).match); + assertFalse(fieldMatch.match(ourFhirContext, left, rightWrongValue).match); + assertFalse(fieldMatch.match(ourFhirContext, left, rightNoSystem).match); + assertFalse(fieldMatch.match(ourFhirContext, left, rightNoValue).match); + assertFalse(fieldMatch.match(ourFhirContext, rightWrongSystem, left).match); + assertFalse(fieldMatch.match(ourFhirContext, rightWrongValue, left).match); + assertFalse(fieldMatch.match(ourFhirContext, rightNoSystem, left).match); + assertFalse(fieldMatch.match(ourFhirContext, rightNoValue, left).match); } @Test @@ -55,9 +51,10 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test { Identifier left = new Identifier().setSystem(MATCHING_SYSTEM); Identifier right = new Identifier().setSystem(MATCHING_SYSTEM); - myMdmMatcherJson.setIdentifierSystem(MATCHING_SYSTEM); + MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER).setIdentifierSystem(MATCHING_SYSTEM); + MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher); - assertFalse(match(left, right)); + assertFalse(fieldMatch.match(ourFhirContext, left, right).match); } @Test @@ -65,9 +62,10 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test { Identifier left = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE); Identifier right = new Identifier().setSystem(MATCHING_SYSTEM).setValue(MATCHING_VALUE); - myMdmMatcherJson.setIdentifierSystem(MATCHING_SYSTEM); + MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER).setIdentifierSystem(MATCHING_SYSTEM); + MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher); - assertTrue(match(left, right)); + assertTrue(fieldMatch.match(ourFhirContext, left, right).match); } @Test @@ -75,12 +73,9 @@ public class IdentifierMatcherR4Test extends BaseMatcherR4Test { Identifier left = new Identifier().setSystem(OTHER_SYSTEM).setValue(MATCHING_VALUE); Identifier right = new Identifier().setSystem(OTHER_SYSTEM).setValue(MATCHING_VALUE); - myMdmMatcherJson.setIdentifierSystem(MATCHING_SYSTEM); + MdmMatcherJson matcher = new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.IDENTIFIER).setIdentifierSystem(MATCHING_SYSTEM); + MdmFieldMatchJson fieldMatch = new MdmFieldMatchJson().setMatcher(matcher); - assertFalse(match(left, right)); - } - - private boolean match(IBase theFirst, IBase theSecond) { - return myIdentifierMatcher.matches(theFirst, theSecond, myMdmMatcherJson); + assertFalse(fieldMatch.match(ourFhirContext, left, right).match); } } diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/NicknameMatcherTest.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/NicknameMatcherTest.java index d59a76312c2..f1ed249a21f 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/NicknameMatcherTest.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/NicknameMatcherTest.java @@ -1,48 +1,31 @@ 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 static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + class NicknameMatcherTest { - IMdmFieldMatcher matcher; - - NicknameSvc myNicknameSvc = new NicknameSvc(); - - @BeforeEach - public void begin() { - matcher = new NicknameMatcher(myNicknameSvc); - } + IMdmStringMatcher matcher = new NicknameMatcher(); @Test public void testMatches() { - Assertions.assertTrue(match("Ken", "ken")); - Assertions.assertTrue(match("ken", "Ken")); - Assertions.assertTrue(match("Ken", "Ken")); - Assertions.assertTrue(match("Kenneth", "Ken")); - Assertions.assertTrue(match("Kenneth", "Kenny")); - Assertions.assertTrue(match("Ken", "Kenneth")); - Assertions.assertTrue(match("Kenny", "Kenneth")); - Assertions.assertTrue(match("Jim", "Jimmy")); - Assertions.assertTrue(match("Jimmy", "Jim")); - Assertions.assertTrue(match("Jim", "James")); - Assertions.assertTrue(match("Jimmy", "James")); - Assertions.assertTrue(match("James", "Jimmy")); - Assertions.assertTrue(match("James", "Jim")); + assertTrue(matcher.matches("Ken", "ken")); + assertTrue(matcher.matches("ken", "Ken")); + assertTrue(matcher.matches("Ken", "Ken")); + assertTrue(matcher.matches("Kenneth", "Ken")); + assertTrue(matcher.matches("Kenneth", "Kenny")); + assertTrue(matcher.matches("Ken", "Kenneth")); + assertTrue(matcher.matches("Kenny", "Kenneth")); + assertTrue(matcher.matches("Jim", "Jimmy")); + assertTrue(matcher.matches("Jimmy", "Jim")); + assertTrue(matcher.matches("Jim", "James")); + assertTrue(matcher.matches("Jimmy", "James")); + assertTrue(matcher.matches("James", "Jimmy")); + assertTrue(matcher.matches("James", "Jim")); - Assertions.assertFalse(match("Ken", "Bob")); + assertFalse(matcher.matches("Ken", "Bob")); // These aren't nickname matches. If you want matches like these use a phonetic matcher - 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); + assertFalse(matcher.matches("Allen", "Allan")); } } diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/StringMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/StringMatcherR4Test.java index 646641f8966..73aafb7aebe 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/StringMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/StringMatcherR4Test.java @@ -1,140 +1,106 @@ 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.DateType; import org.hl7.fhir.r4.model.Enumeration; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.StringType; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; - import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; public class StringMatcherR4Test extends BaseMatcherR4Test { private static final Logger ourLog = LoggerFactory.getLogger(StringMatcherR4Test.class); public static final String LEFT_NAME = "namadega"; 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 public void testNamadega() { String left = LEFT_NAME; String right = RIGHT_NAME; - assertTrue(match(MatchTypeEnum.COLOGNE, left, right)); - assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, left, right)); - assertTrue(match(MatchTypeEnum.MATCH_RATING_APPROACH, left, right)); - assertTrue(match(MatchTypeEnum.METAPHONE, left, right)); - assertTrue(match(MatchTypeEnum.SOUNDEX, left, right)); + assertTrue(match(MdmMatcherEnum.COLOGNE, left, right)); + assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, left, right)); + assertTrue(match(MdmMatcherEnum.MATCH_RATING_APPROACH, left, right)); + assertTrue(match(MdmMatcherEnum.METAPHONE, left, right)); + assertTrue(match(MdmMatcherEnum.SOUNDEX, left, right)); + assertTrue(match(MdmMatcherEnum.METAPHONE, left, right)); - assertFalse(match(MatchTypeEnum.CAVERPHONE1, left, right)); - assertFalse(match(MatchTypeEnum.CAVERPHONE2, left, right)); - assertFalse(match(MatchTypeEnum.NYSIIS, left, right)); - assertFalse(match(MatchTypeEnum.REFINED_SOUNDEX, left, right)); - assertFalse(match(MatchTypeEnum.STRING, left, right)); - assertFalse(match(MatchTypeEnum.SUBSTRING, left, right)); + assertFalse(match(MdmMatcherEnum.CAVERPHONE1, left, right)); + assertFalse(match(MdmMatcherEnum.CAVERPHONE2, left, right)); + assertFalse(match(MdmMatcherEnum.NYSIIS, left, right)); + assertFalse(match(MdmMatcherEnum.REFINED_SOUNDEX, left, right)); + assertFalse(match(MdmMatcherEnum.STRING, left, right)); + assertFalse(match(MdmMatcherEnum.SUBSTRING, left, right)); } @Test public void testNumeric() { - assertTrue(match(MatchTypeEnum.NUMERIC, "4169671111", "(416) 967-1111")); - assertFalse(match(MatchTypeEnum.NUMERIC, "5169671111", "(416) 967-1111")); - assertFalse(match(MatchTypeEnum.NUMERIC, "4169671111", "(416) 967-1111x123")); + assertTrue(match(MdmMatcherEnum.NUMERIC, "4169671111", "(416) 967-1111")); + assertFalse(match(MdmMatcherEnum.NUMERIC, "5169671111", "(416) 967-1111")); + assertFalse(match(MdmMatcherEnum.NUMERIC, "4169671111", "(416) 967-1111x123")); } @Test public void testMetaphone() { - assertTrue(match(MatchTypeEnum.METAPHONE, "Durie", "dury")); - assertTrue(match(MatchTypeEnum.METAPHONE, "Balo", "ballo")); - assertTrue(match(MatchTypeEnum.METAPHONE, "Hans Peter", "Hanspeter")); - assertTrue(match(MatchTypeEnum.METAPHONE, "Lawson", "Law son")); + assertTrue(match(MdmMatcherEnum.METAPHONE, "Durie", "dury")); + assertTrue(match(MdmMatcherEnum.METAPHONE, "Balo", "ballo")); + assertTrue(match(MdmMatcherEnum.METAPHONE, "Hans Peter", "Hanspeter")); + assertTrue(match(MdmMatcherEnum.METAPHONE, "Lawson", "Law son")); - assertFalse(match(MatchTypeEnum.METAPHONE, "Allsop", "Allsob")); - assertFalse(match(MatchTypeEnum.METAPHONE, "Gevne", "Geve")); - assertFalse(match(MatchTypeEnum.METAPHONE, "Bruce", "Bruch")); - assertFalse(match(MatchTypeEnum.METAPHONE, "Smith", "Schmidt")); - assertFalse(match(MatchTypeEnum.METAPHONE, "Jyothi", "Jyoti")); + assertFalse(match(MdmMatcherEnum.METAPHONE, "Allsop", "Allsob")); + assertFalse(match(MdmMatcherEnum.METAPHONE, "Gevne", "Geve")); + assertFalse(match(MdmMatcherEnum.METAPHONE, "Bruce", "Bruch")); + assertFalse(match(MdmMatcherEnum.METAPHONE, "Smith", "Schmidt")); + assertFalse(match(MdmMatcherEnum.METAPHONE, "Jyothi", "Jyoti")); } @Test public void testDoubleMetaphone() { - assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Durie", "dury")); - assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Balo", "ballo")); - assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Hans Peter", "Hanspeter")); - assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Lawson", "Law son")); - assertTrue(match(MatchTypeEnum.DOUBLE_METAPHONE, "Allsop", "Allsob")); + assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Durie", "dury")); + assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Balo", "ballo")); + assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Hans Peter", "Hanspeter")); + assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Lawson", "Law son")); + assertTrue(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Allsop", "Allsob")); - assertFalse(match(MatchTypeEnum.DOUBLE_METAPHONE, "Gevne", "Geve")); - assertFalse(match(MatchTypeEnum.DOUBLE_METAPHONE, "Bruce", "Bruch")); - assertFalse(match(MatchTypeEnum.DOUBLE_METAPHONE, "Smith", "Schmidt")); - assertFalse(match(MatchTypeEnum.DOUBLE_METAPHONE, "Jyothi", "Jyoti")); + assertFalse(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Gevne", "Geve")); + assertFalse(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Bruce", "Bruch")); + assertFalse(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Smith", "Schmidt")); + assertFalse(match(MdmMatcherEnum.DOUBLE_METAPHONE, "Jyothi", "Jyoti")); } @Test public void testNormalizeCase() { - assertTrue(match(MatchTypeEnum.STRING, "joe", "JoE")); - assertTrue(match(MatchTypeEnum.STRING, "MCTAVISH", "McTavish")); + assertTrue(match(MdmMatcherEnum.STRING, "joe", "JoE")); + assertTrue(match(MdmMatcherEnum.STRING, "MCTAVISH", "McTavish")); - assertFalse(match(MatchTypeEnum.STRING, "joey", "joe")); - assertFalse(match(MatchTypeEnum.STRING, "joe", "joey")); + assertFalse(match(MdmMatcherEnum.STRING, "joey", "joe")); + assertFalse(match(MdmMatcherEnum.STRING, "joe", "joey")); } @Test public void testExactString() { - myMdmMatcherJson.setExact(true); + assertTrue(MdmMatcherEnum.STRING.match(ourFhirContext, new StringType("Jilly"), new StringType("Jilly"), true, null)); - assertTrue(getFieldMatcher(MatchTypeEnum.STRING).matches(new StringType("Jilly"), new StringType("Jilly"), myMdmMatcherJson)); - - assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(new StringType("MCTAVISH"), new StringType("McTavish"), myMdmMatcherJson)); - assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(new StringType("Durie"), new StringType("dury"), myMdmMatcherJson)); + assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new StringType("MCTAVISH"), new StringType("McTavish"), true, null)); + assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new StringType("Durie"), new StringType("dury"), true, null)); } @Test public void testExactBoolean() { - myMdmMatcherJson.setExact(true); + assertTrue(MdmMatcherEnum.STRING.match(ourFhirContext, new BooleanType(true), new BooleanType(true), true, null)); - assertTrue(getFieldMatcher(MatchTypeEnum.STRING).matches(new BooleanType(true), new BooleanType(true), myMdmMatcherJson)); - - assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(new BooleanType(true), new BooleanType(false), myMdmMatcherJson)); - assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(new BooleanType(false), new BooleanType(true), myMdmMatcherJson)); + assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new BooleanType(true), new BooleanType(false), true, null)); + assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new BooleanType(false), new BooleanType(true), true, null)); } @Test public void testExactDateString() { - myMdmMatcherJson.setExact(true); + assertTrue(MdmMatcherEnum.STRING.match(ourFhirContext, new DateType("1965-08-09"), new DateType("1965-08-09"), 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)); + assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, new DateType("1965-08-09"), new DateType("1965-09-08"), true, null)); } @@ -146,55 +112,52 @@ public class StringMatcherR4Test extends BaseMatcherR4Test { Enumeration female = new Enumeration(new Enumerations.AdministrativeGenderEnumFactory()); female.setValue(Enumerations.AdministrativeGender.FEMALE); - myMdmMatcherJson.setExact(true); + assertTrue(MdmMatcherEnum.STRING.match(ourFhirContext, male, male, true, null)); - assertTrue(getFieldMatcher(MatchTypeEnum.STRING).matches(male, male, myMdmMatcherJson)); - - assertFalse(getFieldMatcher(MatchTypeEnum.STRING).matches(male, female, myMdmMatcherJson)); + assertFalse(MdmMatcherEnum.STRING.match(ourFhirContext, male, female, true, null)); } @Test public void testSoundex() { - assertTrue(match(MatchTypeEnum.SOUNDEX, "Gail", "Gale")); - assertTrue(match(MatchTypeEnum.SOUNDEX, "John", "Jon")); - assertTrue(match(MatchTypeEnum.SOUNDEX, "Thom", "Tom")); + assertTrue(match(MdmMatcherEnum.SOUNDEX, "Gail", "Gale")); + assertTrue(match(MdmMatcherEnum.SOUNDEX, "John", "Jon")); + assertTrue(match(MdmMatcherEnum.SOUNDEX, "Thom", "Tom")); - assertFalse(match(MatchTypeEnum.SOUNDEX, "Fred", "Frank")); - assertFalse(match(MatchTypeEnum.SOUNDEX, "Thomas", "Tom")); + assertFalse(match(MdmMatcherEnum.SOUNDEX, "Fred", "Frank")); + assertFalse(match(MdmMatcherEnum.SOUNDEX, "Thomas", "Tom")); } @Test public void testCaverphone1() { - assertTrue(match(MatchTypeEnum.CAVERPHONE1, "Gail", "Gael")); - assertTrue(match(MatchTypeEnum.CAVERPHONE1, "John", "Jon")); + assertTrue(match(MdmMatcherEnum.CAVERPHONE1, "Gail", "Gael")); + assertTrue(match(MdmMatcherEnum.CAVERPHONE1, "John", "Jon")); - assertFalse(match(MatchTypeEnum.CAVERPHONE1, "Gail", "Gale")); - assertFalse(match(MatchTypeEnum.CAVERPHONE1, "Fred", "Frank")); - assertFalse(match(MatchTypeEnum.CAVERPHONE1, "Thomas", "Tom")); + assertFalse(match(MdmMatcherEnum.CAVERPHONE1, "Gail", "Gale")); + assertFalse(match(MdmMatcherEnum.CAVERPHONE1, "Fred", "Frank")); + assertFalse(match(MdmMatcherEnum.CAVERPHONE1, "Thomas", "Tom")); } @Test public void testCaverphone2() { - assertTrue(match(MatchTypeEnum.CAVERPHONE2, "Gail", "Gael")); - assertTrue(match(MatchTypeEnum.CAVERPHONE2, "John", "Jon")); - assertTrue(match(MatchTypeEnum.CAVERPHONE2, "Gail", "Gale")); + assertTrue(match(MdmMatcherEnum.CAVERPHONE2, "Gail", "Gael")); + assertTrue(match(MdmMatcherEnum.CAVERPHONE2, "John", "Jon")); + assertTrue(match(MdmMatcherEnum.CAVERPHONE2, "Gail", "Gale")); - assertFalse(match(MatchTypeEnum.CAVERPHONE2, "Fred", "Frank")); - assertFalse(match(MatchTypeEnum.CAVERPHONE2, "Thomas", "Tom")); + assertFalse(match(MdmMatcherEnum.CAVERPHONE2, "Fred", "Frank")); + assertFalse(match(MdmMatcherEnum.CAVERPHONE2, "Thomas", "Tom")); } @Test public void testNormalizeSubstring() { - assertTrue(match(MatchTypeEnum.SUBSTRING, "BILLY", "Bill")); - assertTrue(match(MatchTypeEnum.SUBSTRING, "Bill", "Billy")); - assertTrue(match(MatchTypeEnum.SUBSTRING, "FRED", "Frederik")); + assertTrue(match(MdmMatcherEnum.SUBSTRING, "BILLY", "Bill")); + assertTrue(match(MdmMatcherEnum.SUBSTRING, "Bill", "Billy")); + assertTrue(match(MdmMatcherEnum.SUBSTRING, "FRED", "Frederik")); - assertFalse(match(MatchTypeEnum.SUBSTRING, "Fred", "Friederik")); + assertFalse(match(MdmMatcherEnum.SUBSTRING, "Fred", "Friederik")); } - private boolean match(MatchTypeEnum theMatcher, String theLeft, String theRight) { - return getFieldMatcher(theMatcher) - .matches(new StringType(theLeft), new StringType(theRight), myMdmMatcherJson); + private boolean match(MdmMatcherEnum theMatcher, String theLeft, String theRight) { + return theMatcher.match(ourFhirContext, new StringType(theLeft), new StringType(theRight), false, null); } } diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/BaseMdmRulesR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/BaseMdmRulesR4Test.java index 4ef383b9767..ce8721b97eb 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/BaseMdmRulesR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/BaseMdmRulesR4Test.java @@ -29,7 +29,6 @@ public abstract class BaseMdmRulesR4Test extends BaseR4Test { @BeforeEach public void before() { - super.before(); myMdmRulesJson = new MdmRulesJson(); ArrayList myLegalMdmTypes = new ArrayList<>(); diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/CustomResourceMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/CustomResourceMatcherR4Test.java index d46e225119f..7232394673e 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/CustomResourceMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/CustomResourceMatcherR4Test.java @@ -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.MdmMatcherJson; import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; -import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum; +import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.BeforeAll; @@ -20,7 +20,7 @@ import static org.mockito.Mockito.when; public class CustomResourceMatcherR4Test extends BaseR4Test { - public static final String FIELD_EXACT_MATCH_NAME = MatchTypeEnum.NAME_ANY_ORDER.name(); + public static final String FIELD_EXACT_MATCH_NAME = MdmMatcherEnum.NAME_ANY_ORDER.name(); private static Patient ourJohnHenry; private static Patient ourJohnHENRY; private static Patient ourJaneHenry; @@ -32,7 +32,6 @@ public class CustomResourceMatcherR4Test extends BaseR4Test { @BeforeEach public void before() { - super.before(); when(mySearchParamRetriever.getActiveSearchParam("Patient", "identifier")).thenReturn(mock(RuntimeSearchParam.class)); when(mySearchParamRetriever.getActiveSearchParam("Practitioner", "identifier")).thenReturn(mock(RuntimeSearchParam.class)); when(mySearchParamRetriever.getActiveSearchParam("Medication", "identifier")).thenReturn(mock(RuntimeSearchParam.class)); @@ -41,8 +40,7 @@ public class CustomResourceMatcherR4Test extends BaseR4Test { @Test public void testExactNameAnyOrder() { - MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MatchTypeEnum.NAME_ANY_ORDER, true)); - + MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MdmMatcherEnum.NAME_ANY_ORDER, true)); assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry)); assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn)); assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJOHN)); @@ -55,8 +53,7 @@ public class CustomResourceMatcherR4Test extends BaseR4Test { @Test public void testNormalizedNameAnyOrder() { - MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MatchTypeEnum.NAME_ANY_ORDER, false)); - + MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MdmMatcherEnum.NAME_ANY_ORDER, false)); assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry)); assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn)); assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJOHN)); @@ -69,8 +66,7 @@ public class CustomResourceMatcherR4Test extends BaseR4Test { @Test public void testExactNameFirstAndLast() { - MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MatchTypeEnum.NAME_FIRST_AND_LAST, true)); - + MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MdmMatcherEnum.NAME_FIRST_AND_LAST, true)); assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry)); assertMatchResult(MdmMatchResultEnum.MATCH, 1L, 1.0, false, false, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry)); assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn)); @@ -84,8 +80,7 @@ public class CustomResourceMatcherR4Test extends BaseR4Test { @Test public void testNormalizedNameFirstAndLast() { - MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MatchTypeEnum.NAME_FIRST_AND_LAST, false)); - + MdmResourceMatcherSvc nameAnyOrderMatcher = buildMatcher(buildNameRules(MdmMatcherEnum.NAME_FIRST_AND_LAST, false)); assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourJohnHenry)); assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJohn)); assertMatch(MdmMatchResultEnum.NO_MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourHenryJOHN)); @@ -96,7 +91,7 @@ public class CustomResourceMatcherR4Test extends BaseR4Test { assertMatch(MdmMatchResultEnum.MATCH, nameAnyOrderMatcher.match(ourJohnHenry, ourBillyJohnHenry)); } - private MdmRulesJson buildNameRules(MatchTypeEnum theAlgorithm, boolean theExact) { + private MdmRulesJson buildNameRules(MdmMatcherEnum theAlgorithm, boolean theExact) { MdmMatcherJson matcherJson = new MdmMatcherJson().setAlgorithm(theAlgorithm).setExact(theExact); MdmFieldMatchJson nameAnyOrderFieldMatch = new MdmFieldMatchJson() .setName(FIELD_EXACT_MATCH_NAME) diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/FhirPathResourceMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/FhirPathResourceMatcherR4Test.java index d46a0d06776..b482fcd71ca 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/FhirPathResourceMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/FhirPathResourceMatcherR4Test.java @@ -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.MdmMatcherJson; import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; -import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum; +import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.BeforeEach; @@ -46,9 +46,10 @@ public class FhirPathResourceMatcherR4Test extends BaseMdmRulesR4Test { } } + @Test public void testFhirPathOrderedMatches() { - MdmResourceMatcherSvc matcherSvc = buildMatcher(buildOrderedGivenNameRules(MatchTypeEnum.STRING)); + MdmResourceMatcherSvc matcherSvc = buildMatcher(buildOrderedGivenNameRules(MdmMatcherEnum.STRING)); myLeft = new Patient(); HumanName name = myLeft.addName(); @@ -62,7 +63,6 @@ public class FhirPathResourceMatcherR4Test extends BaseMdmRulesR4Test { name2.addGiven("Gary"); myRight.setId("Patient/2"); - // test MdmMatchOutcome result = matcherSvc.match(myLeft, myRight); assertMatchResult(MdmMatchResultEnum.NO_MATCH, 0L, 0.0, false, false, result); @@ -85,12 +85,12 @@ public class FhirPathResourceMatcherR4Test extends BaseMdmRulesR4Test { @Test public void testStringMatchResult() { - MdmResourceMatcherSvc matcherSvc = buildMatcher(buildOrderedGivenNameRules(MatchTypeEnum.STRING)); + MdmResourceMatcherSvc matcherSvc = buildMatcher(buildOrderedGivenNameRules(MdmMatcherEnum.STRING)); MdmMatchOutcome result = matcherSvc.match(myLeft, myRight); assertMatchResult(MdmMatchResultEnum.NO_MATCH, 0L, 0.0, false, false, result); } - protected MdmRulesJson buildOrderedGivenNameRules(MatchTypeEnum theMatcherEnum) { + protected MdmRulesJson buildOrderedGivenNameRules(MdmMatcherEnum theMatcherEnum) { MdmFieldMatchJson firstGivenNameMatchField = new MdmFieldMatchJson() .setName(PATIENT_GIVEN_FIRST) .setResourceType("Patient") diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceFieldMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceFieldMatcherR4Test.java index c47be74ba27..c00841ca708 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceFieldMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceFieldMatcherR4Test.java @@ -3,8 +3,10 @@ package ca.uhn.fhir.mdm.rules.svc; 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.matcher.fieldmatchers.EmptyFieldMatcher; -import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum; +import ca.uhn.fhir.mdm.rules.json.MdmSimilarityJson; +import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum; +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.Patient; import org.junit.jupiter.api.BeforeEach; @@ -12,12 +14,12 @@ import org.junit.jupiter.api.Test; 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.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; 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 { protected MdmResourceFieldMatcher myComparator; @@ -28,13 +30,7 @@ public class MdmResourceFieldMatcherR4Test extends BaseMdmRulesR4Test { @BeforeEach public void before() { super.before(); - - myComparator = new MdmResourceFieldMatcher( - ourFhirContext, - myIMatcherFactory, - myGivenNameMatchField, - myMdmRulesJson - ); + myComparator = new MdmResourceFieldMatcher(ourFhirContext, myGivenNameMatchField, myMdmRulesJson); myJohn = buildJohn(); myJohny = buildJohny(); } @@ -48,13 +44,8 @@ public class MdmResourceFieldMatcherR4Test extends BaseMdmRulesR4Test { .setName("empty-given") .setResourceType("Patient") .setResourcePath("name.given") - .setMatcher(new MdmMatcherJson().setAlgorithm(MatchTypeEnum.EMPTY_FIELD)); - myComparator = new MdmResourceFieldMatcher( - ourFhirContext, - myIMatcherFactory, - myGivenNameMatchField, - myMdmRulesJson - ); + .setMatcher(new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.EMPTY_FIELD)); + myComparator = new MdmResourceFieldMatcher(ourFhirContext, myGivenNameMatchField, myMdmRulesJson); assertFalse(myComparator.match(myJohn, myJohny).match); @@ -99,9 +90,6 @@ 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 public void testMatch() { assertTrue(myComparator.match(myJohn, myJohny).match); diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceMatcherSvcR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceMatcherSvcR4Test.java index 61045b37671..912348f5980 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceMatcherSvcR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/MdmResourceMatcherSvcR4Test.java @@ -36,6 +36,7 @@ public class MdmResourceMatcherSvcR4Test extends BaseMdmRulesR4Test { public void testCompareFirstNameMatch() { MdmMatchOutcome result = myMdmResourceMatcherSvc.match(myJohn, myJohny); assertMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH, 1L, 0.816, false, false, result); + } @Test diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/ResourceMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/ResourceMatcherR4Test.java index 8fa816bc5f1..8a2342c9398 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/ResourceMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/svc/ResourceMatcherR4Test.java @@ -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.MdmMatcherJson; import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; -import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum; +import ca.uhn.fhir.mdm.rules.matcher.MdmMatcherEnum; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.BeforeEach; @@ -55,21 +55,19 @@ public class ResourceMatcherR4Test extends BaseMdmRulesR4Test { @Test public void testMetaphoneMatchResult() { - MdmResourceMatcherSvc matcherSvc = buildMatcher(buildNamePhoneRules(MatchTypeEnum.METAPHONE)); - - MdmMatchOutcome result = matcherSvc.match(myLeft, myRight); - assertMatchResult(MdmMatchResultEnum.MATCH, 7L, 3.0, false, false, result); + MdmResourceMatcherSvc matcherSvc = buildMatcher(buildNamePhoneRules(MdmMatcherEnum.METAPHONE)); + MdmMatchOutcome result = matcherSvc.match(myLeft, myRight); + assertMatchResult(MdmMatchResultEnum.MATCH, 7L, 3.0, false, false, result); } @Test public void testStringMatchResult() { - MdmResourceMatcherSvc matcherSvc = buildMatcher(buildNamePhoneRules(MatchTypeEnum.STRING)); - + MdmResourceMatcherSvc matcherSvc = buildMatcher(buildNamePhoneRules(MdmMatcherEnum.STRING)); MdmMatchOutcome result = matcherSvc.match(myLeft, myRight); assertMatchResult(MdmMatchResultEnum.NO_MATCH, 5L, 2.0, false, false, result); } - protected MdmRulesJson buildNamePhoneRules(MatchTypeEnum theMatcherEnum) { + protected MdmRulesJson buildNamePhoneRules(MdmMatcherEnum theMatcherEnum) { MdmFieldMatchJson lastNameMatchField = new MdmFieldMatchJson() .setName(PATIENT_FAMILY) .setResourceType("Patient") @@ -86,7 +84,7 @@ public class ResourceMatcherR4Test extends BaseMdmRulesR4Test { .setName(PATIENT_PHONE) .setResourceType("Patient") .setResourcePath("telecom.value") - .setMatcher(new MdmMatcherJson().setAlgorithm(MatchTypeEnum.STRING)); + .setMatcher(new MdmMatcherJson().setAlgorithm(MdmMatcherEnum.STRING)); MdmRulesJson retval = new MdmRulesJson(); retval.setVersion("test version"); diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 013b414d0d7..f6e4d2c5da4 100644 --- a/hapi-fhir-storage-batch2-test-utilities/pom.xml +++ b/hapi-fhir-storage-batch2-test-utilities/pom.xml @@ -55,6 +55,11 @@ junit-jupiter-params compile + + org.hamcrest + hamcrest + compile + org.mockito mockito-core