Skip to main content

Journey Overview

Patient Chart Search empowers clinicians to quickly find critical information within patient records using natural language queries. It leverages semantic understanding to deliver fast, accurate, and context-aware results across notes, labs, medications, and more. For clinicians, it reduces time spent searching and supports informed decision-making. For health systems, it enhances clinical efficiency and improves documentation quality. For patients, it contributes to safer, more responsive care.
If the above resonates with your use case, you are at the right place. Let’s dive into how you can deploy your very own patient chart search experience using Clinia’s Health-Grade Search (HGS) technology with federated search capababilities that enable seamless querying across multiple patient data sources. Federated search is a core feature that enables Patient Chart Search to deliver comprehensive results by simultaneously querying multiple disparate data sources within a health system. This capability allows clinicians to search across different resources, such as:
  • Clinical notes from various EMR systems
  • Allergy and intolerance records
  • Condition and diagnosis information
  • Encounter and visit details from multiple systems
  • Medication statements from pharmacy systems
  • Procedure records from clinical systems
  • Vital signs and laboratory observations
  • Immunization records
  • Diagnostic reports from multiple departments
  • Historical records from legacy systems
  • And many more!
By federating these searches, clinicians get a unified view of patient information without needing to know where specific data resides, dramatically reducing search time and improving clinical decision-making. It’s also the fastest way to interrogate this sort of data, a single query being able to surface data across modalities instead of running numerous concurrent inquiries.

Getting Started

To get a broad understanding of the components within our data fabric, you can refer to our platform overview. To get started in this journey, you will need:
  1. A Clinia workspace
  2. A Clinia service account (API Key)
  3. Ability to execute HTTP requests
  4. Some data to ingest
We will go over a federated multi-collection use case that demonstrates how to search across different types of FHIR-compliant patient data. Make sure to refer to the documentation if you want to tailor the Data Fabric configuration to your exact use case. For the sake of this journey, we’ll leverage the following sample FHIR-compliant patient data across different collections:
{
  "noteId": "NOTE-2024-001",
  "patientId": "PAT-12345",
  "type": "progress_note",
  "date": "2024-01-15T10:30:00Z",
  "provider": "Dr. Sarah Johnson, MD",
  "content": "Patient presents with acute chest pain onset 2 hours ago. Pain described as sharp, substernal, radiating to left arm. Associated with shortness of breath and diaphoresis. Patient has history of hypertension and diabetes mellitus type 2. On examination: BP 150/95, HR 95, RR 22, O2 sat 96% on room air. ECG shows ST depression in leads V4-V6. Troponin I elevated at 0.8 ng/mL. Plan: Admit for further cardiac workup, start heparin protocol, cardiology consultation.",
  "title": "Emergency Department Progress Note - Chest Pain"
}

Workspace Configuration

To leverage semantic search and Clinia’s query understanding capabilities, you will need a HGS partition instead of our Standard partition offering.

Create a Data Source

Documentation Currently, the only data source type available is a Registry. To create your {name} data source, run the following request:
curl --location --request PUT 'https://{workspaceId}.clinia.cloud/catalog/v1/sources/{name}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-Clinia-Api-Key: YOUR_API_KEY_HERE' \
--data '{
    "type": "registry"
}'

Create your Patient Chart Profiles

FHIR to Clinia Platform Mapping: The profiles below are designed to work with the included Python mapping script (fhir_to_clinia_mapper.py) that converts FHIR resources to Clinia Platform datatypes. This provides a realistic approach where you transform your FHIR data before ingestion.
Documentation Before ingesting your data, we need to define the schema of the properties representing your FHIR-compliant patient data model. For federated search to work effectively, we’ll create multiple profiles for different types of patient data. The data will then be ingested separately into the data source, allowing for granular search across different patient data types.
curl --location --globoff --request PUT 'https://{workspaceId}.clinia.cloud/sources/patient_data/v1/profiles/clinical_note' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --header 'X-Clinia-Api-Key: YOUR_API_KEY_HERE' \
  --data '{
  "type": "ROOT",
  "properties": {
    "noteId": {
      "type": "symbol"
    },
    "patientId": {
      "type": "identifier"
    },
    "type": {
      "type": "symbol"
    },
    "date": {
      "type": "datetime"
    },
    "provider": {
      "type": "symbol"
    },
    "content": {
      "type": "markdown"
    },
    "title": {
      "type": "symbol"
    }
  }
}'
Source profiles definition uses the Clinia Data Types System and relies on composition to allow a truly flexible data modelling experience.

FHIR to Clinia Platform Mapping Script

The profiles above are designed to work with proper Clinia Platform datatypes. Since most healthcare systems work with FHIR data, we’ve provided a Python script that helps transform FHIR resources into the correct Clinia Platform format before ingestion.
We’ve created a comprehensive Python script using Pydantic v2 models that handles the conversion from FHIR resources to Clinia Platform datatypes. This script includes:
  • Pydantic Models: Type-safe models for both FHIR input and Clinia Platform output formats
  • Validation: Automatic validation of data structure during conversion
  • Identifier Handling: Proper conversion of patient references to Clinia Platform identifier datatype
  • Comprehensive Mapping: Support for all FHIR resource types used in patient chart search

Key Features of the Mapping Script

  1. Patient ID Normalization: Converts FHIR patient references (Patient/PAT-12345) to proper Clinia Platform identifiers
  2. Complex Object Mapping: Handles nested FHIR structures like Coding, Reference, and Period datatypes
  3. Bulk Operation Support: Generates the correct format for Clinia’s bulk ingestion API
  4. Type Safety: Uses Pydantic v2 for runtime validation and IDE support

