fhirpath

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 9, 2026 License: MIT Imports: 11 Imported by: 0

README

FHIRPath

A complete FHIRPath 2.0 expression evaluator for FHIR resources in Go.

Overview

This package implements the FHIRPath specification for evaluating path expressions against FHIR resources. It supports parsing, compiling, and evaluating FHIRPath expressions with full type safety and UCUM unit normalization.

Installation

import "github.com/gofhir/fhirpath"

Quick Start

package main

import (
    "fmt"
    "github.com/gofhir/fhirpath"
)

func main() {
    patient := []byte(`{
        "resourceType": "Patient",
        "id": "123",
        "name": [{"family": "Doe", "given": ["John"]}],
        "birthDate": "1990-05-15"
    }`)

    // Simple evaluation
    result, err := fhirpath.Evaluate(patient, "Patient.name.family")
    if err != nil {
        panic(err)
    }
    fmt.Println(result) // ["Doe"]

    // Compile once, evaluate many times
    expr := fhirpath.MustCompile("birthDate > @1980-01-01")
    result = expr.Evaluate(patient)
    fmt.Println(result) // [true]
}

FHIR Model Support

For precise type resolution across FHIR versions, supply a version-specific model:

import "github.com/gofhir/models/r4"

result, err := expr.EvaluateWithOptions(resource,
    fhirpath.WithModel(r4.FHIRPathModel()),
)

Without a model, the engine uses built-in heuristics that work for most cases. With a model, type hierarchy queries (is, as, ofType) become precise and polymorphic elements (value[x]) resolve using exact type metadata.

See the documentation for details on the Model interface and custom implementations.

API Reference

Main Functions
Function Description
Evaluate(resource []byte, expr string) (Collection, error) Evaluate expression on resource
MustEvaluate(resource []byte, expr string) Collection Evaluate, panic on error
Compile(expr string) (*Expression, error) Compile expression for reuse
MustCompile(expr string) *Expression Compile, panic on error
Expression Methods
expr := fhirpath.MustCompile("Patient.name.given")
result := expr.Evaluate(patientJSON)

Type System

FHIRPath defines a type system that this implementation fully supports:

Type Go Type Example
Boolean types.Boolean true, false
Integer types.Integer 42, -17
Decimal types.Decimal 3.14159
String types.String 'hello'
Date types.Date @2024-01-15
DateTime types.DateTime @2024-01-15T10:30:00Z
Time types.Time @T14:30:00
Quantity types.Quantity 10 'mg', 100 'cm'
Quantity with UCUM Normalization

Quantities support UCUM unit normalization for comparison:

// These evaluate to true
fhirpath.MustEvaluate(resource, "1000 'mg' = 1 'g'")
fhirpath.MustEvaluate(resource, "100 'cm' = 1 'm'")
fhirpath.MustEvaluate(resource, "60 'min' = 1 'h'")
fhirpath.MustEvaluate(resource, "1 'kg' ~ 1000 'g'")

Operators

Arithmetic Operators
Operator Description Example
+ Addition 2 + 35
- Subtraction 5 - 23
* Multiplication 3 * 412
/ Division 10 / 42.5
div Integer division 10 div 42
mod Modulo 10 mod 31
Comparison Operators
Operator Description Example
= Equals name = 'John'
!= Not equals status != 'active'
< Less than age < 18
> Greater than value > 100
<= Less or equal count <= 10
>= Greater or equal priority >= 1
~ Equivalent 'Hello' ~ 'hello'
!~ Not equivalent code !~ 'ABC'
Boolean Operators
Operator Description Example
and Logical AND active and verified
or Logical OR draft or pending
not Logical NOT not deceased
implies Implication a implies b
xor Exclusive OR a xor b
Collection Operators
Operator Description Example
| Union name | alias
in Membership 'active' in status
contains Contains codes contains 'ABC'
Type Operators
Operator Description Example
is Type check value is Quantity
as Type cast value as String

Functions

