
001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2026 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.bulk.export.svc; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.interceptor.model.RequestPartitionId; 024import ca.uhn.fhir.jpa.api.dao.DaoRegistry; 025import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; 026import ca.uhn.fhir.jpa.api.svc.IIdHelperService; 027import ca.uhn.fhir.jpa.api.svc.ResolveIdentityMode; 028import ca.uhn.fhir.jpa.model.dao.JpaPid; 029import ca.uhn.fhir.mdm.svc.IBulkExportMdmEidMatchOnlyResourceExpander; 030import ca.uhn.fhir.mdm.svc.IBulkExportMdmResourceExpander; 031import ca.uhn.fhir.mdm.svc.MdmEidMatchOnlyExpandSvc; 032import ca.uhn.fhir.rest.api.server.SystemRequestDetails; 033import ca.uhn.fhir.util.FhirTerser; 034import org.hl7.fhir.instance.model.api.IBaseReference; 035import org.hl7.fhir.instance.model.api.IBaseResource; 036import org.hl7.fhir.instance.model.api.IIdType; 037 038import java.util.HashSet; 039import java.util.List; 040import java.util.Set; 041import java.util.stream.Collectors; 042 043/** 044 * Implementation of {@link IBulkExportMdmResourceExpander} that handles bulk export resource expansion 045 * when MDM mode is Match-Only and Eid Systems defined in mdm rules. 046 * 047 * <p>This expander is used during bulk export operations to expand Group resources by resolving 048 * MDM matching resources for the members in the group. Resources are 049 * matched based on just eids rather than the full MDM golden resource relationships.</p> 050 */ 051public class BulkExportMdmEidMatchOnlyResourceExpander implements IBulkExportMdmEidMatchOnlyResourceExpander<JpaPid> { 052 053 private final DaoRegistry myDaoRegistry; 054 private final MdmEidMatchOnlyExpandSvc myMdmEidMatchOnlyLinkExpandSvc; 055 private final FhirContext myFhirContext; 056 private final IIdHelperService<JpaPid> myIdHelperService; 057 058 /** 059 * Constructor 060 */ 061 public BulkExportMdmEidMatchOnlyResourceExpander( 062 DaoRegistry theDaoRegistry, 063 MdmEidMatchOnlyExpandSvc theMdmEidMatchOnlyLinkExpandSvc, 064 FhirContext theFhirContext, 065 IIdHelperService<JpaPid> theIdHelperService) { 066 myDaoRegistry = theDaoRegistry; 067 myMdmEidMatchOnlyLinkExpandSvc = theMdmEidMatchOnlyLinkExpandSvc; 068 myFhirContext = theFhirContext; 069 myIdHelperService = theIdHelperService; 070 } 071 072 /** 073 * Expands a Group resource and returns the Group members' resource persistent ids. 074 * The returned ids consists of group members + all MDM matched resources based on EID only. 075 * 076 * <p>This method:</p> 077 * <ol> 078 * <li>Reads the specified Group resource</li> 079 * <li>Extracts all member entity references from the Group</li> 080 * <li>For each member, uses EID matching to find all resources that have the same EID as the member, using eid system specified in mdm rules</li> 081 * <li>Converts the expanded resource IDs to persistent IDs (PIDs)</li> 082 * </ol> 083 * 084 * @param groupResourceId The ID of the Group resource to expand 085 * @param requestPartitionId The request partition ID 086 * @return A set of {@link JpaPid} objects representing all expanded resources 087 */ 088 @Override 089 public Set<JpaPid> expandGroup(String groupResourceId, RequestPartitionId requestPartitionId) { 090 // Read the Group resource 091 SystemRequestDetails srd = SystemRequestDetails.forRequestPartitionId(requestPartitionId); 092 IIdType groupId = myFhirContext.getVersion().newIdType(groupResourceId); 093 IFhirResourceDao<?> groupDao = myDaoRegistry.getResourceDao("Group"); 094 IBaseResource groupResource = groupDao.read(groupId, srd); 095 096 Set<String> allResourceIds = new HashSet<>(); 097 FhirTerser terser = myFhirContext.newTerser(); 098 // Extract all member.entity references from the Group resource 099 List<IBaseReference> memberEntities = 100 terser.getValues(groupResource, "Group.member.entity", IBaseReference.class); 101 // mdm expand each member based on eid 102 for (IBaseReference entityRef : memberEntities) { 103 if (!entityRef.getReferenceElement().isEmpty()) { 104 IIdType memberId = entityRef.getReferenceElement(); 105 Set<String> expanded = 106 myMdmEidMatchOnlyLinkExpandSvc.expandMdmBySourceResourceId(requestPartitionId, memberId); 107 allResourceIds.addAll(expanded); 108 } 109 } 110 // Convert all resourceIds to IIdType and resolve in batch 111 List<IIdType> idTypes = allResourceIds.stream() 112 .map(id -> myFhirContext.getVersion().newIdType(id)) 113 .collect(Collectors.toList()); 114 List<JpaPid> pidList = myIdHelperService.resolveResourcePids( 115 requestPartitionId, 116 idTypes, 117 ResolveIdentityMode.excludeDeleted().cacheOk()); 118 return new HashSet<>(pidList); 119 } 120 121 /** 122 * Expands a single patient ID to include all patients linked via EID matching. 123 * 124 * @param thePatientId Patient ID to expand (e.g., "Patient/123") 125 * @param theRequestPartitionId Partition context for the request 126 * @return Set of String patient IDs including the original patient and all EID-matched patients 127 */ 128 @Override 129 public Set<String> expandPatient(String thePatientId, RequestPartitionId theRequestPartitionId) { 130 IIdType patientIdType = 131 myFhirContext.getVersion().newIdType(thePatientId).withResourceType("Patient"); 132 return myMdmEidMatchOnlyLinkExpandSvc.expandMdmBySourceResourceId(theRequestPartitionId, patientIdType); 133 } 134 135 @Override 136 public void annotateResource(IBaseResource resource) { 137 // This function is normally used to add golden resource id to the exported resources, 138 // but in the Eid-based match only mode, there isn't any golden resource, so nothing to do here 139 } 140}