Example Usage

from fhir_to_clinia_mapper import FHIRToCliniaMapper

# Your FHIR AllergyIntolerance resource
fhir_allergy = {
    "resourceType": "AllergyIntolerance",
    "id": "allergy-001",
    "subject": {"reference": "Patient/PAT-12345"},
    "code": {
        "coding": [{
            "system": "http://snomed.info/sct",
            "code": "387207008",
            "display": "Penicillin"
        }]
    },
    "category": ["medication"],
    "criticality": "high"
}

# Convert to Clinia Platform format
mapper = FHIRToCliniaMapper()
clinia_allergy = mapper.map_allergy(fhir_allergy)

# Generate bulk operation
bulk_op = {
    "action": "CREATE",
    "create": {
        "type": "allergy",
        **clinia_allergy.model_dump(exclude_none=True)
    }
}

Installation Requirements

pip install pydantic>=2.0.0
The complete script (fhir_to_clinia_mapper.py) is included in this documentation and provides mapping functions for all supported FHIR resource types.

FHIR to Clinia Platform Mapping Files

A Github Repository to help you quickstart your Clinia Journey is coming soon.
These files provide a complete solution for converting your FHIR data to the Clinia Platform format required by Clinia’s patient chart search system.
"""
FHIR to Clinia Platform Datatypes Mapper

This script provides Pydantic models and mapping functions to convert FHIR resources
to Clinia Platform datatypes for ingestion into Clinia's patient chart search system.
"""

from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field


# Clinia Platform Datatypes Models
class CliniaAddress(BaseModel):
    """Clinia Platform Address datatype"""

    use: Optional[str] = None
    type: Optional[str] = None
    text: Optional[str] = None
    line: Optional[List[str]] = None
    city: Optional[str] = None
    district: Optional[str] = None
    state: Optional[str] = None
    postalCode: Optional[str] = None
    country: Optional[str] = None
    period: Optional[Dict[str, Any]] = None


class CliniaIdentifier(BaseModel):
    """Clinia Platform Identifier datatype"""

    system: Optional[str] = None
    value: Optional[str] = None
    use: Optional[str] = None
    period: Optional[Dict[str, Any]] = None


class CliniaCoding(BaseModel):
    """Clinia Platform Coding datatype"""

    system: Optional[str] = None
    version: Optional[str] = None
    code: Optional[str] = None
    display: Optional[str] = None


class CliniaReference(BaseModel):
    """Clinia Platform Reference datatype"""

    reference: Optional[str] = None
    type: Optional[str] = None
    identifier: Optional[CliniaIdentifier] = None
    display: Optional[str] = None


class CliniaPeriod(BaseModel):
    """Clinia Platform Period datatype"""

    start: Optional[str] = None
    end: Optional[str] = None


class CliniaQuantity(BaseModel):
    """Clinia Platform Quantity datatype for observations"""

    value: Optional[float] = None
    unit: Optional[str] = None
    system: Optional[str] = None
    code: Optional[str] = None


# FHIR Input Models
class FHIRCoding(BaseModel):
    system: Optional[str] = None
    code: Optional[str] = None
    display: Optional[str] = None


class FHIRReference(BaseModel):
    reference: Optional[str] = None
    display: Optional[str] = None


class FHIRPeriod(BaseModel):
    start: Optional[str] = None
    end: Optional[str] = None


class FHIRQuantity(BaseModel):
    value: Optional[float] = None
    unit: Optional[str] = None


# Clinia Platform Profile Models (for ingestion)
class CliniaClinicalNote(BaseModel):
    """Clinia Platform Clinical Note profile"""

    noteId: str
    patientId: CliniaIdentifier
    type: str
    date: str
    provider: Optional[str] = None
    content: str
    title: Optional[str] = None


class CliniaAllergy(BaseModel):
    """Clinia Platform Allergy profile"""

    patientId: CliniaIdentifier
    code: CliniaCoding
    category: List[str]
    criticality: Optional[str] = None
    reaction: Optional[List[Dict[str, Any]]] = None
    recordedDate: Optional[str] = None
    note: Optional[List[Dict[str, str]]] = None


class CliniaCondition(BaseModel):
    """Clinia Platform Condition profile"""

    patientId: CliniaIdentifier
    code: CliniaCoding
    category: List[CliniaCoding]
    clinicalStatus: CliniaCoding
    onsetDateTime: Optional[str] = None
    note: Optional[List[Dict[str, str]]] = None


class CliniaEncounter(BaseModel):
    """Clinia Platform Encounter profile"""

    patientId: CliniaIdentifier
    status: str
    class_: CliniaCoding = Field(alias="class")
    period: Optional[CliniaPeriod] = None
    reasonCode: Optional[List[CliniaCoding]] = None
    diagnosis: Optional[List[Dict[str, Any]]] = None
    hospitalization: Optional[Dict[str, Any]] = None


class CliniaMedication(BaseModel):
    """Clinia Platform Medication profile"""

    patientId: CliniaIdentifier
    medicationCodeableConcept: CliniaCoding
    status: str
    effectiveDateTime: Optional[str] = None
    dosage: Optional[List[Dict[str, Any]]] = None
    reasonReference: Optional[List[CliniaReference]] = None
    note: Optional[List[Dict[str, str]]] = None


