Developer ← Insights

Querying Clinical Data with AQL: Paths, Predicates, and Production Practices

Archetype Query Language (AQL) is the openEHR standard for querying clinical data in a CDR. Its syntax is officially described as a synthesis of SQL structural syntax and openEHR path syntax: familiar SELECT / FROM / WHERE / ORDER BY clauses, combined with path expressions that navigate the archetype and composition hierarchy, and a CONTAINS operator that expresses containment between reference model structures [1]. The crucial property — and the reason AQL exists at all — is that queries are written against clinical models, not physical schemas: the archetype identifier in brackets scopes the query to a clinical concept regardless of which template, form, or application stored the data.

A complete example, following the pattern from the specification — all hypertensive blood pressure readings in an EHR, latest first [1]:

SELECT
  o/data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/magnitude AS systolic,
  o/data[at0001]/events[at0006]/data[at0003]/items[at0005]/value/magnitude AS diastolic,
  c/context/start_time AS date_time
FROM EHR e[ehr_id/value=$ehrUid]
  CONTAINS COMPOSITION c
    CONTAINS OBSERVATION o[openEHR-EHR-OBSERVATION.blood_pressure.v1]
WHERE
  o/data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/magnitude >= 140
  OR o/data[at0001]/events[at0006]/data[at0003]/items[at0005]/value/magnitude >= 90
ORDER BY c/context/start_time DESC

Reading AQL Paths: Where the at-Codes Come From

AQL paths are derived directly from the archetype structure, and the specification distinguishes two kinds: archetype paths, which refer to nodes within an archetype, and reference model attribute paths, which point at RM attributes like c/context/start_time or e/ehr_id/value [1]. The segments in brackets — at0001, at0004 — are at-codes: local node identifiers defined in the archetype's terminology section, each bound to a human-readable name. In the blood pressure archetype, items[at0004] is the systolic node and items[at0005] the diastolic node; the path /data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/magnitude reads as "the magnitude of the systolic element, within any event, within the observation's data."

Nobody should construct these by hand from memory. The reliable workflow is to inspect the archetype's ADL definition or — better — navigate the node tree in a visual modelling tool and copy the generated path; raw reference-model paths are difficult to handle unaided, and at-codes are archetype facts, not guessable strings. Two practical details that save debugging time: name-based predicates can disambiguate repeated siblings (e.g., items[at0011, 'Fractions'] in the specification's lab examples), and specialized archetypes introduce dotted at-codes (at0013.1) whose meaning only makes sense against the specialization hierarchy — another reason the archetype, not the query, is the source of truth. For a full walkthrough of paths, predicates, and query construction, see the CaboLabs AQL Guide [5].

Query on Archetypes, Not Templates

When the same clinical concept can be recorded through multiple templates — and in any mature CDR, it will be — prefer querying on archetype identifiers rather than template-specific structures. The archetype predicate (OBSERVATION o[openEHR-EHR-OBSERVATION.blood_pressure.v1]) matches every composition containing that observation regardless of the template used to commit it, so the query keeps returning correct results as new templates are deployed and old ones evolve. This is the query-side dividend of two-level modelling: your analytics are coupled to the governed clinical model, not to the faster-changing form layer. Template-scoped queries have their place — when you genuinely want "data captured through this document" — but they should be the deliberate exception, not the reflex.

Executing AQL in Production: The REST API

The openEHR REST API standardizes query execution across conformant platforms [3]. Ad-hoc queries go to the query endpoint — with POST recommended over GET, since long AQL strings and parameter lists quickly exceed URI length limits:

POST [base]/query/aql HTTP/1.1
Content-Type: application/json
{
  "q": "SELECT ... FROM EHR e[ehr_id/value=$ehrUid] CONTAINS OBSERVATION o[openEHR-EHR-OBSERVATION.blood_pressure.v1] WHERE o/.../value/magnitude >= $systolicThreshold",
  "query_parameters": {
    "ehrUid": "554f896d-faca-4513-bddf-66454114630d",
    "systolicThreshold": 140
  }
}

