001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.dao.mdm; 021 022import ca.uhn.fhir.jpa.api.dao.DaoRegistry; 023import ca.uhn.fhir.jpa.dao.data.IMdmLinkJpaMetricsRepository; 024import ca.uhn.fhir.mdm.api.BaseMdmMetricSvc; 025import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; 026import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; 027import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; 028import ca.uhn.fhir.mdm.model.MdmLinkMetrics; 029import ca.uhn.fhir.mdm.model.MdmLinkScoreMetrics; 030import ca.uhn.fhir.mdm.model.MdmMetrics; 031import ca.uhn.fhir.mdm.model.MdmResourceMetrics; 032import jakarta.persistence.EntityManager; 033import jakarta.persistence.EntityManagerFactory; 034import jakarta.persistence.Query; 035import org.springframework.transaction.annotation.Transactional; 036 037import java.math.BigInteger; 038import java.util.Arrays; 039import java.util.List; 040import java.util.stream.Collectors; 041 042public class MdmMetricSvcJpaImpl extends BaseMdmMetricSvc { 043 044 private final IMdmLinkJpaMetricsRepository myJpaRepository; 045 046 private final EntityManagerFactory myEntityManagerFactory; 047 048 public MdmMetricSvcJpaImpl( 049 IMdmLinkJpaMetricsRepository theRepository, 050 DaoRegistry theDaoRegistry, 051 EntityManagerFactory theEntityManagerFactory) { 052 super(theDaoRegistry); 053 myJpaRepository = theRepository; 054 myEntityManagerFactory = theEntityManagerFactory; 055 } 056 057 protected MdmLinkMetrics generateLinkMetrics(GenerateMdmMetricsParameters theParameters) { 058 List<MdmLinkSourceEnum> linkSources = theParameters.getLinkSourceFilters(); 059 List<MdmMatchResultEnum> matchResults = theParameters.getMatchResultFilters(); 060 061 if (linkSources.isEmpty()) { 062 linkSources = Arrays.asList(MdmLinkSourceEnum.values()); 063 } 064 if (matchResults.isEmpty()) { 065 matchResults = Arrays.asList(MdmMatchResultEnum.values()); 066 } 067 068 Object[][] data = myJpaRepository.generateMetrics(theParameters.getResourceType(), linkSources, matchResults); 069 MdmLinkMetrics metrics = new MdmLinkMetrics(); 070 metrics.setResourceType(theParameters.getResourceType()); 071 for (Object[] row : data) { 072 MdmMatchResultEnum matchResult = (MdmMatchResultEnum) row[0]; 073 MdmLinkSourceEnum source = (MdmLinkSourceEnum) row[1]; 074 long count = (Long) row[2]; 075 metrics.addMetric(matchResult, source, count); 076 } 077 return metrics; 078 } 079 080 protected MdmLinkScoreMetrics generateLinkScoreMetrics(GenerateMdmMetricsParameters theParameters) { 081 String resourceType = theParameters.getResourceType(); 082 083 List<MdmMatchResultEnum> matchResultTypes = theParameters.getMatchResultFilters(); 084 085 // if no result type filter, add all result types 086 if (matchResultTypes.isEmpty()) { 087 matchResultTypes = Arrays.asList(MdmMatchResultEnum.values()); 088 } 089 090 String sql = "SELECT %s FROM MPI_LINK ml WHERE ml.TARGET_TYPE = :resourceType " 091 + "AND ml.MATCH_RESULT in (:matchResult)"; 092 093 StringBuilder sb = new StringBuilder(); 094 sb.append("sum(case when ml.SCORE is null then 1 else 0 end) as B_" + NULL_VALUE); 095 096 for (int i = 0; i < BUCKETS; i++) { 097 double bucket = getBucket(i + 1); 098 sb.append(",\n"); 099 if (i == 0) { 100 // score <= .01 101 sb.append(String.format("sum(case when ml.SCORE <= %.2f then 1 else 0 end) as B%d", bucket, i)); 102 } else { 103 // score > i/100 && score <= i/100 104 sb.append(String.format( 105 "sum(case when ml.score > %.2f and ml.SCORE <= %.2f then 1 else 0 end) as B%d", 106 getBucket(i), bucket, i)); 107 } 108 } 109 110 EntityManager em = myEntityManagerFactory.createEntityManager(); 111 112 Query nativeQuery = em.createNativeQuery(String.format(sql, sb.toString())); 113 114 org.hibernate.query.Query<?> hibernateQuery = (org.hibernate.query.Query<?>) nativeQuery; 115 116 hibernateQuery.setParameter("resourceType", resourceType); 117 hibernateQuery.setParameter( 118 "matchResult", matchResultTypes.stream().map(Enum::ordinal).collect(Collectors.toList())); 119 120 List<?> results = hibernateQuery.getResultList(); 121 122 em.close(); 123 124 MdmLinkScoreMetrics metrics = new MdmLinkScoreMetrics(); 125 126 // we only get one row back 127 Object[] row = (Object[]) results.get(0); 128 int length = row.length; 129 for (int i = 0; i < length; i++) { 130 // if there's nothing in the db, these values will all be null 131 Number bi = row[i] != null ? (Number) row[i] : BigInteger.valueOf(0); 132 double bucket = getBucket(i); 133 if (i == 0) { 134 metrics.addScore(NULL_VALUE, bi.longValue()); 135 } else if (i == 1) { 136 metrics.addScore(String.format(FIRST_BUCKET, bucket), bi.longValue()); 137 } else { 138 metrics.addScore(String.format(NTH_BUCKET, getBucket(i - 1), bucket), bi.longValue()); 139 } 140 } 141 142 return metrics; 143 } 144 145 @Transactional 146 @Override 147 public MdmMetrics generateMdmMetrics(GenerateMdmMetricsParameters theParameters) { 148 MdmResourceMetrics resourceMetrics = generateResourceMetrics(theParameters); 149 MdmLinkMetrics linkMetrics = generateLinkMetrics(theParameters); 150 MdmLinkScoreMetrics scoreMetrics = generateLinkScoreMetrics(theParameters); 151 152 MdmMetrics metrics = MdmMetrics.fromSeperableMetrics(resourceMetrics, linkMetrics, scoreMetrics); 153 return metrics; 154 } 155}