class CliniaProcedure(BaseModel):
    """Clinia Platform Procedure profile"""

    patientId: CliniaIdentifier
    status: str
    code: CliniaCoding
    performedDateTime: Optional[str] = None
    reasonReference: Optional[List[CliniaReference]] = None
    note: Optional[List[Dict[str, str]]] = None


class CliniaObservation(BaseModel):
    """Clinia Platform Observation profile"""

    patientId: CliniaIdentifier
    status: str
    category: List[CliniaCoding]
    code: CliniaCoding
    effectiveDateTime: Optional[str] = None
    component: Optional[List[Dict[str, Any]]] = None
    valueQuantity: Optional[CliniaQuantity] = None


class CliniaImmunization(BaseModel):
    """Clinia Platform Immunization profile"""

    patientId: CliniaIdentifier
    status: str
    vaccineCode: CliniaCoding
    occurrenceDateTime: Optional[str] = None
    location: Optional[CliniaReference] = None
    lotNumber: Optional[str] = None
    expirationDate: Optional[str] = None
    note: Optional[List[Dict[str, str]]] = None


class CliniaDiagnosticReport(BaseModel):
    """Clinia Platform Diagnostic Report profile"""

    patientId: CliniaIdentifier
    status: str
    category: List[CliniaCoding]
    code: CliniaCoding
    effectiveDateTime: Optional[str] = None
    issued: Optional[str] = None
    result: Optional[List[CliniaReference]] = None
    conclusion: Optional[str] = None


