Skip to main content

Implementation Features

FeatureSupportNotes
Compartment SearchFullSingle resource type or all types (*)
GET SearchFullQuery string parameters
POST SearchFullForm-urlencoded body
CompartmentDefinition HookFullAutomatic membership table updates
Temporal BoundariesFullstartParam and endParam support
Multiple ParametersFullOR logic for membership parameters
Dynamic ConfigurationFullRuntime updates via CompartmentDefinition resources
All Search FeaturesFullIncludes, pagination, filters work within compartments

Endpoints

Zunder supports the standard FHIR compartment search endpoints:
ScopeGETPOSTNotes
Specific Type/fhir/{Compartment}/{id}/{ResourceType}/fhir/{Compartment}/{id}/{ResourceType}/_searchSearch one resource type
All Types/fhir/{Compartment}/{id}/*/fhir/{Compartment}/{id}/_searchSearch all compartment types
Examples:
# Search for Observations in Patient/123's compartment
curl "http://localhost:8080/fhir/Patient/123/Observation?code=http://loinc.org|29463-7"

# Search for all resources in Patient/123's compartment
curl "http://localhost:8080/fhir/Patient/123/*"

# POST-based compartment search
curl -X POST "http://localhost:8080/fhir/Patient/123/Observation/_search" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "code=http://loinc.org|29463-7&status=final"

# All-types compartment search with type filtering
curl "http://localhost:8080/fhir/Patient/123/*?_type=Observation,Condition"
The literal * in the URL path means “all resource types in this compartment”. For POST searches, use the /_search endpoint without the *.

How Compartments Work

Compartment Membership

Resources belong to a compartment when they reference the compartment resource through specific search parameters. For example:
  • Patient Compartment: Resources with patient, subject, or performer parameters pointing to a Patient
  • Encounter Compartment: Resources with encounter or context parameters pointing to an Encounter
  • Device Compartment: Resources with device or subject parameters pointing to a Device

CompartmentDefinition Resources

Zunder uses CompartmentDefinition resources to define compartment membership rules. When you create or update a CompartmentDefinition, the server automatically:
  1. Parses the compartment type (e.g., “Patient”, “Encounter”)
  2. Extracts resource membership rules from the resource[] array
  3. Updates the compartment_memberships database table
  4. Enables compartment searches for that compartment type
Example CompartmentDefinition:
{
  "resourceType": "CompartmentDefinition",
  "id": "patient",
  "code": "Patient",
  "search": true,
  "resource": [
    {
      "code": "Patient",
      "param": ["{def}"]
    },
    {
      "code": "Observation",
      "param": ["patient", "performer"]
    },
    {
      "code": "DiagnosticReport",
      "param": ["subject"]
    },
    {
      "code": "Account",
      "param": ["patient"],
      "startParam": "period",
      "endParam": "period"
    }
  ]
}

Special Parameter Values

  • {def}: The compartment resource itself (e.g., Patient in Patient compartment)
  • Multiple parameters: OR logic - resource is in compartment if ANY parameter matches
  • Empty param array: Resource type is NOT in this compartment

Temporal Boundaries

Some resources have time-based membership using startParam and endParam:
{
  "code": "Account",
  "param": ["patient"],
  "startParam": "period",
  "endParam": "period"
}
This means an Account is only in the Patient compartment during its period date range.

Database Schema

Zunder stores compartment membership rules in the compartment_memberships table:
CREATE TABLE compartment_memberships (
    compartment_type VARCHAR(64) NOT NULL,     -- e.g., "Patient", "Encounter"
    resource_type VARCHAR(64) NOT NULL,        -- e.g., "Observation", "Condition"
    parameter_names TEXT[] NOT NULL,           -- e.g., ["patient", "subject"]
    start_param VARCHAR(64),                   -- Optional temporal boundary
    end_param VARCHAR(64),                     -- Optional temporal boundary
    loaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    PRIMARY KEY (compartment_type, resource_type)
);

Search Examples

# Find all Observations for Patient/123
curl "http://localhost:8080/fhir/Patient/123/Observation"

# Find final lab results for Patient/123
curl "http://localhost:8080/fhir/Patient/123/Observation?category=laboratory&status=final"

# Find all resources in Patient/123's compartment
curl "http://localhost:8080/fhir/Patient/123/*"

Advanced Search Features

All standard FHIR search features work within compartments:
# Compartment search with includes
curl "http://localhost:8080/fhir/Patient/123/DiagnosticReport?_include=DiagnosticReport:result"

# Compartment search with pagination
curl "http://localhost:8080/fhir/Patient/123/Observation?_count=10"

# Compartment search with chaining
curl "http://localhost:8080/fhir/Patient/123/DiagnosticReport?result.code=http://loinc.org|33747-0"

# Compartment search with _filter
curl -G "http://localhost:8080/fhir/Patient/123/Observation" \
  --data-urlencode '_filter=status eq final and category co laboratory'

# Compartment search with summary
curl "http://localhost:8080/fhir/Patient/123/Observation?_summary=count"
# Search specific types in compartment
curl "http://localhost:8080/fhir/Patient/123/*?_type=Observation,Condition,DiagnosticReport"

# Exclude certain types (using _filter)
curl -G "http://localhost:8080/fhir/Patient/123/*" \
  --data-urlencode '_filter=not (resourceType eq AuditEvent)'

Managing CompartmentDefinitions

Creating a CompartmentDefinition

curl -X POST "http://localhost:8080/fhir/CompartmentDefinition" \
  -H "Content-Type: application/fhir+json" \
  -d '{
    "resourceType": "CompartmentDefinition",
    "id": "encounter",
    "code": "Encounter",
    "search": true,
    "resource": [
      {
        "code": "Encounter",
        "param": ["{def}"]
      },
      {
        "code": "Observation",
        "param": ["encounter", "context"]
      },
      {
        "code": "DiagnosticReport",
        "param": ["encounter", "context"]
      }
    ]
  }'

Updating Compartment Rules

When you update a CompartmentDefinition, Zunder automatically:
  1. Clears existing membership rules for that compartment type
  2. Rebuilds the rules from the updated definition
  3. Applies changes immediately to new searches
curl -X PUT "http://localhost:8080/fhir/CompartmentDefinition/encounter" \
  -H "Content-Type: application/fhir+json" \
  -d '{
    "resourceType": "CompartmentDefinition",
    "id": "encounter",
    "code": "Encounter",
    "search": true,
    "resource": [
      {
        "code": "Encounter",
        "param": ["{def}"]
      },
      {
        "code": "Observation",
        "param": ["encounter"]
      }
    ]
  }'

Deleting CompartmentDefinitions

Per FHIR spec, servers may continue using compartment definitions even after deletion. Zunder keeps the membership rules unless explicitly replaced:
# This deletes the CompartmentDefinition resource but keeps the compartment functional
curl -X DELETE "http://localhost:8080/fhir/CompartmentDefinition/encounter"
To fully disable a compartment, create a CompartmentDefinition with an empty resource array.

Configuration

Compartment search can be disabled in the server configuration:
fhir:
  interactions:
    compartment:
      search: false # Disable compartment search endpoints

Performance Considerations

  • Indexing: Zunder uses GIN indexes on parameter_names for efficient membership queries
  • Caching: Compartment membership rules are loaded once per search and cached
  • Query Optimization: The search engine builds optimized SQL queries with proper JOINs
  • Resource Types: Searching specific resource types is more efficient than all-types searches

Limitations

  • Compartment Instance Validation: Zunder does not validate that the compartment resource (e.g., Patient/123) exists
  • Empty Results: Non-existent compartment instances return empty search results rather than errors
  • Temporal Boundaries: Currently stored but not yet fully implemented in search logic

Error Handling

# Unknown compartment type
curl "http://localhost:8080/fhir/UnknownType/123/Observation"
# Returns: 400 Bad Request - Unsupported resource type

# Unknown resource type in compartment
curl "http://localhost:8080/fhir/Patient/123/UnknownType"
# Returns: 400 Bad Request - Unsupported resource type

# Malformed compartment ID
curl "http://localhost:8080/fhir/Patient//Observation"
# Returns: 404 Not Found