From 551f945ac6b8847d6ee5d7d7e1206c928fa8b545 Mon Sep 17 00:00:00 2001 From: hmk Date: Wed, 12 Aug 2026 12:10:46 +0000 Subject: [PATCH] Update howto-fhir-date-parameter.md --- howto-fhir-date-parameter.md | 131 +++++++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 21 deletions(-) diff --git a/howto-fhir-date-parameter.md b/howto-fhir-date-parameter.md index afcd44d..d72299e 100644 --- a/howto-fhir-date-parameter.md +++ b/howto-fhir-date-parameter.md @@ -52,23 +52,110 @@ Treffer-Bereich (grün/farbig schattiert): |---|---|---| | `eq` | gleich | Resource-Intervall liegt vollständig im Such-Intervall | | `ne` | ungleich | Komplement von `eq` | -| `gt` | größer als | Resource-Datum beginnt nach dem Ende des Such-Intervalls | -| `lt` | kleiner als | Resource-Datum endet vor dem Beginn des Such-Intervalls | -| `ge` | größer/gleich | Beginnt am oder nach Beginn des Such-Intervalls | -| `le` | kleiner/gleich | Endet am oder vor Ende des Such-Intervalls | -| `sa` | starts after | Beginnt vollständig **nach** dem Ende des Such-Intervalls | -| `eb` | ends before | Endet vollständig **vor** dem Beginn des Such-Intervalls | -| `ap` | approximately | Ungefähre Übereinstimmung, Toleranz serverabhängig | +| `gt` | größer als | Resource-Intervall **ragt über** das Ende des Such-Intervalls hinaus | +| `lt` | kleiner als | Resource-Intervall **beginnt vor** dem Anfang des Such-Intervalls | +| `ge` | größer/gleich | `gt` **oder** vollständig im Such-Intervall enthalten (`eq`) | +| `le` | kleiner/gleich | `lt` **oder** vollständig im Such-Intervall enthalten (`eq`) | +| `sa` | starts after | Beginnt vollständig **nach** dem Ende des Such-Intervalls, keine Überlappung | +| `eb` | ends before | Endet vollständig **vor** dem Beginn des Such-Intervalls, keine Überlappung | +| `ap` | approximately | Überlappung, plus ähnliche Größenordnung der Intervalle | -**Praxis-Tipp:** `sa`/`eb` unterscheiden sich von `gt`/`lt` nur spürbar bei -Resource-Feldern, die selbst ein **Intervall** sind (`Period`, z. B. -`Encounter.period`). Bei einem einzelnen Zeitpunkt-Feld ist der Unterschied -meist nicht beobachtbar – `gt` reicht schon, wenn die Period *beginnt* nach -dem Suchdatum; `sa` verlangt, dass die *gesamte* Period danach liegt. +**Wichtig — Containment vs. Overlap:** Nur `eq` (und implizit `sa`/`eb`) +verlangen, dass das Resource-Intervall **vollständig** innerhalb bzw. +**vollständig getrennt** vom Such-Intervall liegt. Bei `gt`, `lt`, `ge` +und `le` reicht dagegen eine **Überlappung** — das Resource-Intervall muss +nicht komplett außerhalb des Suchbereichs liegen, es genügt, dass es über +die jeweilige Grenze hinausragt. Details dazu im nächsten Abschnitt. --- -## 3. Mehrere `date`-Parameter kombinieren +## 3. Sonderfall: Matching gegen `Period` und `Range` + +Die Grafiken oben zeigen das Resource-Datum als schmalen roten Marker – das +passt für einfache `date`/`dateTime`-Felder wie `Patient.birthDate`. Sobald +das Zielfeld selbst ein **Intervall** ist (`Period`, z. B. +`Encounter.period`, `Condition.onsetPeriod`, oder `Range`), wird die +Unterscheidung zwischen **Containment** (vollständiges Enthaltensein) und +**Overlap** (bloße Überlappung) entscheidend: + +- **`eq`** verlangt Containment: das gesamte Resource-Intervall muss im + Such-Intervall liegen. +- **`gt` / `lt` / `ge` / `le`** verlangen nur Overlap: es reicht, wenn das + Resource-Intervall über die jeweilige Grenze hinausragt – auch wenn ein + Teil davon auf der "falschen" Seite liegt. +- **`sa` / `eb`** verlangen das Gegenteil von Overlap: das Resource-Intervall + muss vollständig und ohne jede Überlappung auf der jeweiligen Seite liegen. + +**Beispiel:** Ein Encounter läuft vom 10. bis 20. Juni. Die Suche +`date=gt2023-06-15` soll ihn finden, obwohl der Encounter *vor* dem 15. +begonnen hat — entscheidend ist nur, dass er über den 15. hinausreicht +(Overlap mit dem Bereich "nach dem 15."). Bei `date=sa2023-06-15` würde +derselbe Encounter dagegen **nicht** matchen, weil er nicht vollständig nach +dem 15. liegt — genau das ist der praktische Unterschied zwischen `gt` und +`sa`. + +Die folgende Grafik macht das für alle vier "einseitigen" Präfixe (`gt`, +`sa`, `lt`, `eb`) an denselben drei Beispiel-Periods sichtbar — einer +Period komplett davor, einer, die die Suchgrenze überlappt, und einer +komplett danach: + +![Period/Range Matching](fhir-date-period-range-matching.svg) + +Man sieht deutlich: `gt` und `lt` (blau schattierter Bereich, durchgezogene +Grenzlinie) lassen die überlappende Period B als Treffer durch, während `sa` +und `eb` (gestrichelte Grenzlinie) sie ablehnen — nur eine Period, die +komplett auf der richtigen Seite liegt, zählt dort. + +| Encounter-Zeitraum | `date=gt2023-06-15` | `date=sa2023-06-15` | +|---|---|---| +| 10.–20. Juni (überlappt die Grenze) | ✅ Treffer (Overlap) | ❌ kein Treffer (nicht vollständig danach) | +| 16.–20. Juni (komplett danach) | ✅ Treffer | ✅ Treffer | +| 01.–10. Juni (komplett davor) | ❌ kein Treffer | ❌ kein Treffer | + +### Offene (unvollständige) Periods + +In der Praxis sind Periods oft **nicht abgeschlossen** — z. B. ein laufender +Encounter ohne `end`, oder ein importierter Datensatz mit unbekanntem +`start`. Der FHIR-Standard regelt das eindeutig: + +> Ein fehlender unterer Rand (`start`) gilt implizit als **kleiner als jedes +> reale Datum** (also `-∞`). Ein fehlender oberer Rand (`end`) gilt implizit +> als **größer als jedes reale Datum** (also `+∞`). + +Das hat spürbare Konsequenzen, gerade für `sa` und `eb`: + +- Ein laufender Encounter (`start` gesetzt, `end` fehlt → `end = +∞`) + kann **niemals** `eb` (ends before) matchen — sein Ende liegt ja nie vor + irgendeinem endlichen Datum. Er matcht aber sehr wohl `gt`, weil sein + (unendliches) Ende immer über jede Suchgrenze hinausragt. +- Ein Encounter mit unbekanntem Start (`start` fehlt → `start = -∞`) kann + **niemals** `sa` (starts after) matchen — sein Beginn liegt ja nie nach + irgendeinem endlichen Datum. Er matcht aber `lt`, weil sein (unendlicher) + Anfang immer vor jede Suchgrenze hineinreicht. +- Eine Period ganz ohne `start` und `end` überlappt praktisch **jeden** + overlap-basierten Filter (`gt`, `lt`, `ge`, `le`) — sie „reicht" ja per + Definition über jede Grenze hinaus. + +Dieselben vier Präfixe wie oben, jetzt mit offenen statt geschlossenen +Periods: + +![Offene Periods Matching](fhir-date-offene-periods.svg) + +**Praktische Konsequenz:** Wenn du „laufende" Ressourcen (z. B. aktive +Encounter, offene Verordnungen) gezielt aus einer Zeitraumsuche ausschließen +willst, reicht `eb`/`sa` allein nicht — du musst zusätzlich nach dem Status +oder explizit nach `end:missing=true`/`start:missing=true` filtern, je +nachdem, was dein Server unterstützt. + +Bei einem einfachen Zeitpunkt-Feld (kein `Period`) fällt dieser Unterschied +praktisch nicht auf, da Resource-Start und -Ende (fast) identisch sind — dort +verhalten sich `gt` und `sa` de facto gleich. Der Unterschied wird erst bei +echten Zeitspannen relevant. Das ist auch der Grund, warum `sa`/`eb` +laut Spezifikation in erster Linie für `Period`/`Range`-Werte gedacht sind. + +--- + +## 4. Mehrere `date`-Parameter kombinieren In der Praxis reicht ein einzelner Präfix selten – meist willst du einen **Bereich** definieren. FHIR-Server verknüpfen mehrere `date`-Parameter in @@ -89,7 +176,7 @@ klassischen Zeitfenster-Queries in SQL. --- -## 4. Die vollständige 3×3-Matrix aller Bereichs-Kombinationen +## 5. Die vollständige 3×3-Matrix aller Bereichs-Kombinationen Die vier Muster oben sind nur die gebräuchlichsten. Tatsächlich lässt sich **jede** Untergrenze mit **jeder** Obergrenze kombinieren: @@ -110,7 +197,7 @@ gültige, funktionierende Query. --- -## 5. Sonderfälle: Wenn Kombinationen redundant oder widersprüchlich werden +## 6. Sonderfälle: Wenn Kombinationen redundant oder widersprüchlich werden Nicht jede syntaktisch gültige Kombination ist auch praktisch sinnvoll. Drei Kategorien solltest du kennen, bevor du sie versehentlich in einer @@ -146,19 +233,21 @@ Rückmeldung. Es lohnt sich, so etwas clientseitig vorab zu validieren. --- -## 6. Praxis-Empfehlungen zum Schluss +## 7. Praxis-Empfehlungen zum Schluss - Für einfache Bereichsfilter ist **`ge` + `lt`** (halboffenes Intervall) meist das robusteste Muster – es vermeidet Rundungsprobleme an der Precision-Grenze (kein Off-by-one bei „bis einschließlich 31.12."). -- **`sa`/`eb`** nur einsetzen, wenn das Zielfeld tatsächlich ein `Period`- oder - `Range`-Element ist. Bei einfachen `date`/`dateTime`-Feldern liefern sie in - der Regel dasselbe Ergebnis wie `gt`/`lt` – der zusätzliche Ausdruck bringt - dann keinen Mehrwert. +- **`sa`/`eb`** bewusst einsetzen, wenn du bei `Period`/`Range`-Feldern eine + *vollständige* Trennung brauchst (siehe Abschnitt 3) – z. B. „Encounter + vollständig nach Entlassung X". Für einen simplen „ab Datum X"-Filter ist + meist `ge`/`gt` gemeint, nicht `sa`. - Vor dem Absetzen einer Multi-Parameter-Query lohnt sich ein kurzer Check auf logische Konsistenz zwischen Unter- und Obergrenze, um leere Ergebnisse durch Konfigurationsfehler in der eigenen Pipeline zu vermeiden. Mit diesen fünf Grafiken im Hinterkopf – ohne Präfix, alle neun Präfixe einzeln, typische Kombinationen, die volle 3×3-Matrix und die Sonderfälle – -sollte die nächste `date`-Query im FHIR-Suchparameter kein Rätsel mehr sein. +und dem Wissen um den Unterschied zwischen Containment und Overlap bei +`Period`/`Range`-Feldern sollte die nächste `date`-Query im +FHIR-Suchparameter kein Rätsel mehr sein.