# Mapping Functions
class FHIRToCliniaMapper:
    """Main mapper class for converting FHIR resources to Clinia Platform datatypes"""

    @staticmethod
    def extract_patient_id(fhir_reference: str) -> CliniaIdentifier:
        """Extract patient ID from FHIR reference and convert to Clinia Platform Identifier"""
        if fhir_reference.startswith("Patient/"):
            patient_id = fhir_reference.replace("Patient/", "")
            return CliniaIdentifier(
                system="Patient",
                value=patient_id,
                use="usual",
            )
        return CliniaIdentifier(value=fhir_reference, use="usual")

    @staticmethod
    def map_coding(fhir_coding: Dict[str, Any]) -> CliniaCoding:
        """Map FHIR Coding to Clinia Platform Coding"""
        return CliniaCoding(
            system=fhir_coding.get("system"),
            code=fhir_coding.get("code"),
            display=fhir_coding.get("display"),
        )

    @staticmethod
    def map_reference(fhir_reference: Dict[str, Any]) -> CliniaReference:
        """Map FHIR Reference to Clinia Platform Reference"""
        return CliniaReference(
            reference=fhir_reference.get("reference"),
            display=fhir_reference.get("display"),
        )

    @staticmethod
    def map_period(fhir_period: Dict[str, Any]) -> CliniaPeriod:
        """Map FHIR Period to Clinia Platform Period"""
        return CliniaPeriod(start=fhir_period.get("start"), end=fhir_period.get("end"))

    @staticmethod
    def map_clinical_note(fhir_note: Dict[str, Any]) -> CliniaClinicalNote:
        """Map FHIR clinical note to Clinia Platform Clinical Note"""
        patient_id = FHIRToCliniaMapper.extract_patient_id(fhir_note["patientId"])

        return CliniaClinicalNote(
            noteId=fhir_note["noteId"],
            patientId=patient_id,
            type=fhir_note["type"],
            date=fhir_note["date"],
            provider=fhir_note.get("provider"),
            content=fhir_note["content"],
            title=fhir_note.get("title"),
        )

    @staticmethod
    def map_allergy(fhir_allergy: Dict[str, Any]) -> CliniaAllergy:
        """Map FHIR AllergyIntolerance to Clinia Platform Allergy"""
        subject_ref = fhir_allergy["subject"]["reference"]
        patient_id = FHIRToCliniaMapper.extract_patient_id(subject_ref)

        # Map the main code
        code_coding = fhir_allergy["code"]["coding"][0]
        code = FHIRToCliniaMapper.map_coding(code_coding)

        return CliniaAllergy(
            patientId=patient_id,
            code=code,
            category=fhir_allergy["category"],
            criticality=fhir_allergy.get("criticality"),
            reaction=fhir_allergy.get("reaction"),
            recordedDate=fhir_allergy.get("recordedDate"),
            note=fhir_allergy.get("note"),
        )

    @staticmethod
    def map_condition(fhir_condition: Dict[str, Any]) -> CliniaCondition:
        """Map FHIR Condition to Clinia Platform Condition"""
        subject_ref = fhir_condition["subject"]["reference"]
        patient_id = FHIRToCliniaMapper.extract_patient_id(subject_ref)

        # Map the main code
        code_coding = fhir_condition["code"]["coding"][0]
        code = FHIRToCliniaMapper.map_coding(code_coding)

        # Map categories
        categories = []
        if "category" in fhir_condition:
            for cat in fhir_condition["category"]:
                if "coding" in cat:
                    categories.append(FHIRToCliniaMapper.map_coding(cat["coding"][0]))

        # Map clinical status
        clinical_status = FHIRToCliniaMapper.map_coding(
            fhir_condition["clinicalStatus"]["coding"][0]
        )

        return CliniaCondition(
            patientId=patient_id,
            code=code,
            category=categories,
            clinicalStatus=clinical_status,
            onsetDateTime=fhir_condition.get("onsetDateTime"),
            note=fhir_condition.get("note"),
        )

    @staticmethod
    def map_encounter(fhir_encounter: Dict[str, Any]) -> CliniaEncounter:
        """Map FHIR Encounter to Clinia Platform Encounter"""
        subject_ref = fhir_encounter["subject"]["reference"]
        patient_id = FHIRToCliniaMapper.extract_patient_id(subject_ref)

        # Map class
        class_coding = FHIRToCliniaMapper.map_coding(fhir_encounter["class"])

        # Map period if present
        period = None
        if "period" in fhir_encounter:
            period = FHIRToCliniaMapper.map_period(fhir_encounter["period"])

        # Map reason codes
        reason_codes = []
        if "reasonCode" in fhir_encounter:
            for reason in fhir_encounter["reasonCode"]:
                if "coding" in reason:
                    reason_codes.append(FHIRToCliniaMapper.map_coding(reason["coding"][0]))

        return CliniaEncounter(
            patientId=patient_id,
            status=fhir_encounter["status"],
            class_=class_coding,
            period=period,
            reasonCode=reason_codes if reason_codes else None,
            diagnosis=fhir_encounter.get("diagnosis"),
            hospitalization=fhir_encounter.get("hospitalization"),
        )

    @staticmethod
    def map_medication(fhir_medication: Dict[str, Any]) -> CliniaMedication:
        """Map FHIR MedicationStatement to Clinia Platform Medication"""
        subject_ref = fhir_medication["subject"]["reference"]
        patient_id = FHIRToCliniaMapper.extract_patient_id(subject_ref)

        # Map medication code
        med_coding = fhir_medication["medicationCodeableConcept"]["coding"][0]
        medication_code = FHIRToCliniaMapper.map_coding(med_coding)

        # Map reason references
        reason_refs = []
        if "reasonReference" in fhir_medication:
            for ref in fhir_medication["reasonReference"]:
                reason_refs.append(FHIRToCliniaMapper.map_reference(ref))

        return CliniaMedication(
            patientId=patient_id,
            medicationCodeableConcept=medication_code,
            status=fhir_medication["status"],
            effectiveDateTime=fhir_medication.get("effectiveDateTime"),
            dosage=fhir_medication.get("dosage"),
            reasonReference=reason_refs if reason_refs else None,
            note=fhir_medication.get("note"),
        )

    @staticmethod
    def map_procedure(fhir_procedure: Dict[str, Any]) -> CliniaProcedure:
        """Map FHIR Procedure to Clinia Platform Procedure"""
        subject_ref = fhir_procedure["subject"]["reference"]
        patient_id = FHIRToCliniaMapper.extract_patient_id(subject_ref)

        # Map procedure code
        proc_coding = fhir_procedure["code"]["coding"][0]
        procedure_code = FHIRToCliniaMapper.map_coding(proc_coding)

        # Map reason references
        reason_refs = []
        if "reasonReference" in fhir_procedure:
            for ref in fhir_procedure["reasonReference"]:
                reason_refs.append(FHIRToCliniaMapper.map_reference(ref))

        return CliniaProcedure(
            patientId=patient_id,
            status=fhir_procedure["status"],
            code=procedure_code,
            performedDateTime=fhir_procedure.get("performedDateTime"),
            reasonReference=reason_refs if reason_refs else None,
            note=fhir_procedure.get("note"),
        )

    @staticmethod
    def map_observation(fhir_observation: Dict[str, Any]) -> CliniaObservation:
        """Map FHIR Observation to Clinia Platform Observation"""
        subject_ref = fhir_observation["subject"]["reference"]
        patient_id = FHIRToCliniaMapper.extract_patient_id(subject_ref)

        # Map categories
        categories = []
        for cat in fhir_observation["category"]:
            if "coding" in cat:
                categories.append(FHIRToCliniaMapper.map_coding(cat["coding"][0]))

        # Map observation code
        obs_coding = fhir_observation["code"]["coding"][0]
        observation_code = FHIRToCliniaMapper.map_coding(obs_coding)

        # Map value quantity if present
        value_quantity = None
        if "valueQuantity" in fhir_observation:
            vq = fhir_observation["valueQuantity"]
            value_quantity = CliniaQuantity(
                value=vq.get("value"),
                unit=vq.get("unit"),
                system=vq.get("system"),
                code=vq.get("code"),
            )

        return CliniaObservation(
            patientId=patient_id,
            status=fhir_observation["status"],
            category=categories,
            code=observation_code,
            effectiveDateTime=fhir_observation.get("effectiveDateTime"),
            component=fhir_observation.get("component"),
            valueQuantity=value_quantity,
        )

    @staticmethod
    def map_immunization(fhir_immunization: Dict[str, Any]) -> CliniaImmunization:
        """Map FHIR Immunization to Clinia Platform Immunization"""
        # Handle both 'patient' and 'subject' reference fields
        patient_ref = fhir_immunization.get("patient", {}).get("reference") or fhir_immunization.get("subject", {}).get("reference")
        patient_id = FHIRToCliniaMapper.extract_patient_id(patient_ref)

        # Map vaccine code
        vaccine_coding = fhir_immunization["vaccineCode"]["coding"][0]
        vaccine_code = FHIRToCliniaMapper.map_coding(vaccine_coding)

        # Map location reference if present
        location_ref = None
        if "location" in fhir_immunization:
            location_ref = FHIRToCliniaMapper.map_reference(fhir_immunization["location"])

        return CliniaImmunization(
            patientId=patient_id,
            status=fhir_immunization["status"],
            vaccineCode=vaccine_code,
            occurrenceDateTime=fhir_immunization.get("occurrenceDateTime"),
            location=location_ref,
            lotNumber=fhir_immunization.get("lotNumber"),
            expirationDate=fhir_immunization.get("expirationDate"),
            note=fhir_immunization.get("note"),
        )

    @staticmethod
    def map_diagnostic_report(fhir_diagnostic: Dict[str, Any]) -> CliniaDiagnosticReport:
        """Map FHIR DiagnosticReport to Clinia Platform DiagnosticReport"""
        subject_ref = fhir_diagnostic["subject"]["reference"]
        patient_id = FHIRToCliniaMapper.extract_patient_id(subject_ref)

        # Map categories
        categories = []
        for cat in fhir_diagnostic["category"]:
            if "coding" in cat:
                categories.append(FHIRToCliniaMapper.map_coding(cat["coding"][0]))

        # Map diagnostic code
        diag_coding = fhir_diagnostic["code"]["coding"][0]
        diagnostic_code = FHIRToCliniaMapper.map_coding(diag_coding)

        # Map result references
        result_refs = []
        if "result" in fhir_diagnostic:
            for ref in fhir_diagnostic["result"]:
                result_refs.append(FHIRToCliniaMapper.map_reference(ref))

        return CliniaDiagnosticReport(
            patientId=patient_id,
            status=fhir_diagnostic["status"],
            category=categories,
            code=diagnostic_code,
            effectiveDateTime=fhir_diagnostic.get("effectiveDateTime"),
            issued=fhir_diagnostic.get("issued"),
            result=result_refs if result_refs else None,
            conclusion=fhir_diagnostic.get("conclusion"),
        )


