Basic Queries
This page walks through the most common FHIRPath operations you will perform: reading simple fields, traversing nested objects, and indexing into arrays. Every example includes a complete FHIR® JSON resource and the Go code needed to evaluate it.
Extracting Patient Demographics
The simplest FHIRPath expressions navigate from the resource root to a leaf field. The path always starts with the resource type name.
Sample Patient Resource
{
"resourceType": "Patient",
"id": "example-patient-1",
"active": true,
"name": [
{
"use": "official",
"family": "Chalmers",
"given": ["Peter", "James"]
},
{
"use": "usual",
"given": ["Jim"]
}
],
"gender": "male",
"birthDate": "1974-12-25",
"telecom": [
{
"system": "phone",
"value": "(03) 5555 6473",
"use": "work"
},
{
"system": "email",
"value": "peter.chalmers@example.com",
"use": "home"
}
],
"address": [
{
"use": "home",
"line": ["534 Erewhon St"],
"city": "PleasantVille",
"state": "VT",
"postalCode": "3999"
},
{
"use": "work",
"line": ["100 Corporate Dr"],
"city": "Metropolis",
"state": "IL",
"postalCode": "60007"
}
]
}Getting the Resource ID
package main
import (
"fmt"
"log"
"github.com/gofhir/fhirpath"
)
func main() {
patient := []byte(`{
"resourceType": "Patient",
"id": "example-patient-1",
"active": true,
"name": [{"use": "official", "family": "Chalmers", "given": ["Peter", "James"]}],
"gender": "male",
"birthDate": "1974-12-25"
}`)
// Extract the resource id
id, err := fhirpath.EvaluateToString(patient, "Patient.id")
if err != nil {
log.Fatal(err)
}
fmt.Println("ID:", id)
// Output: ID: example-patient-1
}Getting the Family Name
family, err := fhirpath.EvaluateToString(patient, "Patient.name.family")
if err != nil {
log.Fatal(err)
}
fmt.Println("Family:", family)
// Output: Family: ChalmersWhen the path traverses an array (like name), FHIRPath automatically iterates over every element and collects the family field from each one. If only one name has a family value, you get a single-element collection.
Getting the Birth Date
birthDate, err := fhirpath.EvaluateToString(patient, "Patient.birthDate")
if err != nil {
log.Fatal(err)
}
fmt.Println("Birth date:", birthDate)
// Output: Birth date: 1974-12-25Getting Multiple Fields at Once
You can evaluate several expressions against the same resource. For better performance in production, use EvaluateCached so each expression is compiled only once:
patient := []byte(`{
"resourceType": "Patient",
"id": "example-patient-1",
"active": true,
"name": [{"use": "official", "family": "Chalmers", "given": ["Peter", "James"]}],
"gender": "male",
"birthDate": "1974-12-25"
}`)
expressions := map[string]string{
"id": "Patient.id",
"family": "Patient.name.family",
"gender": "Patient.gender",
"birthDate": "Patient.birthDate",
}
for label, expr := range expressions {
result, err := fhirpath.EvaluateCached(patient, expr)
if err != nil {
log.Printf("Error evaluating %s: %v", label, err)
continue
}
fmt.Printf("%-10s: %s\n", label, result)
}Navigating Nested Structures
FHIR® resources contain deeply nested objects. FHIRPath uses dot notation to traverse them.
Extracting Address Fields
patient := []byte(`{
"resourceType": "Patient",
"id": "patient-nested",
"address": [
{
"use": "home",
"line": ["534 Erewhon St"],
"city": "PleasantVille",
"state": "VT",
"postalCode": "3999"
},
{
"use": "work",
"line": ["100 Corporate Dr"],
"city": "Metropolis",
"state": "IL",
"postalCode": "60007"
}
]
}`)
// Get all cities -- traverses all address entries
cities, err := fhirpath.EvaluateToStrings(patient, "Patient.address.city")
if err != nil {
log.Fatal(err)
}
fmt.Println("Cities:", cities)
// Output: Cities: [PleasantVille Metropolis]Extracting Telecom Values
patient := []byte(`{
"resourceType": "Patient",
"id": "patient-telecom",
"telecom": [
{"system": "phone", "value": "(03) 5555 6473", "use": "work"},
{"system": "email", "value": "peter.chalmers@example.com", "use": "home"}
]
}`)
// Get all telecom values
values, err := fhirpath.EvaluateToStrings(patient, "Patient.telecom.value")
if err != nil {
log.Fatal(err)
}
for _, v := range values {
fmt.Println("Telecom:", v)
}
// Output:
// Telecom: (03) 5555 6473
// Telecom: peter.chalmers@example.comNavigating Multi-Level Nesting
Some FHIR® resources are deeply nested. For example, an Observation with a component:
observation := []byte(`{
"resourceType": "Observation",
"id": "blood-pressure",
"status": "final",
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "85354-9",
"display": "Blood pressure panel"
}
]
},
"component": [
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8480-6",
"display": "Systolic blood pressure"
}
]
},
"valueQuantity": {
"value": 120,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]"
}
},
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8462-4",
"display": "Diastolic blood pressure"
}
]
},
"valueQuantity": {
"value": 80,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]"
}
}
]
}`)
// Get the display text of the top-level code
display, err := fhirpath.EvaluateToString(observation, "Observation.code.coding.display")
if err != nil {
log.Fatal(err)
}
fmt.Println("Code display:", display)
// Output: Code display: Blood pressure panel
// Get all component displays
componentDisplays, err := fhirpath.EvaluateToStrings(observation,
"Observation.component.code.coding.display")
if err != nil {
log.Fatal(err)
}
fmt.Println("Components:", componentDisplays)
// Output: Components: [Systolic blood pressure Diastolic blood pressure]Working with Arrays
FHIRPath provides several ways to work with arrays: indexing, filtering with where(), and implicit iteration.
Indexing into Arrays
Use bracket notation to access a specific element by its zero-based index:
patient := []byte(`{
"resourceType": "Patient",
"id": "patient-arrays",
"name": [
{"use": "official", "family": "Chalmers", "given": ["Peter", "James"]},
{"use": "usual", "given": ["Jim"]}
]
}`)
// Get the first name entry
result, err := fhirpath.EvaluateToString(patient, "Patient.name[0].family")
if err != nil {
log.Fatal(err)
}
fmt.Println("First family name:", result)
// Output: First family name: Chalmers
// Get the first given name of the first name entry
firstGiven, err := fhirpath.EvaluateToString(patient, "Patient.name[0].given[0]")
if err != nil {
log.Fatal(err)
}
fmt.Println("First given name:", firstGiven)
// Output: First given name: Peter
// Get all given names across all name entries (implicit iteration)
allGiven, err := fhirpath.EvaluateToStrings(patient, "Patient.name.given")
if err != nil {
log.Fatal(err)
}
fmt.Println("All given names:", allGiven)
// Output: All given names: [Peter James Jim]Filtering with where()
The where() function lets you select elements that match a Boolean criterion:
patient := []byte(`{
"resourceType": "Patient",
"id": "patient-where",
"address": [
{"use": "home", "city": "PleasantVille", "state": "VT"},
{"use": "work", "city": "Metropolis", "state": "IL"},
{"use": "temp", "city": "Somewhere", "state": "CA"}
]
}`)
// Get only the home address city
homeCity, err := fhirpath.EvaluateToString(patient,
"Patient.address.where(use = 'home').city")
if err != nil {
log.Fatal(err)
}
fmt.Println("Home city:", homeCity)
// Output: Home city: PleasantVille
// Get the state of the work address
workState, err := fhirpath.EvaluateToString(patient,
"Patient.address.where(use = 'work').state")
if err != nil {
log.Fatal(err)
}
fmt.Println("Work state:", workState)
// Output: Work state: ILUsing first() and last()
patient := []byte(`{
"resourceType": "Patient",
"id": "patient-first-last",
"name": [
{"use": "official", "family": "Chalmers", "given": ["Peter"]},
{"use": "maiden", "family": "Windsor", "given": ["Anne"]}
]
}`)
// Get the first name entry's family
first, err := fhirpath.EvaluateToString(patient, "Patient.name.first().family")
if err != nil {
log.Fatal(err)
}
fmt.Println("First:", first)
// Output: First: Chalmers
// Get the last name entry's family
last, err := fhirpath.EvaluateToString(patient, "Patient.name.last().family")
if err != nil {
log.Fatal(err)
}
fmt.Println("Last:", last)
// Output: Last: WindsorUsing the Convenience Helpers
The library provides several typed convenience functions that save you from manually inspecting the result collection.
Checking Existence
patient := []byte(`{
"resourceType": "Patient",
"id": "patient-exists",
"name": [{"family": "Doe"}],
"birthDate": "1990-01-15"
}`)
// Check if the patient has a birthDate
hasBirthDate, err := fhirpath.Exists(patient, "Patient.birthDate")
if err != nil {
log.Fatal(err)
}
fmt.Println("Has birthDate:", hasBirthDate)
// Output: Has birthDate: true
// Check if the patient has a deceased indicator
hasDeceased, err := fhirpath.Exists(patient, "Patient.deceased")
if err != nil {
log.Fatal(err)
}
fmt.Println("Has deceased:", hasDeceased)
// Output: Has deceased: falseCounting Results
patient := []byte(`{
"resourceType": "Patient",
"id": "patient-count",
"name": [
{"use": "official", "family": "Chalmers", "given": ["Peter", "James"]},
{"use": "usual", "given": ["Jim"]}
],
"telecom": [
{"system": "phone", "value": "555-1234"},
{"system": "email", "value": "peter@example.com"},
{"system": "phone", "value": "555-5678"}
]
}`)
// Count the number of name entries
nameCount, err := fhirpath.Count(patient, "Patient.name")
if err != nil {
log.Fatal(err)
}
fmt.Println("Name entries:", nameCount)
// Output: Name entries: 2
// Count all given names across all entries
givenCount, err := fhirpath.Count(patient, "Patient.name.given")
if err != nil {
log.Fatal(err)
}
fmt.Println("Given names:", givenCount)
// Output: Given names: 3
// Count telecom entries
telecomCount, err := fhirpath.Count(patient, "Patient.telecom")
if err != nil {
log.Fatal(err)
}
fmt.Println("Telecom entries:", telecomCount)
// Output: Telecom entries: 3Boolean Evaluation
patient := []byte(`{
"resourceType": "Patient",
"id": "patient-bool",
"active": true,
"name": [{"family": "Smith"}]
}`)
// Evaluate a boolean expression
isActive, err := fhirpath.EvaluateToBoolean(patient, "Patient.active")
if err != nil {
log.Fatal(err)
}
fmt.Println("Is active:", isActive)
// Output: Is active: true
// Evaluate an existence check as a boolean
hasName, err := fhirpath.EvaluateToBoolean(patient, "Patient.name.exists()")
if err != nil {
log.Fatal(err)
}
fmt.Println("Has name:", hasName)
// Output: Has name: trueComplete Working Example
Here is a self-contained program that demonstrates several basic queries against a realistic Patient resource:
package main
import (
"fmt"
"log"
"github.com/gofhir/fhirpath"
)
func main() {
patient := []byte(`{
"resourceType": "Patient",
"id": "pat-12345",
"meta": {
"versionId": "3",
"lastUpdated": "2024-01-15T10:30:00Z"
},
"active": true,
"name": [
{
"use": "official",
"family": "Rodriguez",
"given": ["Maria", "Elena"],
"prefix": ["Mrs."]
}
],
"gender": "female",
"birthDate": "1985-07-20",
"telecom": [
{"system": "phone", "value": "+1-555-867-5309", "use": "mobile"},
{"system": "email", "value": "maria.rodriguez@example.com", "use": "home"}
],
"address": [
{
"use": "home",
"line": ["742 Evergreen Terrace"],
"city": "Springfield",
"state": "IL",
"postalCode": "62704",
"country": "US"
}
]
}`)
// Resource ID
id, _ := fhirpath.EvaluateToString(patient, "Patient.id")
fmt.Println("ID: ", id)
// Full name
family, _ := fhirpath.EvaluateToString(patient, "Patient.name.where(use = 'official').family")
given, _ := fhirpath.EvaluateToStrings(patient, "Patient.name.where(use = 'official').given")
fmt.Printf("Name: %s %s\n", given, family)
// Demographics
gender, _ := fhirpath.EvaluateToString(patient, "Patient.gender")
dob, _ := fhirpath.EvaluateToString(patient, "Patient.birthDate")
fmt.Println("Gender: ", gender)
fmt.Println("Birth date:", dob)
// Contact information
phone, _ := fhirpath.EvaluateToString(patient, "Patient.telecom.where(system = 'phone').value")
email, _ := fhirpath.EvaluateToString(patient, "Patient.telecom.where(system = 'email').value")
fmt.Println("Phone: ", phone)
fmt.Println("Email: ", email)
// Address
city, _ := fhirpath.EvaluateToString(patient, "Patient.address.where(use = 'home').city")
state, _ := fhirpath.EvaluateToString(patient, "Patient.address.where(use = 'home').state")
fmt.Println("City: ", city)
fmt.Println("State: ", state)
// Counts
nameCount, _ := fhirpath.Count(patient, "Patient.name")
telecomCount, _ := fhirpath.Count(patient, "Patient.telecom")
fmt.Println("Name entries: ", nameCount)
fmt.Println("Telecom entries:", telecomCount)
// Existence checks
hasEmail, _ := fhirpath.Exists(patient, "Patient.telecom.where(system = 'email')")
hasDeceased, _ := fhirpath.Exists(patient, "Patient.deceased")
fmt.Println("Has email: ", hasEmail)
fmt.Println("Has deceased: ", hasDeceased)
// Error handling -- invalid expression
_, err := fhirpath.Evaluate(patient, "Patient.!!!invalid")
if err != nil {
log.Printf("Expected error: %v", err)
}
}