String Functions
Function Description Example
startsWith(prefix) Check prefix name.startsWith('Dr')
endsWith(suffix) Check suffix file.endsWith('.pdf')
contains(substring) Check contains text.contains('error')
replace(old, new) Replace text name.replace('-', '_')
matches(regex) Regex match code.matches('[A-Z]{3}')
replaceMatches(regex, sub) Regex replace text.replaceMatches('\\s+', ' ')
indexOf(substring) Find position text.indexOf(':')
substring(start[, length]) Extract substring code.substring(0, 3)
lower() Lowercase name.lower()
upper() Uppercase code.upper()
length() String length name.length()
toChars() Split to chars 'abc'.toChars()
trim() Trim whitespace input.trim()
split(separator) Split string csv.split(',')
join([separator]) Join collection names.join(', ')
encode(encoding) Encode string text.encode('base64')
decode(encoding) Decode string data.decode('base64')
Math Functions
Function Description Example
abs() Absolute value (-5).abs()5
ceiling() Round up (3.2).ceiling()4
floor() Round down (3.8).floor()3
truncate() Remove decimals (3.9).truncate()3
round([precision]) Round (3.456).round(2)3.46
exp() Exponential (2).exp()
ln() Natural log (10).ln()
log(base) Logarithm (100).log(10)2
power(exp) Power (2).power(3)8
sqrt() Square root (16).sqrt()4
Existence Functions
Function Description Example
empty() Collection empty name.empty()
exists([criteria]) Any exist telecom.exists(system='email')
all(criteria) All match name.all(family.exists())
allTrue() All true flags.allTrue()
anyTrue() Any true conditions.anyTrue()
allFalse() All false errors.allFalse()
anyFalse() Any false checks.anyFalse()
count() Count items name.count()
distinct() Remove duplicates codes.distinct()
isDistinct() All unique ids.isDistinct()
Filtering Functions
Function Description Example
where(criteria) Filter items name.where(use='official')
select(projection) Transform items telecom.select(value)
repeat(expression) Recursive navigation contained.repeat(children())
ofType(type) Filter by type value.ofType(Quantity)
Subsetting Functions
Function Description Example
first() First item name.first()
last() Last item entry.last()
tail() All except first items.tail()
take(n) First n items results.take(5)
skip(n) Skip n items entries.skip(10)
single() Exactly one identifier.single()
intersect(other) Intersection a.intersect(b)
exclude(other) Exclusion all.exclude(removed)
Combining Functions
Function Description Example
union(other) Set union names.union(aliases)
combine(other) Concatenate first.combine(second)
Aggregate Functions
Function Description Example
aggregate(init, accumulator) Reduce collection values.aggregate(0, $total + $this)
Conversion Functions
Function Description Example
iif(cond, true[, false]) Conditional iif(active, 'Yes', 'No')
toBoolean() Convert to boolean 'true'.toBoolean()
convertsToBoolean() Can convert value.convertsToBoolean()
toInteger() Convert to integer '42'.toInteger()
convertsToInteger() Can convert value.convertsToInteger()
toDecimal() Convert to decimal '3.14'.toDecimal()
convertsToDecimal() Can convert value.convertsToDecimal()
toString() Convert to string (42).toString()
convertsToString() Can convert value.convertsToString()
toDate() Convert to date '2024-01-15'.toDate()
convertsToDate() Can convert value.convertsToDate()
toDateTime() Convert to datetime date.toDateTime()
convertsToDateTime() Can convert value.convertsToDateTime()
toTime() Convert to time '14:30:00'.toTime()
convertsToTime() Can convert value.convertsToTime()
toQuantity([unit]) Convert to quantity value.toQuantity('mg')
convertsToQuantity([unit]) Can convert value.convertsToQuantity('kg')
Temporal Functions
Function Description Example
now() Current datetime now()
today() Current date today()
timeOfDay() Current time timeOfDay()
year() Extract year birthDate.year()
month() Extract month birthDate.month()
day() Extract day birthDate.day()
hour() Extract hour time.hour()
minute() Extract minute time.minute()
second() Extract second time.second()
millisecond() Extract ms time.millisecond()
Utility Functions
Function Description Example
trace([name]) Debug output value.trace('debug')
children() Child elements element.children()
descendants() All descendants resource.descendants()

Environment Variables

Variable Description
%resource Root resource being evaluated
%context Current evaluation context
%ucum UCUM unit system URL
// Access environment variables
fhirpath.Evaluate(patient, "%resource.id")
fhirpath.Evaluate(patient, "%context.resourceType")

Special Identifiers