# Example usage
def main():
    """Example usage of the FHIR to Clinia Platform mapper"""

    # Example FHIR AllergyIntolerance
    fhir_allergy = {
        "resourceType": "AllergyIntolerance",
        "id": "allergy-001",
        "subject": {"reference": "Patient/PAT-12345"},
        "code": {
            "coding": [
                {
                    "system": "http://snomed.info/sct",
                    "code": "387207008",
                    "display": "Penicillin",
                }
            ]
        },
        "category": ["medication"],
        "criticality": "high",
        "reaction": [
            {
                "manifestation": [
                    {
                        "coding": [
                            {
                                "system": "http://snomed.info/sct",
                                "code": "271807003",
                                "display": "Skin rash",
                            }
                        ]
                    }
                ],
                "severity": "moderate",
            }
        ],
        "recordedDate": "2023-03-15T09:00:00Z",
        "note": [
            {
                "text": "Patient developed generalized urticaria after receiving penicillin injection"
            }
        ],
    }

    # Map to Clinia Platform format
    mapper = FHIRToCliniaMapper()
    clinia_allergy = mapper.map_allergy(fhir_allergy)

    # Print the result
    print("Clinia Platform Allergy:")
    print(clinia_allergy.model_dump(exclude_none=True))

    # Generate bulk operation format
    bulk_operation = {
        "action": "CREATE",
        "create": {"type": "allergy", **clinia_allergy.model_dump(exclude_none=True)},
    }

    print("\nBulk Operation:")
    print(bulk_operation)


if __name__ == "__main__":
    main()

Create your Data Partition

Documentation Once your profile is created, we can now create the data partition, which is a virtual, searchable view of your data. Let’s create a patient_chart_search partition for your EMR search application. Specifying the HEALTH_GRADE_SEARCH is key to indicating that we want this partition to be built so that we can use semantic search functionalities instead of regular keyword search.
curl --location --globoff --request PUT 'https://{workspaceId}.clinia.cloud/catalog/v1/partitions/patient_chart_search' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-Clinia-Api-Key: YOUR_API_KEY_HERE' \
--data '{
  "modules": {
    "search": "HEALTH_GRADE_SEARCH"
  },
  "source": {
    "type": "DATA_SOURCE",
    "key": "patient_data",
    "collections": [
      {
        "key": "clinical_note"
      },
      {
        "key": "allergy"
      },
      {
        "key": "condition"
      },
      {
        "key": "encounter"
      },
      {
        "key": "medication"
      },
      {
        "key": "procedure"
      },
      {
        "key": "observation"
      },
      {
        "key": "immunization"
      },
      {
        "key": "diagnostic_report"
      }
    ]
  }
}'
This federated partition enables clinicians to search across all FHIR-compliant patient data types with a single query, providing comprehensive results that span clinical notes, allergies, conditions, encounters, medications, procedures, observations, immunizations, and diagnostic reports.

Ingestion Pipeline

