net9.0
SQL on FHIR implementation for analytics queries
Published
Dec 7, 2025
10mo ago
Total Downloads
9.2K
9,180
Install
dotnet add package Ignixa.SqlOnFhir --version 0.0.68
| Product | Versions |
|---|---|
| .NET |
README
Ignixa.SqlOnFhir
SQL on FHIR v2 implementation for analytics queries. Enables SQL-like querying over FHIR resources by flattening hierarchical FHIR data into tabular views.
Why Use This Package?
- SQL on FHIR v2 spec compliance: Implements the official specification
- FHIRPath-based column extraction: Uses FHIRPath for flexible data access
- Array unnesting: Handle FHIR arrays (name, identifier, etc.) with forEach
- Type conversion: Automatic conversion of FHIR types to SQL types
Installation
dotnet add package Ignixa.SqlOnFhir
Quick Start
Define a View
using Ignixa.SqlOnFhir.Models;
// Create a view definition for patient demographics
var viewDefinition = new ViewDefinition
{
Resource = "Patient",
Select = new List<SelectGroup>
{
new SelectGroup
{
Column = new List<ViewColumnDefinition>
{
new() { Name = "id", Path = "id" },
new() { Name = "birth_date", Path = "birthDate", Type = "date" },
new() { Name = "gender", Path = "gender", Type = "string" }
}
}
}
};
Execute View Against Resources
using Ignixa.SqlOnFhir.Evaluation;
using Ignixa.Abstractions;
// Get FHIR resources
IEnumerable<IElement> patients = GetPatientElements();
// Create evaluator
var evaluator = new SqlOnFhirEvaluator(schema);
// Execute view
var rows = evaluator.Evaluate(viewDefinition, patients);
// Process results
foreach (var row in rows)
{
var id = row["id"];
var birthDate = row["birth_date"];
var gender = row["gender"];
Console.WriteLine($"Patient {id}: {gender}, born {birthDate}");
}
Advanced Features
Array Unnesting with forEach
Extract values from FHIR arrays:
var viewDefinition = new ViewDefinition
{
Resource = "Patient",
Select = new List<SelectGroup>
{
// Base columns (one row per patient)
new SelectGroup
{
Column = new List<ViewColumnDefinition>
{
new() { Name = "id", Path = "id" }
}
},
// Name columns (one row per name)
new SelectGroup
{
ForEach = "name", // Unnest name array
Column = new List<ViewColumnDefinition>
{
new() { Name = "name_use", Path = "use" },
new() { Name = "family", Path = "family" },
new() { Name = "given", Path = "given.first()" }
}
}
}
};
// Result: One row per patient name
// id | name_use | family | given
// 123| official | Doe | John
// 123| maiden | Smith | Jane
Filtering with WHERE
var viewDefinition = new ViewDefinition
{
Resource = "Patient",
Where = new List<WhereClause>
{
new() { Path = "active = true" },
new() { Path = "birthDate > @2000-01-01" }
},
Select = new List<SelectGroup>
{
new SelectGroup
{
Column = new List<ViewColumnDefinition>
{
new() { Name = "id", Path = "id" },
new() { Name = "name", Path = "name.first().family" }
}
}
}
};
// Only evaluates active patients born after 2000
Using Constants
Parameterize your views:
var viewDefinition = new ViewDefinition
{
Resource = "Observation",
Constant = new List<ViewConstant>
{
new() { Name = "minValue", Value = "100" }
},
Where = new List<WhereClause>
{
new() { Path = "value.value >= %minValue" }
},
Select = new List<SelectGroup>
{
new SelectGroup
{
Column = new List<ViewColumnDefinition>
{
new() { Name = "id", Path = "id" },
new() { Name = "value", Path = "value.value" }
}
}
}
};
Multiple Select Groups
Create multiple row groups from a single resource:
var viewDefinition = new ViewDefinition
{
Resource = "Patient",
Select = new List<SelectGroup>
{
// First SELECT: Names (one row per name)
new SelectGroup
{
ForEach = "name",
Column = new List<ViewColumnDefinition>
{
new() { Name = "id", Path = "%resource.id" }, // Use %resource to access root
new() { Name = "family", Path = "family" }
}
},
// Second SELECT: Identifiers (one row per identifier)
new SelectGroup
{
ForEach = "identifier",
Column = new List<ViewColumnDefinition>
{
new() { Name = "id", Path = "%resource.id" },
new() { Name = "identifier_value", Path = "value" }
}
}
}
};
// Result: Multiple row groups
// Group 1 (names):
// id | family
// 123| Doe
// 123| Smith
//
// Group 2 (identifiers):
// id | identifier_value
// 123| MRN123
// 123| SSN456
SQL Type Conversions
Supported column types:
new ViewColumnDefinition
{
Name = "string_col",
Path = "someString",
Type = "string"
};
new ViewColumnDefinition
{
Name = "int_col",
Path = "someInteger",
Type = "integer"
};
new ViewColumnDefinition
{
Name = "decimal_col",
Path = "someDecimal",
Type = "decimal"
};
new ViewColumnDefinition
{
Name = "bool_col",
Path = "someBoolean",
Type = "boolean"
};
new ViewColumnDefinition
{
Name = "date_col",
Path = "someDate",
Type = "date"
};
new ViewColumnDefinition
{
Name = "datetime_col",
Path = "someDatetime",
Type = "datetime"
};
Integration with Other Packages
- Ignixa.FhirPath: Used to evaluate column Path expressions
- Ignixa.Abstractions: Works with
IElementrepresentations - Ignixa.Specification: Uses schema for type information
Use Cases
Analytics and Reporting
Flatten FHIR resources for BI tools:
// Create views for your BI dashboard
var patientView = CreatePatientDemographicsView();
var observationView = CreateVitalsView();
var conditionView = CreateDiagnosesView();
// Export to SQL database
var patientRows = evaluator.Evaluate(patientView, patients);
await BulkInsertToSql("patient_demographics", patientRows);
Data Export for Research
Extract specific data elements for research datasets:
var researchView = new ViewDefinition
{
Resource = "Patient",
Where = new List<WhereClause>
{
new() { Path = "meta.tag.code contains 'research-approved'" }
},
Select = new List<SelectGroup> { /* De-identified columns */ }
};
Specification Compliance
This implementation follows the SQL on FHIR v2 specification.
License
MIT License - see LICENSE file in repository root
Changes from Previous Version
No structural changes between v0.0.62 and v0.0.68.
Adoption Assessment
Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.
Compatibility metadata lists 1 target framework; confirm the exact target used by your application.
Related Guides & Workflows
Learn how to evaluate, integrate, or audit this package in your .NET project.