Backtick-Delimited Identifiers

For field names with special characters:

// JSON: {"PID-1": "12345"}
fhirpath.Evaluate(resource, "Patient.`PID-1`")
Polymorphic Elements (value[x])

Automatic resolution of FHIR polymorphic elements:

// Observation.valueQuantity, Observation.valueString, etc.
fhirpath.Evaluate(observation, "Observation.value")  // Resolves automatically
fhirpath.Evaluate(observation, "Observation.value.ofType(Quantity)")

Lazy Evaluation

The iif() function uses lazy evaluation - only the matching branch is evaluated:

// Only evaluates the true branch, avoiding potential errors in false branch
fhirpath.Evaluate(resource, "iif(value.exists(), value.first(), 'default')")

Performance

Expression Caching

Compiled expressions are cached internally for performance:

// Recommended: compile once, reuse
expr := fhirpath.MustCompile("Patient.name.where(use='official').given")
for _, patient := range patients {
    result := expr.Evaluate(patient)
}
Best Practices
  1. Compile expressions that will be used multiple times
  2. Use specific paths rather than wildcards when possible
  3. Filter early with where() to reduce collection sizes
  4. Avoid unnecessary conversions - work with native types

Error Handling

result, err := fhirpath.Evaluate(resource, expr)
if err != nil {
    // Parse error or evaluation error
    log.Printf("FHIRPath error: %v", err)
}

if result.Empty() {
    // Expression evaluated but no results
}

Specification Compliance

This implementation follows FHIRPath Normative Release 2.0.0:

  • Full type system (Boolean, Integer, Decimal, String, Date, DateTime, Time, Quantity)
  • All operators (arithmetic, comparison, boolean, collection, type)
  • All standard functions (40+ functions)
  • UCUM unit normalization for Quantity comparisons
  • Three-valued logic (empty propagation)
  • Lazy evaluation for iif()
  • Polymorphic element resolution (value[x])
  • Environment variables (%resource, %context)
  • Delimited identifiers (backticks)

License

See repository root for license information.

Documentation

Overview

Package fhirpath provides a FHIRPath engine for evaluating expressions on FHIR resources.

Index

Constants

This section is empty.

Variables

View Source
var DefaultCache = NewExpressionCache(1000)

DefaultCache is a global expression cache for convenience. Use NewExpressionCache for finer control over cache lifetime.

Functions

func Count

func Count(resource []byte, expr string) (int, error)

Count evaluates an expression and returns the number of results.

func Evaluate

func Evaluate(resource []byte, expr string) (types.Collection, error)

Evaluate parses and evaluates a FHIRPath expression against a JSON resource. This is a convenience function that compiles and evaluates in one step.

func EvaluateToBoolean

func EvaluateToBoolean(resource []byte, expr string) (bool, error)

EvaluateToBoolean evaluates an expression and returns a boolean result. Returns false if the result is empty or not a boolean.

func EvaluateToString

func EvaluateToString(resource []byte, expr string) (string, error)

EvaluateToString evaluates an expression and returns a string result.

func EvaluateToStrings

func EvaluateToStrings(resource []byte, expr string) ([]string, error)

EvaluateToStrings evaluates an expression and returns all results as strings.

func Exists

func Exists(resource []byte, expr string) (bool, error)

Exists evaluates an expression and returns true if any results exist.

func MustEvaluate

func MustEvaluate(resource []byte, expr string) types.Collection

MustEvaluate is like Evaluate but panics on error.

Types

type CacheStats

type CacheStats struct {
	Size   int
	Limit  int
	Hits   int64
	Misses int64
}

CacheStats holds cache performance statistics.

type Collection

type Collection = types.Collection

Collection is an alias for types.Collection for easier external use.

func EvaluateCached

func EvaluateCached(resource []byte, expr string) (Collection, error)

EvaluateCached compiles (with caching) and evaluates a FHIRPath expression. This is the recommended function for production use.

func EvaluateResource

func EvaluateResource(resource Resource, expr string) (Collection, error)

EvaluateResource evaluates a FHIRPath expression against a Go struct. The resource is serialized to JSON first, then evaluated. For better performance with multiple evaluations, cache the JSON bytes.

func EvaluateResourceCached

func EvaluateResourceCached(resource Resource, expr string) (Collection, error)