Documentation Now into the fun stuff. To leverage Clinia’s HGS engine, you will need to augment your raw data using our various processors. For patient chart search, we typically deal with diverse types of clinical content - from dense clinical notes to structured FHIR resources. While dense retrievers excel at understanding clinical context, the challenge in a federated search system is to return the most relevant information from across all patient data sources to feed to the LLM in a token-efficient manner. For Clinical Notes, we use the Chunker processor to break down lengthy narrative text into meaningful segments. For FHIR resources (Allergies, Conditions, Encounters, Medications, Procedures, Observations, Immunizations, and DiagnosticReports), we apply the Vectorizer processor directly to create semantic representations of the structured clinical data. The Segmenter takes as input a symbol or markdown data type and returns a list of chunks. Internally, chunks are object datatypes, with a text symbol property that we can then search on — or apply other processors to! To properly support semantic search across patient charts, we will also need a Vectorizer processor to create semantic representations of the clinical content. We recommend using our mte-base-clinical model to do this as it was expressly trained on clinical data and designed to work well in patient care workflows. The Vectorizer takes as input symbol data types and returns vectors (arrays of float-value points) representing your data in the vector space. This vector space is built in such a way that semantically related ideas or sentences (e.g. “diabetes” and “hyperglycemia”) are closer together and dissimilar ideas (e.g. “banana” and “psychologist”) are farther apart. In the context of patient chart search, we will focus on generating vectors for properties where traditional keyword search may not be sufficient and that can benefit from semantic understanding (synonyms, hierarchical relationships, etc.). Since FHIR data contains a great number of clinical coding datatypes, we will use our model specialized in clinical terminology that is most efficient at representing clinical concept names and labels, mte-base-clinical.1.
{
  "steps": [
    // will chunk the 'content' into 'content.chunks'
    {
      "type": "SEGMENTER",
      "segmenter": {
        "inputProperty": "content",
        "propertyKey": "chunks",
        "modelId": "clinia-chunk.1"
      }
    },
    // will create a semantic representation of the 'content.chunks' into 'content.chunks.vector'
    {
      "type": "VECTORIZER",
      "vectorizer": {
        "inputProperty": "content.chunks",
        "propertyKey": "vector",
        "modelId": "mte-base-clinical.1"
      }
    }
  ]
}
You can add these pipelines to your collections with the following requests:
curl --location --globoff --request PUT 'https://{workspaceId}.clinia.cloud/catalog/sources/patient_data/v1/collections/clinical_note/pipeline' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --header 'X-Clinia-Api-Key: YOUR_API_KEY_HERE' \
  --data '{
  "steps": [
    {
      "type": "SEGMENTER",
      "segmenter": {
        "inputProperty": "content",
        "propertyKey": "chunks",
        "modelId": "clinia-chunk.1"
      }
    },
    {
      "type": "VECTORIZER",
      "vectorizer": {
        "inputProperty": "content.chunks",
        "propertyKey": "vector",
        "modelId": "mte-base-clinical.1"
      }
    }
  ]
}'
Now that your ingestion pipeline and steps are set up, your data source is ready to receive data. Incoming records will be processed through the pipeline and their data augmented before being persisted in the system.

Ingesting data

