12 KiB
HowTo: Der FHIR date-Suchparameter – Präfixe, Intervalle und Kombinationen richtig verstehen
Wer FHIR-Server baut oder abfragt, stolpert früher oder später über den
date-Suchparameter. Auf den ersten Blick sieht er simpel aus –
?date=2023-06-15 – aber sobald Präfixe wie ge, sa oder ap ins Spiel
kommen und mehrere date-Parameter kombiniert werden, wird es schnell
unübersichtlich. Dieser Artikel bringt Ordnung rein: Schritt für Schritt, mit
Zeitleisten-Grafiken zu jedem Fall.
1. Das Grundprinzip: FHIR vergleicht Intervalle, keine Zeitpunkte
Der wichtigste gedankliche Reset zuerst: FHIR behandelt Datumswerte nie als exakten Zeitpunkt, sondern immer als Intervall. Wie breit dieses Intervall ist, hängt von der Precision des angegebenen Werts ab:
2023→ das ganze Jahr 20232023-06→ der ganze Juni 20232023-06-15→ der ganze 15. Juni 2023 (00:00–24:00 Uhr)2023-06-15T10:00:00→ (fast) ein exakter Zeitpunkt
Jeder Vergleichsoperator vergleicht anschließend zwei Intervalle
miteinander: das Such-Intervall aus deinem Query-Parameter und das
Resource-Intervall aus dem Feld der Ressource (z. B. Patient.birthDate oder
Encounter.period).
Ohne Präfix = eq, aber die Precision entscheidet mit
Gibst du kein Präfix an, nimmt der Server eq an. Das Resource-Datum muss
dann vollständig innerhalb deines Such-Intervalls liegen. Je gröber die
Precision, desto breiter das Intervall – und desto mehr Resource-Werte passen
hinein:
date=2023-06-15 matched nur diesen einen Tag. date=2023-06 matched dagegen
jeden Tag im Juni – auch den 15. Das ist der häufigste Stolperstein für
Einsteiger: eine „ungenaue" Suchanfrage ist nicht strenger, sondern lockerer.
2. Die neun Präfixe im Überblick
FHIR definiert insgesamt neun Präfixe für date. Jeder davon beschreibt eine
andere Beziehung zwischen Such-Intervall (rot gestrichelt in der Grafik) und
Treffer-Bereich (grün/farbig schattiert):
| Präfix | Name | Bedeutung |
|---|---|---|
eq |
gleich | Resource-Intervall liegt vollständig im Such-Intervall |
ne |
ungleich | Komplement von eq |
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 |
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. 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:
eqverlangt Containment: das gesamte Resource-Intervall muss im Such-Intervall liegen.gt/lt/ge/leverlangen nur Overlap: es reicht, wenn das Resource-Intervall über die jeweilige Grenze hinausragt – auch wenn ein Teil davon auf der "falschen" Seite liegt.sa/ebverlangen 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:
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 (
startgesetzt,endfehlt →end = +∞) kann niemalseb(ends before) matchen — sein Ende liegt ja nie vor irgendeinem endlichen Datum. Er matcht aber sehr wohlgt, weil sein (unendliches) Ende immer über jede Suchgrenze hinausragt. - Ein Encounter mit unbekanntem Start (
startfehlt →start = -∞) kann niemalssa(starts after) matchen — sein Beginn liegt ja nie nach irgendeinem endlichen Datum. Er matcht aberlt, weil sein (unendlicher) Anfang immer vor jede Suchgrenze hineinreicht. - Eine Period ganz ohne
startundendü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:
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
derselben Query immer per UND. Vier typische Muster:
| Kombination | Beispiel | Charakter |
|---|---|---|
ge + le |
ge2023-01-01&le2023-12-31 |
Geschlossenes Intervall (beide Grenzen inklusive) |
gt + lt |
gt2023-01-01<2023-12-31 |
Offenes Intervall (beide Grenzen exklusive) |
sa + eb |
sa2023-01-01&eb2023-12-31 |
Strikt getrennter Bereich, primär für Period-Werte |
ge + lt |
ge2023-06-01<2023-07-01 |
Halboffenes Intervall [start, ende) – Standardmuster für „genau ein Monat" |
Wenn du in Timestamp-lastigen ETL-Pipelines mit [start, ende) arbeitest,
kommt dir ge + lt bekannt vor – es ist exakt dasselbe Muster wie bei
klassischen Zeitfenster-Queries in SQL.
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:
- Untergrenzen:
ge,gt,sa - Obergrenzen:
le,lt,eb
Das ergibt 3 × 3 = 9 mögliche Kombinationen, alle spec-konform:
Sie unterscheiden sich nur darin, ob die jeweilige Grenze inklusiv
(durchgezogene Linie) oder exklusiv (gestrichelte Linie) ist, und ob es sich
um einen einfachen Werte-Vergleich (ge/gt/le/lt) oder einen strengen
Period-Vergleich (sa/eb) handelt. Es gibt keine explizite
Ausschluss-Regel im FHIR-Standard – jede Zelle dieser Matrix ist eine
gültige, funktionierende Query.
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 generierten Query landen lässt:
a) Redundante Kombinationen
Ändern nichts am Ergebnis, sind aber technisch kein Fehler:
eq+ eine Grenze, die vomeq-Intervall ohnehin erfüllt wird (eq2023-06-15&ge2020-01-01) – die Zusatzbedingung ist überflüssig.- Zwei Bedingungen desselben Richtungstyps
(
ge2020-01-01&ge2023-01-01) – nur die strengere (spätere) Untergrenze wirkt effektiv.
b) Kombinationen mit garantiert leerem Ergebnis
Strukturell unmöglich zu erfüllen:
eq+neauf denselben Wert (eq2023-06-15&ne2023-06-15) – sich gegenseitig ausschließende Bedingungen.- Untergrenze liegt zeitlich nach der Obergrenze
(
ge2023-06-01&le2023-01-01) – kein Datum erfüllt beides gleichzeitig.
Der Server wird solche Queries syntaktisch akzeptieren und einfach eine leere Ergebnismenge zurückgeben – kein Fehler, aber auch keine hilfreiche Rückmeldung. Es lohnt sich, so etwas clientseitig vorab zu validieren.
c) Ungewöhnliche, aber gültige Sonderfälle
- Drei oder mehr
date-Parameter, z. B. ein Jahresbereich mit ausgeschlossenem Einzeltag:ge2023-01-01&le2023-12-31&ne2023-06-15. apkombiniert mit einer Grenze, z. B.ap2023-06-15&ge2023-01-01– selten genutzt, daapselbst schon unscharf ist und die Toleranzberechnung serverabhängig bleibt.
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/ebbewusst einsetzen, wenn du beiPeriod/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 meistge/gtgemeint, nichtsa.- 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 –
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.