EvaluateResourceCached is like EvaluateResource but uses the expression cache.

type EvalOption

type EvalOption func(*EvalOptions)

EvalOption is a functional option for configuring evaluation.

func WithContext

func WithContext(ctx context.Context) EvalOption

WithContext sets the context for cancellation.

func WithMaxCollectionSize

func WithMaxCollectionSize(size int) EvalOption

WithMaxCollectionSize sets the maximum output collection size.

func WithMaxDepth

func WithMaxDepth(depth int) EvalOption

WithMaxDepth sets the maximum recursion depth.

func WithModel added in v1.2.0

func WithModel(m Model) EvalOption

WithModel sets the FHIR version-specific model for the evaluation. When provided, the engine uses precise choice type lists, full type hierarchy, and path-based type resolution instead of built-in heuristics.

func WithResolver

func WithResolver(r ReferenceResolver) EvalOption

WithResolver sets the reference resolver.

func WithTimeout

func WithTimeout(d time.Duration) EvalOption

WithTimeout sets the evaluation timeout.

func WithVariable

func WithVariable(name string, value types.Collection) EvalOption

WithVariable sets an external variable.

type EvalOptions

type EvalOptions struct {
	// Context for cancellation and timeout
	Ctx context.Context

	// Timeout for evaluation (0 means no timeout)
	Timeout time.Duration

	// MaxDepth limits recursion depth for descendants() (0 means default of 100)
	MaxDepth int

	// MaxCollectionSize limits output collection size (0 means no limit)
	MaxCollectionSize int

	// Variables are external variables accessible via %name
	Variables map[string]types.Collection

	// Resolver handles reference resolution for resolve() function
	Resolver ReferenceResolver

	// Model provides FHIR version-specific type metadata.
	// When nil, the engine uses built-in heuristics.
	Model Model
}

EvalOptions configures expression evaluation.

func DefaultOptions

func DefaultOptions() *EvalOptions

DefaultOptions returns default evaluation options suitable for production.

type Expression

type Expression struct {
	// contains filtered or unexported fields
}

Expression represents a compiled FHIRPath expression.

func Compile

func Compile(expr string) (*Expression, error)

Compile parses a FHIRPath expression and returns a compiled Expression. The compiled expression can be evaluated multiple times against different resources.

func GetCached

func GetCached(expr string) (*Expression, error)

GetCached retrieves or compiles an expression using the default cache.

func MustCompile

func MustCompile(expr string) *Expression

MustCompile is like Compile but panics on error.

func MustGetCached

func MustGetCached(expr string) *Expression

MustGetCached is like GetCached but panics on error.

func (*Expression) Evaluate

func (e *Expression) Evaluate(resource []byte) (types.Collection, error)

Evaluate executes the expression against a JSON resource.

func (*Expression) EvaluateWithContext

func (e *Expression) EvaluateWithContext(ctx *eval.Context) (types.Collection, error)

EvaluateWithContext executes the expression with a custom context.

func (*Expression) EvaluateWithOptions

func (e *Expression) EvaluateWithOptions(resource []byte, opts ...EvalOption) (types.Collection, error)

EvaluateWithOptions evaluates an expression with custom options.

func (*Expression) String

func (e *Expression) String() string

String returns the original expression string.

type ExpressionCache

type ExpressionCache struct {
	// contains filtered or unexported fields
}

ExpressionCache provides thread-safe caching of compiled FHIRPath expressions with LRU eviction. Use this in production to avoid recompiling the same expressions.

func NewExpressionCache

func NewExpressionCache(limit int) *ExpressionCache

NewExpressionCache creates a new cache with the given size limit. If limit <= 0, the cache is unbounded.

func (*ExpressionCache) Clear

func (c *ExpressionCache) Clear()

Clear removes all cached expressions.

func (*ExpressionCache) Get

func (c *ExpressionCache) Get(expr string) (*Expression, error)

Get retrieves a compiled expression from the cache, compiling it if necessary.

func (*ExpressionCache) HitRate

func (c *ExpressionCache) HitRate() float64

HitRate returns the cache hit rate as a percentage (0-100).

func (*ExpressionCache) MustGet

func (c *ExpressionCache) MustGet(expr string) *Expression

MustGet is like Get but panics on error.