Three API capabilities deserve deliberate use rather than incidental discovery:

  • Parameter binding: AQL supports parameterization by design ($ehrUid, $systolicThreshold), with values supplied at execution via query_parameters. Use it always — string-concatenating values into AQL is the same anti-pattern as concatenating SQL, with the same injection and caching consequences.
  • Stored (named) queries: the REST API's definitions endpoints manage registered queries that clients execute by qualified name and version [4]. In our experience this is the single most effective cure for AQL strings scattered and duplicated across application code: queries become versioned, governed server-side artifacts, and applications hold references, not copies.
  • Result control: scope queries to a single EHR wherever the use case allows (via ehr_id parameter or header) rather than running population queries by default, order results explicitly, and paginate large result sets rather than fetching unbounded lists.

AQL Query Hygiene at a Glance

Practice Instead Of Why It Matters
Archetype-based predicates Template-coupled queries Queries stay valid across template changes and new form deployments
Paths copied from modelling tools Hand-typed at-code paths at-codes and specialization codes are archetype facts, not guessable strings
Parameter binding ($params) String-concatenated values Safety, reuse, and shareable statements — parameterization is built into the language
Stored named queries AQL strings in application code Versioned, governed, server-side query definitions with a stable contract
EHR-scoped execution + pagination Unbounded population queries Predictable performance; population queries become a deliberate, monitored choice

Performance ultimately depends on how the underlying CDR structures and indexes compositions — AQL defines the semantics, not the execution strategy — so the same query can behave very differently across platforms. openEHR CDRs such as EHRbase, Better, and our own Atomik each implement AQL execution over their own persistence designs; in our experience, testing your heaviest queries against realistic data volumes on your actual platform belongs in evaluation and pre-production, not after the first slow dashboard.

Where CaboLabs Fits

AQL is where an openEHR investment starts paying analytical dividends — if the queries are correct, maintainable, and fast. CaboLabs has deep, implementation-level AQL expertise: we've built AQL query engines, designed archetype-path indexing strategies for relational persistence, and helped teams turn ad-hoc query sprawl into governed, stored-query catalogs. Our openEHR-native clinical data repository, Atomik, puts that experience into production: archetype-based querying over a standards-based CDR, with your clinical models — not your database schema — as the query surface.

If your team is learning AQL, your queries are outgrowing your CDR's performance, or you're evaluating openEHR platforms and want to understand what's really behind each AQL endpoint, talk to us at cabolabs.com — we speak AQL at the engine level.

References & Verifiable Sources

  1. openEHR Foundation: Archetype Query Language (AQL) Specification (Official specification defining AQL as a synthesis of SQL structural syntax and openEHR path syntax, the CONTAINS containment mechanism, archetype predicates, parameterization, and the two path types — including the blood pressure example with the at0004 systolic and at0005 diastolic paths; supports the syntax and paths sections).
  2. openEHR Foundation: AQL Examples (Official worked examples including name-based predicates for disambiguating sibling nodes and specialized dotted at-codes in laboratory archetypes; supports the path construction details).
  3. openEHR Foundation: openEHR REST API — Query (Official REST API specification for AQL execution, including the recommendation to use POST over GET due to URI length limits, query_parameters for runtime parameter binding, and EHR-scoped execution; supports the production execution section).
  4. openEHR Foundation: openEHR REST API — Definitions (Official REST API specification covering the management of stored clinical definitions including registered queries executable by qualified name; supports the stored named queries recommendation).
  5. CaboLabs: The CaboLabs AQL Guide (Our practical reference to the Archetype Query Language, covering paths, predicates, operators, functions, pagination, and a step-by-step method for writing queries by hand; supports the path construction and tooling workflow recommendations).

Do you have any questions?

Let us know how we can help you.

Company CaboLabs Health Informatics
Address Juan Paullier 995, Montevideo, Uruguay
Phone +598 99 043 145