Introduction
Let's discover ArcEHR in less than 5 minutes.
Why ArcEHR?
Traditional openEHR Clinical Data Repositories (CDRs) often act as black boxes. They store data in rigid, unreadable relational structures and force your engineering team to learn Archetype Query Language (AQL) just to fetch a patient record.
ArcEHR changes this by mapping the openEHR Reference Model (RM) onto ArcadeDB, a multi-model graph database. Instead of forcing your team to master a niche language, ArcEHR lets you query natively versioned, compliant COMPOSITION payloads using languages you already know: SQL, Cypher, Gremlin, and GraphQL.
This introduction walks you through how ArcEHR bridges the gap between openEHR data and standard, rapid application development.
Database for openEHR Data
ArcEHR runs on ArcadeDB. ArcadeDB is an open-source, Apache 2.0 licensed multi-model database. ArcadeDB supports SQL, OpenCypher, Gremlin, GraphQL, and the MongoDB query protocol, delivering over 10 million records per second with full ACID compliance. It is well suited for managing complex openEHR data models without compromise.
Because ArcadeDB supports the PostgreSQL wire protocol, you do not need proprietary clients; you can use standard Postgres drivers, BI tools, or the open-source ArcadeDB Studio web interface to connect to the server and run the queries. For advanced use cases, even more power can be unlocked directly via the Java API.
Persisting openEHR Data
ArcEHR stores openEHR data in a graph database. It uses the openEHR Reference Model (RM) to map the openEHR data onto the graph database. RM types are created as ArcadeDB Document Types. All data stored is strictly typed. No schema-less JSON is used.
Each openEHR COMPOSITION is mapped to its own Document Type (document types are analogous to tables in a relational database). When adding a new openEHR COMPOSITION, ArcEHR creates a new Document Type (e.g. openEHR-EHR-COMPOSITION.encounter.v1) and populates it with the COMPOSITION's data. Document Types are also created for all the archetypes used by the COMPOSITION (e.g. openEHR-EHR-OBSERVATION.blood_pressure.v2, openEHR-EHR-OBSERVATION.pulse.v2). These document types are created as needed on the fly. Each template inherits from the COMPOSITION type, and the archetypes inherit from their RM classes. No database schema migrations are needed, since templates and archetypes don't add any columns to the RM types they inherit from.
Using edges, ArcEHR connects the Document Types to each other. This allows us to query/traverse the graph database to retrieve openEHR data. This is much more efficient than traditional relational databases which require many joins to retrieve data.
Current openEHR CDRs are black boxes for data stored in them. Querying is possible only with AQL. Data is persisted in a relational database but is unreadable with SQL.
The biggest advantage of ArcEHR is that the data is not stored in a "black-box" format. This means that you can query the data using standard SQL, Cypher, Gremlin, and GraphQL.
The goal of ArcEHR is to make openEHR data accessible to developers and data scientists by building on top of existing databases, capable of handling highly hierarchical health data. ArcEHR leverages the power of existing databases to avoid reinventing the wheel.
Querying openEHR Data
By leveraging ArcadeDB, we can enable polyglot querying over openEHR data structures. Your developers and analysts can pick the exact query language that fits their background:
- SQL: Extended with graph traversal, full-text search, vector, and time-series functions.
- Cypher (openCypher): A drop-in replacement for Neo4j Cypher.
- Gremlin: Apache TinkerPop graph traversal.
- GraphQL: Schema-driven queries natively over HTTP.
- MongoDB QL: Document-oriented queries via the MongoDB protocol.
- GQL: Native Graph Query Language support coming soon.
SQL and Cypher are particularly powerful for handling the deeply nested, interconnected nature of clinical models.
AQL
If you work with openEHR, you already know Archetype Query Language (AQL). It’s a brilliant technical achievement designed specifically for hierarchical health data.
But let’s be honest: most developers and analysts outside the openEHR ecosystem have never heard of it. When onboarding new engineers to a health tech project, teaching them AQL from scratch is a massive bottleneck. Usually, they have to write or look at something like this just to get blood pressure data:
SELECT o/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude,
o/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/units,
o/data[at0001]/events[at0002]/data[at0003]/items[at0006]/value/magnitude,
o/data[at0001]/events[at0002]/data[at0003]/items[at1005]/value/magnitude,
o/data[at0001]/events[at0002]/data[at0003]/items[at0005]/value/units,
o/data[at0001]/events[at0002]/data[at0003]/items[at1006]/value/units
FROM EHR e CONTAINS COMPOSITION c CONTAINS OBSERVATION o[openEHR-EHR-OBSERVATION.blood_pressure.v2]
WHERE e/ehr_id/value = '0f5ea58d-9e30-4365-853e-81f2224a85e0'"
Here is how this AQL query looks when translated into SQL and Cypher.
SQL
SELECT $event
FROM `openEHR-EHR-OBSERVATION.blood_pressure.v2`
WHERE data.archetype_node_id = "at0001"
AND data.events CONTAINS ( -- History/events
archetype_node_id = "at0006" AND -- Any event
data.archetype_node_id = "at0003" -- blood pressure
)
Result:
{"records": [
{ "data.events.data.items.value": [
{ "magnitude": 120.0, "units": "mm[Hg]" },
{ "magnitude": 80.0, "units": "mm[Hg]" }]},
{ "data.events.data.items.value": [
{ "magnitude": 144.5, "units": "mm[Hg]" },
{ "magnitude": 89.1, "units": "mm[Hg]" }]}
]}
Cypher
MATCH (o:`openEHR-EHR-OBSERVATION.blood_pressure.v2`)
WHERE o.data.archetype_node_id = "at0001"
RETURN [
event IN o.data.events
WHERE event.archetype_node_id = "at0006"
AND event.data.archetype_node_id = "at0003"
| [
item IN event.data.items
| {
name: item.name.value,
archetype_node_id: item.archetype_node_id,
magnitude: item.value.magnitude,
units: item.value.units
}
]
] AS blood_pressure