1010import java .util .EnumSet ;
1111import java .util .List ;
1212import java .util .Objects ;
13- import java .util .function .Consumer ;
13+ import java .util .function .Function ;
1414import java .util .regex .Matcher ;
1515import java .util .regex .Pattern ;
1616import java .util .stream .Collectors ;
2727
2828import ca .uhn .fhir .context .FhirContext ;
2929import ca .uhn .fhir .parser .DataFormatException ;
30+ import ca .uhn .fhir .rest .api .Constants ;
3031import de .netzwerk_universitaetsmedizin .codex .processes .data_transfer .ConstantsDataTransfer ;
3132import de .netzwerk_universitaetsmedizin .codex .processes .data_transfer .domain .DateWithPrecision ;
3233import de .netzwerk_universitaetsmedizin .codex .processes .data_transfer .variables .PseudonymList ;
@@ -104,7 +105,7 @@ public FhirClientImpl(HapiFhirClientFactory clientFactory, FhirContext fhirConte
104105 */
105106 private Bundle getSearchBundle (DateWithPrecision exportFrom , Date exportTo )
106107 {
107- return doGetSearchBundle (null , exportFrom , exportTo , true );
108+ return doGetSearchBundle (null , null , exportFrom , exportTo , true );
108109 }
109110
110111 /**
@@ -116,12 +117,30 @@ private Bundle getSearchBundle(DateWithPrecision exportFrom, Date exportTo)
116117 * not <code>null</code>
117118 * @return
118119 */
119- private Bundle getSearchBundle (String pseudonym , DateWithPrecision exportFrom , Date exportTo )
120+ private Bundle getSearchBundleWithPseudonym (String pseudonym , DateWithPrecision exportFrom , Date exportTo )
120121 {
121- return doGetSearchBundle (pseudonym , exportFrom , exportTo , false );
122+ Objects .requireNonNull (pseudonym , "pseudonym" );
123+
124+ return doGetSearchBundle (null , pseudonym , exportFrom , exportTo , false );
122125 }
123126
124- private Bundle doGetSearchBundle (String pseudonym , DateWithPrecision exportFrom , Date exportTo ,
127+ /**
128+ * @param patientId
129+ * not <code>null</code>
130+ * @param exportFrom
131+ * may be <code>null</code>
132+ * @param exportTo
133+ * not <code>null</code>
134+ * @return
135+ */
136+ private Bundle getSearchBundleWithPatientId (String patientId , DateWithPrecision exportFrom , Date exportTo )
137+ {
138+ Objects .requireNonNull (patientId , "patientId" );
139+
140+ return doGetSearchBundle (patientId , null , exportFrom , exportTo , false );
141+ }
142+
143+ private Bundle doGetSearchBundle (String patientId , String pseudonym , DateWithPrecision exportFrom , Date exportTo ,
125144 boolean includePatient )
126145 {
127146 Objects .requireNonNull (exportTo , "exportTo" );
@@ -134,13 +153,17 @@ private Bundle doGetSearchBundle(String pseudonym, DateWithPrecision exportFrom,
134153 throw new RuntimeException ("Search-Bundle type not batch or transaction" );
135154 }
136155
137- bundle .getEntry ().forEach (modifySearchUrl (pseudonym , exportFrom , exportTo , includePatient ));
156+ List <BundleEntryComponent > entries = bundle .getEntry ().stream ()
157+ .map (modifySearchUrl (patientId , pseudonym , exportFrom , exportTo , includePatient )).filter (e -> e != null )
158+ .collect (Collectors .toList ());
159+
160+ bundle .setEntry (entries );
138161
139162 return bundle ;
140163 }
141164
142- private Consumer <? super BundleEntryComponent > modifySearchUrl (String pseudonym , DateWithPrecision exportFrom ,
143- Date exportTo , boolean includePatient )
165+ private Function < BundleEntryComponent , BundleEntryComponent > modifySearchUrl (String patientId , String pseudonym ,
166+ DateWithPrecision exportFrom , Date exportTo , boolean includePatient )
144167 {
145168 return entry ->
146169 {
@@ -163,13 +186,20 @@ private Consumer<? super BundleEntryComponent> modifySearchUrl(String pseudonym,
163186
164187 if (RESOURCES_WITH_PATIENT_REF .contains (resource ))
165188 {
166- query += createPatPrefixPseudonymSearchUrlPart (pseudonym );
189+ if (patientId != null )
190+ query += createPatIdSearchUrlPart (patientId );
191+ else
192+ query += createPatPrefixPseudonymSearchUrlPart (pseudonym );
167193
168194 if (includePatient )
169195 query += createIncludeSearchUrlPart (resource );
170196 }
171197 else if ("Patient" .equals (resource ))
172198 {
199+ // filtering search for patient if patient id known
200+ if (patientId != null )
201+ return null ;
202+
173203 query += createPseudonymSearchUrlPart (pseudonym );
174204 }
175205 else
@@ -185,6 +215,8 @@ else if ("Patient".equals(resource))
185215 query += createExportToSearchUrlPart (exportTo );
186216
187217 entry .getRequest ().setUrl (resource + query );
218+
219+ return entry ;
188220 };
189221 }
190222
@@ -196,6 +228,14 @@ private String createPseudonymSearchUrlPart(String pseudonym)
196228 return "" ;
197229 }
198230
231+ private String createPatIdSearchUrlPart (String patientId )
232+ {
233+ if (patientId != null && !patientId .isBlank ())
234+ return "&patient=" + patientId ;
235+ else
236+ return "" ;
237+ }
238+
199239 private String createPatPrefixPseudonymSearchUrlPart (String pseudonym )
200240 {
201241 if (pseudonym != null && !pseudonym .isBlank ())
@@ -279,7 +319,7 @@ private Bundle readSerachBundleTemplate()
279319 {
280320 try (InputStream in = FhirClientImpl .class .getResourceAsStream ("/fhir/Bundle/SearchBundle.xml" ))
281321 {
282- logger .warn ("Using internal Search-Bundle" );
322+ logger .info ("Using internal Search-Bundle" );
283323 return fhirContext .newXmlParser ().parseResource (Bundle .class , in );
284324 }
285325
@@ -301,7 +341,8 @@ public PseudonymList getPseudonymsWithNewData(DateWithPrecision exportFrom, Date
301341 logger .debug ("Executing Search-Bundle: {}" ,
302342 fhirContext .newJsonParser ().encodeResourceToString (searchBundle ));
303343
304- Bundle resultBundle = clientFactory .getFhirStoreClient ().transaction ().withBundle (searchBundle ).execute ();
344+ Bundle resultBundle = clientFactory .getFhirStoreClient ().transaction ().withBundle (searchBundle )
345+ .withAdditionalHeader (Constants .HEADER_PREFER , "handling=strict" ).execute ();
305346
306347 if (logger .isDebugEnabled ())
307348 logger .debug ("Search-Bundle result: {}" , fhirContext .newJsonParser ().encodeResourceToString (resultBundle ));
@@ -352,7 +393,8 @@ private Bundle continueSearch(String url)
352393 if (logger .isDebugEnabled ())
353394 logger .debug ("Executing search: {}" , url );
354395
355- Bundle resultBundle = (Bundle ) clientFactory .getFhirStoreClient ().search ().byUrl (url ).execute ();
396+ Bundle resultBundle = (Bundle ) clientFactory .getFhirStoreClient ().search ().byUrl (url )
397+ .withAdditionalHeader (Constants .HEADER_PREFER , "handling=strict" ).execute ();
356398
357399 if (logger .isDebugEnabled ())
358400 logger .debug ("Search-Bundle result: {}" , fhirContext .newJsonParser ().encodeResourceToString (resultBundle ));
@@ -363,13 +405,23 @@ private Bundle continueSearch(String url)
363405 @ Override
364406 public Stream <DomainResource > getNewData (String pseudonym , DateWithPrecision exportFrom , Date exportTo )
365407 {
366- Bundle searchBundle = getSearchBundle (pseudonym , exportFrom , exportTo );
408+ if (clientFactory .supportsIdentifierReferenceSearch ())
409+ return getNewDataWithIdentifierReferenceSupport (pseudonym , exportFrom , exportTo );
410+ else
411+ return getNewDataWithoutIdentifierReferenceSupport (pseudonym , exportFrom , exportTo );
412+ }
413+
414+ private Stream <DomainResource > getNewDataWithIdentifierReferenceSupport (String pseudonym ,
415+ DateWithPrecision exportFrom , Date exportTo )
416+ {
417+ Bundle searchBundle = getSearchBundleWithPseudonym (pseudonym , exportFrom , exportTo );
367418
368419 if (logger .isDebugEnabled ())
369420 logger .debug ("Executing Search-Bundle: {}" ,
370421 fhirContext .newJsonParser ().encodeResourceToString (searchBundle ));
371422
372- Bundle resultBundle = clientFactory .getFhirStoreClient ().transaction ().withBundle (searchBundle ).execute ();
423+ Bundle resultBundle = clientFactory .getFhirStoreClient ().transaction ().withBundle (searchBundle )
424+ .withAdditionalHeader (Constants .HEADER_PREFER , "handling=strict" ).execute ();
373425
374426 if (logger .isDebugEnabled ())
375427 logger .debug ("Search-Bundle result: {}" , fhirContext .newJsonParser ().encodeResourceToString (resultBundle ));
@@ -378,6 +430,45 @@ public Stream<DomainResource> getNewData(String pseudonym, DateWithPrecision exp
378430 .map (e -> (Bundle ) e .getResource ()).flatMap (this ::getDomainResources );
379431 }
380432
433+ private Stream <DomainResource > getNewDataWithoutIdentifierReferenceSupport (String pseudonym ,
434+ DateWithPrecision exportFrom , Date exportTo )
435+ {
436+ Bundle patientBundle = (Bundle ) clientFactory .getFhirStoreClient ().search ().forResource (Patient .class )
437+ .where (Patient .IDENTIFIER .exactly ()
438+ .systemAndIdentifier (ConstantsDataTransfer .NAMING_SYSTEM_NUM_CODEX_DIC_PSEUDONYM , pseudonym ))
439+ .execute ();
440+
441+ if (logger .isDebugEnabled ())
442+ logger .debug ("Patient search-bundle result: {}" ,
443+ fhirContext .newJsonParser ().encodeResourceToString (patientBundle ));
444+
445+ if (patientBundle .getTotal () != 1 || !(patientBundle .getEntryFirstRep ().getResource () instanceof Patient ))
446+ {
447+ logger .warn (
448+ "Error while retrieving patient for pseudonym {}, result bundle total not 1 or first entry not patient" ,
449+ pseudonym );
450+ throw new RuntimeException ("Error while retrieving patient for pseudonym " + pseudonym );
451+ }
452+
453+ Patient patient = (Patient ) patientBundle .getEntryFirstRep ().getResource ();
454+
455+ Bundle searchBundle = getSearchBundleWithPatientId (patient .getIdElement ().getIdPart (), exportFrom , exportTo );
456+
457+ if (logger .isDebugEnabled ())
458+ logger .debug ("Executing Search-Bundle: {}" ,
459+ fhirContext .newJsonParser ().encodeResourceToString (searchBundle ));
460+
461+ Bundle resultBundle = clientFactory .getFhirStoreClient ().transaction ().withBundle (searchBundle )
462+ .withAdditionalHeader (Constants .HEADER_PREFER , "handling=strict" ).execute ();
463+
464+ if (logger .isDebugEnabled ())
465+ logger .debug ("Search-Bundle result: {}" , fhirContext .newJsonParser ().encodeResourceToString (resultBundle ));
466+
467+ return Stream .concat (Stream .of (patient ),
468+ resultBundle .getEntry ().stream ().filter (e -> e .hasResource () && e .getResource () instanceof Bundle )
469+ .map (e -> (Bundle ) e .getResource ()).flatMap (this ::getDomainResources ));
470+ }
471+
381472 private Stream <DomainResource > getDomainResources (Bundle bundle )
382473 {
383474 Stream <DomainResource > domainResources = getDomainResourcesFromBundle (bundle );
@@ -392,7 +483,7 @@ private Stream<DomainResource> getDomainResources(Bundle bundle)
392483 private Stream <DomainResource > getDomainResourcesFromBundle (Bundle bundle )
393484 {
394485 return bundle .getEntry ().stream ().filter (e -> e .hasResource () && e .getResource () instanceof DomainResource )
395- .map (e -> (Patient ) e .getResource ());
486+ .map (e -> (DomainResource ) e .getResource ());
396487 }
397488
398489 private Stream <DomainResource > doGetDomainResources (String nextUrl , int subTotal )
@@ -410,6 +501,7 @@ private Stream<DomainResource> doGetDomainResources(String nextUrl, int subTotal
410501 @ Override
411502 public void storeBundle (Bundle bundle )
412503 {
413- clientFactory .getFhirStoreClient ().transaction ().withBundle (bundle ).execute ();
504+ clientFactory .getFhirStoreClient ().transaction ().withBundle (bundle )
505+ .withAdditionalHeader (Constants .HEADER_PREFER , "handling=strict" ).execute ();
414506 }
415507}
0 commit comments