New article "fhir date parameters", config changes for multilanguage
support, tarnslations for existing pages and articles
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
Title: HowTo: Der FHIR `date`-Suchparameter – Präfixe, Intervalle und Kombinationen richtig verstehen
|
||||
Slug: howto-fhir-date-parameter
|
||||
Category: work
|
||||
Tags: FHIR, howto
|
||||
Date: 2026-08-12
|
||||
Lang: de
|
||||
Status: published
|
||||
|
||||
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, und als Kurzreferenz zum schnellen
|
||||
Nachschlagen.
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
[TOC]
|
||||
|
||||
---
|
||||
|
||||
## Kurzreferenz: Alle Präfixe auf einen Blick
|
||||
|
||||
Für den schnellen Blick zwischendurch. Details und Grafiken zu jeder Zeile
|
||||
folgen in den Abschnitten unten.
|
||||
|
||||
| Präfix | Name | Kurzregel | Beispiel |
|
||||
|---|---|---|---|
|
||||
| *(keins)* | implizit `eq` | Precision des Werts bestimmt das Intervall | `date=2023-06-15` |
|
||||
| `eq` | gleich | Resource-Intervall vollständig im Such-Intervall | `date=eq2023-06-15` |
|
||||
| `ne` | ungleich | Komplement von `eq` | `date=ne2023-06-15` |
|
||||
| `gt` | größer als | Ragt über das Ende hinaus (Overlap genügt) | `date=gt2023-06-15` |
|
||||
| `lt` | kleiner als | Beginnt vor dem Anfang (Overlap genügt) | `date=lt2023-06-15` |
|
||||
| `ge` | größer/gleich | `gt` oder vollständig enthalten (`eq`) | `date=ge2023-06-15` |
|
||||
| `le` | kleiner/gleich | `lt` oder vollständig enthalten (`eq`) | `date=le2023-06-15` |
|
||||
| `sa` | starts after | Vollständig danach, **keine** Überlappung | `date=sa2023-06-15` |
|
||||
| `eb` | ends before | Vollständig davor, **keine** Überlappung | `date=eb2023-06-15` |
|
||||
| `ap` | approximately | Überlappung + ähnliche Größenordnung | `date=ap2023-06-15` |
|
||||
|
||||
**Fehlender `start`/`end` bei `Period`:** fehlender `start` bedeutet `-∞`,
|
||||
fehlendes `end` bedeutet `+∞`. Deshalb kann `sa` bei fehlendem `start` nie
|
||||
matchen, und `eb` kann bei fehlendem `end` nie matchen (Details in
|
||||
[Abschnitt 4](#4-offene-unvollständige-periods)).
|
||||
|
||||
---
|
||||
|
||||
## 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 2023
|
||||
- `2023-06` → der ganze Juni 2023
|
||||
- `2023-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:
|
||||
|
||||

|
||||
*FHIR date-Suchparameter OHNE Präfix*
|
||||
|
||||
`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.
|
||||
|
||||
---
|
||||
|
||||
## 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):
|
||||
|
||||

|
||||
*Alle Präfixe im Überblick*
|
||||
|
||||
| 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.
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
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.
|
||||
|
||||
**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:
|
||||
|
||||

|
||||
*Matching gegen Period/Range (Containment vs. Overlap)*
|
||||
|
||||
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**, zum Beispiel 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, also `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, also `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 in [Abschnitt 3](#3-matching-gegen-period-und-range),
|
||||
jetzt mit offenen statt geschlossenen Periods:
|
||||
|
||||

|
||||
*Matching gegen offene (unvollständige) Period/Range*
|
||||
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
## 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 mehrerer Parameter (UND-Verknüpfung)*
|
||||
|
||||
| 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.
|
||||
|
||||
---
|
||||
|
||||
## 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:
|
||||
|
||||

|
||||
*3x3-Matrix der Bereichs-Kombinationen (Untergrenze x Obergrenze)*
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## 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:
|
||||
|
||||

|
||||
*Sonderfälle bei Parameter-Kombinationen*
|
||||
|
||||
### Redundante Kombinationen
|
||||
Ändern nichts am Ergebnis, sind aber technisch kein Fehler:
|
||||
- `eq` + eine Grenze, die vom `eq`-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.
|
||||
|
||||
### Kombinationen mit garantiert leerem Ergebnis
|
||||
Strukturell unmöglich zu erfüllen:
|
||||
- `eq` + `ne` auf 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.
|
||||
|
||||
### 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`.
|
||||
- **`ap` kombiniert mit einer Grenze**, z. B.
|
||||
`ap2023-06-15&ge2023-01-01`: selten genutzt, da `ap` selbst schon
|
||||
unscharf ist und die Toleranzberechnung serverabhängig bleibt.
|
||||
|
||||
---
|
||||
|
||||
## 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`** bewusst einsetzen, wenn du bei `Period`/`Range`-Feldern eine
|
||||
*vollständige* Trennung brauchst (siehe [Abschnitt 3](#3-matching-gegen-period-und-range)),
|
||||
z. B. „Encounter vollständig nach Entlassung X". Für einen simplen
|
||||
„ab Datum X"-Filter ist meist `ge`/`gt` gemeint, nicht `sa`.
|
||||
- Bei `Period`-Feldern mit möglichen offenen Enden (siehe
|
||||
[Abschnitt 4](#4-offene-unvollständige-periods)) nicht vergessen: `sa`/`eb`
|
||||
greifen dort strukturell nie. Ggf. zusätzlich `:missing` oder Status
|
||||
filtern.
|
||||
- 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 der Kurzreferenz oben und den sechs Grafiken im Hinterkopf, ohne Präfix,
|
||||
alle neun Präfixe einzeln, Containment vs. Overlap bei Period/Range (offen
|
||||
wie geschlossen), 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.
|
||||
Reference in New Issue
Block a user