Once everything is configured, you can create your FHIR-compliant patient chart records using our Standard or Bulk API. The Bulk API allows you to ingest data from multiple collections representing different aspects of patient care in a single endpoint.
Important: The bulk ingestion example below shows data that has already been converted from FHIR to Clinia Platform format using the fhir_to_clinia_mapper.py script. In a real implementation, you would first use the mapping script to convert your FHIR resources before sending them to Clinia.
Using the Bulk API, here is what that can look like after your FHIR data has been converted to Clinia Platform format:
curl --location --globoff --request PUT 'https://{workspaceId}.clinia.cloud/catalog/sources/patient_data/v1/resources/bulk' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-Clinia-Api-Key: YOUR_API_KEY_HERE' \
--data '{
  "operations": [
    {
      "action": "CREATE",
      "create": {
        "type": "clinical_note",
        "noteId": "NOTE-2024-001",
        "patientId": {
          "system": "Patient",
          "value": "PAT-12345",
          "use": "usual"
        },
        "type": "progress_note",
        "date": "2024-01-15T10:30:00Z",
        "provider": "Dr. Sarah Johnson, MD",
        "content": "Patient presents with acute chest pain onset 2 hours ago. Pain described as sharp, substernal, radiating to left arm. Associated with shortness of breath and diaphoresis. Patient has history of hypertension and diabetes mellitus type 2. On examination: BP 150/95, HR 95, RR 22, O2 sat 96% on room air. ECG shows ST depression in leads V4-V6. Troponin I elevated at 0.8 ng/mL. Plan: Admit for further cardiac workup, start heparin protocol, cardiology consultation.",
        "title": "Emergency Department Progress Note - Chest Pain"
      }
    },
    {
      "action": "CREATE",
      "create": {
        "type": "allergy",
        "patientId": {
          "system": "Patient",
          "value": "PAT-12345",
          "use": "usual"
        },
        "code": {
          "system": "http://snomed.info/sct",
          "code": "387207008",
          "display": "Penicillin"
        },
        "category": ["medication"],
        "criticality": "high",
        "reaction": [{
          "manifestation": [{
            "coding": [{
              "system": "http://snomed.info/sct",
              "code": "271807003",
              "display": "Skin rash"
            }]
          }],
          "severity": "moderate"
        }],
        "recordedDate": "2023-03-15T09:00:00Z",
        "note": [{
          "text": "Patient developed generalized urticaria after receiving penicillin injection"
        }]
      }
    },
    {
      "action": "CREATE",
      "create": {
        "type": "condition",
        "patientId": {
          "system": "Patient",
          "value": "PAT-12345",
          "use": "usual"
        },
        "code": {
          "system": "http://snomed.info/sct",
          "code": "44054006",
          "display": "Type 2 diabetes mellitus"
        },
        "category": [{
          "system": "http://terminology.hl7.org/CodeSystem/condition-category",
          "code": "encounter-diagnosis",
          "display": "Encounter Diagnosis"
        }],
        "clinicalStatus": {
          "system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
          "code": "active"
        },
        "onsetDateTime": "2022-08-10T00:00:00Z",
        "note": [{
          "text": "Well-controlled with metformin. HbA1c 6.8% at last visit."
        }]
      }
    },
    {
      "action": "CREATE",
      "create": {
        "type": "encounter",
        "patientId": {
          "system": "Patient",
          "value": "PAT-12345",
          "use": "usual"
        },
        "status": "finished",
        "class": {
          "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
          "code": "EMER",
          "display": "emergency"
        },
        "period": {
          "start": "2024-01-15T08:30:00Z",
          "end": "2024-01-15T14:00:00Z"
        },
        "reasonCode": [{
          "system": "http://snomed.info/sct",
          "code": "29857009",
          "display": "Chest pain"
        }],
        "diagnosis": [{
          "condition": {
            "reference": "Condition/condition-002"
          },
          "rank": 1
        }],
        "hospitalization": {
          "admitSource": {
            "system": "http://terminology.hl7.org/CodeSystem/admit-source",
            "code": "emd",
            "display": "From accident/emergency department"
          }
        }
      }
    },
    {
      "action": "CREATE",
      "create": {
        "type": "medication",
        "patientId": {
          "system": "Patient",
          "value": "PAT-12345",
          "use": "usual"
        },
        "medicationCodeableConcept": {
          "system": "http://www.nlm.nih.gov/research/umls/rxnorm",
          "code": "860975",
          "display": "Metformin 500 MG Oral Tablet"
        },
        "status": "active",
        "effectiveDateTime": "2023-06-01T00:00:00Z",
        "dosage": [{
          "text": "500mg twice daily with meals",
          "timing": {
            "repeat": {
              "frequency": 2,
              "period": 1,
              "periodUnit": "d"
            }
          }
        }],
        "reasonReference": [{
          "reference": "Condition/condition-001"
        }],
        "note": [{
          "text": "Patient reports good tolerance. No gastrointestinal side effects."
        }]
      }
    },
    {
      "action": "CREATE",
      "create": {
        "type": "procedure",
        "patientId": {
          "system": "Patient",
          "value": "PAT-12345",
          "use": "usual"
        },
        "status": "completed",
        "code": {
          "system": "http://snomed.info/sct",
          "code": "29303009",
          "display": "Electrocardiography"
        },
        "performedDateTime": "2024-01-15T10:45:00Z",
        "reasonReference": [{
          "reference": "Condition/condition-002"
        }],
        "note": [{
          "text": "12-lead ECG performed. Shows ST depression in leads V4-V6 suggestive of cardiac ischemia."
        }]
      }
    },
    {
      "action": "CREATE",
      "create": {
        "type": "observation",
        "patientId": {
          "system": "Patient",
          "value": "PAT-12345",
          "use": "usual"
        },
        "status": "final",
        "category": [{
          "system": "http://terminology.hl7.org/CodeSystem/observation-category",
          "code": "vital-signs",
          "display": "Vital Signs"
        }],
        "code": {
          "system": "http://loinc.org",
          "code": "85354-9",
          "display": "Blood pressure panel with all children optional"
        },
        "effectiveDateTime": "2024-01-15T10:30:00Z",
        "component": [{
          "code": {
            "system": "http://loinc.org",
            "code": "8480-6",
            "display": "Systolic blood pressure"
          },
          "valueQuantity": {
            "value": 150,
            "unit": "mmHg"
          }
        }, {
          "code": {
            "system": "http://loinc.org",
            "code": "8462-4",
            "display": "Diastolic blood pressure"
          },
          "valueQuantity": {
            "value": 95,
            "unit": "mmHg"
          }
        }]
      }
    },
    {
      "action": "CREATE",
      "create": {
        "type": "immunization",
        "patientId": {
          "system": "Patient",
          "value": "PAT-12345",
          "use": "usual"
        },
        "status": "completed",
        "vaccineCode": {
          "system": "http://hl7.org/fhir/sid/cvx",
          "code": "208",
          "display": "COVID-19, mRNA, LNP-S, PF, 30 mcg/0.3 mL dose"
        },
        "occurrenceDateTime": "2023-10-15T14:30:00Z",
        "location": {
          "reference": "Location/clinic-001"
        },
        "lotNumber": "ABC123",
        "expirationDate": "2024-10-15",
        "note": [{
          "text": "Patient tolerated vaccination well. No immediate adverse reactions observed."
        }]
      }
    },
    {
      "action": "CREATE",
      "create": {
        "type": "diagnostic_report",
        "patientId": {
          "system": "Patient",
          "value": "PAT-12345",
          "use": "usual"
        },
        "status": "final",
        "category": [{
          "system": "http://terminology.hl7.org/CodeSystem/v2-0074",
          "code": "LAB",
          "display": "Laboratory"
        }],
        "code": {
          "system": "http://loinc.org",
          "code": "33747-0",
          "display": "Cardiac enzymes panel"
        },
        "effectiveDateTime": "2024-01-15T11:00:00Z",
        "issued": "2024-01-15T12:30:00Z",
        "result": [{
          "reference": "Observation/troponin-001"
        }, {
          "reference": "Observation/ckmb-001"
        }],
        "conclusion": "Elevated cardiac enzymes consistent with acute myocardial injury. Troponin I significantly elevated at 0.8 ng/mL (normal <0.04). CK-MB also elevated at 15.2 ng/mL (normal 0.0-6.3). These findings support the clinical suspicion of acute coronary syndrome."
      }
    }
  ]
}'
You can use the bulkId from the response that the request above will give you to track the status of the bulk ingestion request. Use this request to do so:
curl --location --globoff --request GET 'https://{workspaceId}.clinia.cloud/catalog/sources/patient_data/v1/resources/bulk/bulkId?withReceipts=true' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-Clinia-Api-Key: YOUR_API_KEY_HERE'
Once fully processed, the bulk task with be marked as successful and the records propagated to the relevant partitions.

Querying your Federated Patient Chart Search Partition

Documentation Once the ingestion is complete, you are now ready to use your HGS partition! The federated search capability allows you to query across all patient data collections simultaneously, providing comprehensive results from clinical notes, allergies, conditions, encounters, medications, procedures, observations, immunizations, and diagnostic reports. You can use the Data Partition API to perform federated search queries. Here is one example of a query that uses the knn operator for your semantic fields across multiple collections to find information about a patient’s cardiac condition:
curl --request POST \
     --url https://{workspaceId}.clinia.cloud/partitions/patient_chart_search/v1/federated/resources.clinical_note,resources.allergy,resources.condition,resources.encounter,resources.medication,resources.procedure,resources.observation,resources.immunization,resources.diagnostic_report \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "page": 0,
  "perPage": 20,
  "query": {
    "or": [
      {
        "knn": {
          "resources.clinical_note.content.chunks.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.allergy.code.display.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.allergy.note.text.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.condition.code.display.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.condition.note.text.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.encounter.class.display.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.encounter.reasonCode.display.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.medication.medicationCodeableConcept.display.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.medication.note.text.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.procedure.code.display.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.procedure.note.text.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.observation.code.display.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.immunization.vaccineCode.display.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      },
      {
        "knn": {
          "resources.diagnostic_report.conclusion.chunks.vector": {
            "value": "chest pain cardiac symptoms"
          }
        }
      }
    ]
  },
  "highlighting": [
    "resources.clinical_note.content.chunks.vector", 
    "resources.allergy.code.display.vector",
    "resources.allergy.note.text.vector",
    "resources.condition.code.display.vector",
    "resources.condition.note.text.vector",
    "resources.encounter.class.display.vector",
    "resources.encounter.reasonCode.display.vector",
    "resources.medication.medicationCodeableConcept.display.vector",
    "resources.medication.note.text.vector",
    "resources.procedure.code.display.vector",
    "resources.procedure.note.text.vector",
    "resources.observation.code.display.vector",
    "resources.immunization.vaccineCode.display.vector",
    "resources.diagnostic_report.conclusion.chunks.vector"
  ],
  "filter": {
    "or": [
      {
        "composite": {
          "resources.clinical_note": [
            {
              "eq": {
                "resources.clinical_note.patientId.value": "PAT-12345"
              }
            },
            {
              "eq": {
                "resources.clinical_note.patientId.system": "Patient"
              }
            }
          ]
        }
      },
      {
        "composite": {
          "resources.allergy": [
            {
              "eq": {
                "resources.allergy.patientId.value": "PAT-12345"
              }
            },
            {
              "eq": {
                "resources.allergy.patientId.system": "Patient"
              }
            }
          ]
        }
      },
      {
        "composite": {
          "resources.condition": [
            {
              "eq": {
                "resources.condition.patientId.value": "PAT-12345"
              }
            },
            {
              "eq": {
                "resources.condition.patientId.system": "Patient"
              }
            }
          ]
        }
      },
      {
        "composite": {
          "resources.encounter": [
            {
              "eq": {
                "resources.encounter.patientId.value": "PAT-12345"
              }
            },
            {
              "eq": {
                "resources.encounter.patientId.system": "Patient"
              }
            }
          ]
        }
      },
      {
        "composite": {
          "resources.medication": [
            {
              "eq": {
                "resources.medication.patientId.value": "PAT-12345"
              }
            },
            {
              "eq": {
                "resources.medication.patientId.system": "Patient"
              }
            }
          ]
        }
      },
      {
        "composite": {
          "resources.procedure": [
            {
              "eq": {
                "resources.procedure.patientId.value": "PAT-12345"
              }
            },
            {
              "eq": {
                "resources.procedure.patientId.system": "Patient"
              }
            }
          ]
        }
      },
      {
        "composite": {
          "resources.observation": [
            {
              "eq": {
                "resources.observation.patientId.value": "PAT-12345"
              }
            },
            {
              "eq": {
                "resources.observation.patientId.system": "Patient"
              }
            }
          ]
        }
      },
      {
        "composite": {
          "resources.immunization": [
            {
              "eq": {
                "resources.immunization.patientId.value": "PAT-12345"
              }
            },
            {
              "eq": {
                "resources.immunization.patientId.system": "Patient"
              }
            }
          ]
        }
      },
      {
        "composite": {
          "resources.diagnostic_report": [
            {
              "eq": {
                "resources.diagnostic_report.patientId.value": "PAT-12345"
              }
            },
            {
              "eq": {
                "resources.diagnostic_report.patientId.system": "Patient"
              }
            }
          ]
        }
      }
    ]
  }
}
'
You can find details about the API response here. The API also comes with highlighting support, to tell you why a given result was relevant. Using highlighting, you will be able to tell which of the chunks or passages within each patient record hit was most relevant. This is particularly useful for display purposes in clinical interfaces, but also to generate the best answer possible using our Summarization API. The federated search response will include results from all relevant FHIR-compliant collections (clinical notes, allergies, conditions, encounters, medications, procedures, observations, immunizations, and diagnostic reports), allowing clinicians to see a comprehensive view of patient information related to their query.
I