Ingest HL7 FHIR R5 Patient resources from bulk NDJSON and individual JSON files, flattening nested name, telecom, and address arrays into queryable SQL columns.
-- 1. Zone + schema (idempotent)
CREATE ZONE IF NOT EXISTS {{zone_name}} TYPE EXTERNAL
COMMENT 'External tables, demo datasets and file-backed data';
CREATE SCHEMA IF NOT EXISTS {{zone_name}}.fhir_demos
COMMENT 'FHIR-backed external tables: HL7 FHIR R5 resources as JSON';
-- 2. Bulk NDJSON table, 50 patients, one per line
CREATE EXTERNAL TABLE IF NOT EXISTS {{zone_name}}.fhir_demos.patients_bulk
USING JSON
LOCATION '{{data_path}}'
OPTIONS (
file_filter = '*.ndjson',
json_flatten_config = '{
"root_path": "$",
"include_paths": ["$.resourceType", "$.id", "$.name", "$.gender", "$.birthDate"],
"column_mappings": {"$.id": "patient_id", "$.birthDate": "birth_date"},
"max_depth": 3,
"separator": "_",
"default_array_handling": "to_json",
"infer_types": true
}',
file_metadata = '{"columns":["df_file_name","df_row_number"]}'
);
-- 3. Rich individual-file table, 7 Patient resources with full nesting
CREATE EXTERNAL TABLE IF NOT EXISTS {{zone_name}}.fhir_demos.patients_detailed
USING JSON
LOCATION '{{data_path}}'
OPTIONS (
file_filter = 'patient-example*.json',
json_flatten_config = '{
"root_path": "$",
"include_paths": [
"$.resourceType", "$.id", "$.active", "$.gender", "$.birthDate",
"$.deceasedBoolean", "$.name", "$.telecom", "$.address",
"$.maritalStatus", "$.managingOrganization"
],
"json_paths": ["$.maritalStatus", "$.managingOrganization"],
"column_mappings": {
"$.id": "patient_id",
"$.birthDate": "birth_date",
"$.deceasedBoolean": "is_deceased",
"$.managingOrganization": "managing_org"
},
"max_depth": 3,
"separator": "_",
"default_array_handling": "to_json",
"infer_types": true
}',
file_metadata = '{"columns":["df_file_name","df_row_number"]}'
);
-- 4. Analyse: gender distribution from bulk export
SELECT gender, COUNT(*) AS patient_count
FROM {{zone_name}}.fhir_demos.patients_bulk
GROUP BY gender
ORDER BY patient_count DESC;
-- 5. Cleanup
DROP EXTERNAL TABLE IF EXISTS {{zone_name}}.fhir_demos.patients_bulk WITH FILES;
DROP EXTERNAL TABLE IF EXISTS {{zone_name}}.fhir_demos.patients_detailed WITH FILES;
DROP SCHEMA IF EXISTS {{zone_name}}.fhir_demos;
## When to Use Reach for this demo when onboarding a hospital's Patient registry exported from an EHR or FHIR server. It models the two canonical FHIR delivery shapes side-by-side: a FHIR Bulk Data NDJSON export (one Patient per line, for batch analytics) and individually-stored Patient JSON resources (rich, deeply nested, for clinical review). It is the right starting point before tackling Observation, Condition, or MedicationRequest. ## What You Will Learn 1. How DeltaForge's `json_flatten_config` + `include_paths` pattern flattens FHIR's nested `name[]`, `telecom[]`, and `address[]` arrays without losing structure. 2. How `column_mappings` rename FHIR JSONPath expressions (`$.id`, `$.birthDate`, `$.deceasedBoolean`) into analyst-friendly snake_case columns. 3. How NDJSON (newline-delimited JSON) is parsed line-by-line via `file_filter = '*.ndjson'`, giving one row per line. 4. How two external tables can point at the same directory and partition the files via `file_filter` (`*.ndjson` vs `patient-example*.json`). 5. How `file_metadata` injects `df_file_name` and `df_row_number` for clinical data lineage and audit trails. 6. How FHIR schema evolution: `deceasedBoolean`, `maritalStatus`, `managingOrganization` appearing only in some patients, produces NULL-padded columns automatically. ## Prerequisites - DeltaForge workspace with permission to `CREATE ZONE` and `CREATE SCHEMA` (external zone type). - Read access to a directory containing FHIR R5 Patient JSON and/or NDJSON files. - Familiarity with FHIR resource basics (Patient, HumanName, Identifier) is helpful but not required.