func (*ExpressionCache) Size

func (c *ExpressionCache) Size() int

Size returns the number of cached expressions.

func (*ExpressionCache) Stats

func (c *ExpressionCache) Stats() CacheStats

Stats returns cache performance statistics.

type Model added in v1.2.0

type Model interface {
	// ChoiceTypes returns the permitted type suffixes for a polymorphic
	// element path. For example, "Observation.value" might return
	// ["Quantity","CodeableConcept","string","boolean",...].
	// Returns nil if the path is not a choice type element.
	ChoiceTypes(path string) []string

	// TypeOf returns the FHIR type code for the given element path.
	// For example, "Patient.name" returns "HumanName".
	// Returns "" if the path is unknown.
	TypeOf(path string) string

	// ReferenceTargets returns the allowed target resource type names for
	// a Reference or canonical element path.
	// Returns nil if the path is not a reference or has no constrained targets.
	ReferenceTargets(path string) []string

	// ParentType returns the immediate parent type in the FHIR type hierarchy.
	// For example, "Patient" returns "DomainResource", "Age" returns "Quantity".
	// Returns "" if the type is unknown or has no parent.
	ParentType(typeName string) string

	// IsSubtype reports whether child is the same as, or a subtype of,
	// parent in the FHIR type hierarchy.
	IsSubtype(child, parent string) bool

	// ResolvePath resolves a path whose element definition is borrowed from
	// another location via contentReference.
	// For example, "Questionnaire.item.item" returns "Questionnaire.item".
	// Returns the original path if no redirection is needed.
	ResolvePath(path string) string

	// IsResource reports whether the given type name is a known FHIR resource type.
	IsResource(typeName string) bool
}

Model provides FHIR version-specific type and path metadata for the FHIRPath engine. When a Model is supplied via WithModel, the evaluator uses it for precise polymorphic field resolution, type hierarchy checking, and path-based type inference. When nil (the default), the engine falls back to its built-in heuristics.

This interface is satisfied by gofhir/models/r4.FHIRPathModelData (and the r4b/r5 equivalents) via Go's structural typing — no import dependency is required between the two packages.

type ReferenceResolver

type ReferenceResolver interface {
	// Resolve takes a reference string (e.g., "Patient/123") and returns the resource.
	Resolve(ctx context.Context, reference string) ([]byte, error)
}

ReferenceResolver resolves FHIR references for the resolve() function.

type Resource

type Resource interface {
	GetResourceType() string
}

Resource represents any FHIR resource that can be evaluated.

type ResourceJSON

type ResourceJSON struct {
	// contains filtered or unexported fields
}

ResourceJSON wraps a resource with its pre-serialized JSON for efficient repeated evaluation.

func MustNewResourceJSON

func MustNewResourceJSON(resource Resource) *ResourceJSON

MustNewResourceJSON is like NewResourceJSON but panics on error.

func NewResourceJSON

func NewResourceJSON(resource Resource) (*ResourceJSON, error)

NewResourceJSON creates a ResourceJSON from a Go resource.

func (*ResourceJSON) Evaluate

func (r *ResourceJSON) Evaluate(expr string) (Collection, error)

Evaluate evaluates a FHIRPath expression against this resource.

func (*ResourceJSON) EvaluateCached

func (r *ResourceJSON) EvaluateCached(expr string) (Collection, error)

EvaluateCached evaluates using the expression cache.

func (*ResourceJSON) JSON

func (r *ResourceJSON) JSON() []byte

JSON returns the pre-serialized JSON bytes.

func (*ResourceJSON) Resource

func (r *ResourceJSON) Resource() Resource

Resource returns the original Go resource.

type Value

type Value = types.Value

Value is an alias for types.Value for easier external use.

Directories

Path Synopsis
Package eval provides the FHIRPath expression evaluator.
Package eval provides the FHIRPath expression evaluator.
Package funcs provides FHIRPath function implementations.
Package funcs provides FHIRPath function implementations.
internal
ucum
Package ucum provides UCUM (Unified Code for Units of Measure) normalization for FHIR quantity search parameters.
Package ucum provides UCUM (Unified Code for Units of Measure) normalization for FHIR quantity search parameters.
parser
Package types defines the FHIRPath type system.
Package types defines the FHIRPath type system.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL