Is it possible to follow a link in AQL?

The locatable class provides a links attribute with which, for example, structures in other compositions can be referenced.

We have tried to use a single AQL query to read a composition and, within the same AQL query, retrieve another composition that is referenced from the first via the links attribute.

So far, our attempts have failed because the links attribute contains a slightly different value (e.g. links.target: “ehr:/123456/composition/abcdef”) than the uid of the targeted composition (e.g. “abcdef”). This is due, among other things, to the differing data types of links.target (DV_EHR_URI) and uid (String) . As a result, it is not possible to perform a join between the two.

Furthermore, the available string functions in the AQL specification do not permit the conversion of the target.links and uid values into a format that would enable such a join.

Is there a practicable way to follow a links target in AQL?

2 Likes

The simple answer right now is no. This has bern under discussion to be added to the spec though

It is possibl that some local implementations do something better.

Thank you very much for your clear answer!

Spec wise, I think we’ll need to have a LINK API first, and some search parameters/filters for getting things from LINK’s sources and targets, maybe that’s an easier addition.

The way I see LINKs is like the construct for a graph structure inside a purely hierarchical model and query model, and querying graphs might be more complicated than querying hierarchies, though I didn’t analyze implementation options in detail, it might need new logic to follow that graph structure to check filters and retrieve the right results (maybe that’s the reason we avoided it in AQL for the last 15 years?).

1 Like

Are you able to discuss your requirements for following the links?

1 Like

I tried a JOIN of two COMPOSITIONS that are linked via a RM-LINK. The problem could possibly be solved with a regex in MATCHES, for example as follows or similar?
WHERE l/meaning/value = “is_part_of” AND l/target/value MATCHES {“.*/” + trg/uid/value + “$”}

The Data Modelling Group Switzerland is currently evaluating the possibilities regarding “administrative case IDs” in the CDR. As Emmanuel is on holiday, he has asked me to answer any questions during his absence.

Here is the context:

In many healthcare settings, administrative cases (in German: ‘administrativer Fall’ or ‘abrechnungsrelevanter Fall’), identified by a case number, serve as a central unit for organising clinical documentation, billing, and analytics.

To support downstream processes such as automated billing, quality reporting, statistical evaluation, and other queries, systems must be able to determine to which administrative case a given composition belongs.

The goal of this guideline is to establish a lightweight, interoperable mechanism for referencing an administrative case within compositions — without requiring full modeling or storage of administrative or encounter data in the CDR. The solution must support retrieval via REST, filtering and selection via AQL, and work consistently across different CDRs.

Among various options, we considered the following:

  • We create a template that includes an ADMIN ENTRY archetype called “Episode of care – institution” (openEHR-EHR-ADMIN_ENTRY.episode_institution.v0).

  • Each administrative case is stored as a composition based on this template.

  • Clinical compositions that belong to this administrative case link to it using the linksRM attribute in their root data element.

    • The attributes of the links RM attribute would be used as follows:

      • meaning: code: “LINK-E1i” ; description: “documents (describes, reports)” (according to the definition of Common Information Model : “Values for meaning include those described in Annex C, ENV 13606 pt 2”

      • type: “administrative_case_number”

      • target: the uidof the administrative case composition

We would like to be able to retrieve a composition together with the linked administrative case from the other composition via AQL in a single AQL statement. The EHRBase documentation describes a JOIN that does not work for the reasons described by Emanuel.

I tried MATCHES, but unfortunately it does not support Regex. We also did not find any suitable string editing options.

Are solutions for MATCHES, as already described in other articles, planned with Regex in openEHR? Are other solutions planned?

I would say that today the easiest way of implementing cases is by using a folder.

In the last RM version you can even have a data structure defined for the folder itself, which can be archetyped. REF Common Information Model

Or you can have a specific composition inside the folder that represents the case information, then individual documents for the whole case tracking (events).

Links are not needed when using folders.

Did you consider that path?

Thank you for your prompt reply.

We are examining the following options

  • Adding the CLUSTER v0 archetype “Case identification” to the template
  • Adding the ADMIN ENTRY v0 archetype “Episode of care - institution” to the template
  • Adding the EVALUATION ENTRY v1 archetype “Reason for encounter” to the template
  • Using tags
  • Filing the composition in a folder with the corresponding case number
  • Using the reference model attribute “links”

We see the following disadvantages with folders:

  • Access to folder information and the list of compositions contained within is primarily via the folder, rather than the composition.
  • ‘AQL on Folders’, which provides direct access to folders containing a composition, is still experimental.
  • Filing of a composition in a folder is not clearly visible as folders are not an explicit part of the template.
  • Folders are not returned when getting a composition (neither by REST nor by standard AQL)

We are looking for a way to use AQL to obtain a composition including an administrative case in a single AQL statement. This does not seem to be possible with either Folder or RM-Link. Is that correct?

If the administrative case is included in each template/composition, it becomes more difficult when changes are needed to an administrative case. All compositions with this case must be updated.

As this will be a guideline for Switzerland, it should be within the openEHR standard and usable on the various platforms of Vitagroup, Better, etc.

Have we overlooked or misunderstood something? Are there other possibilities?

Why is that a disadvantage? You have a specific endpoint to get a folder and its contents: EHR API openEHR specs

Agree, though that is more support than the AQL for LINKs.

Folders and their relationship with compositions shouldn’t be mentioned in templates. The way they relate is through the API as a business process at runtime not as a modeling time.

It doesn’t make sense to get a composition that will know on which folders it’s included, it can be referenced by many compositions. In the business process you would like to know: for patient X, get episode of Y (folder template_id or folder name or folder path) and maybe at certain time (info that could be inside the folder.details, which should be supported in AQL), then get all items in that folder and/or subfolders (those would be references to the compos, not the compo contents), then get the compositions by their ID. That will follow the structural hierarchy top-down.

Of course it depends on the specific use cases if it’s easy or difficult to implement with the current specs and implementations. What I suggest is just one way that should be possible withing most implementations or easy to implement.

I know Code24 has a specific type of composition inside the folder that is kind of the folder metadata (was developed before we had folder.details), which can contain metadata about the episode/case and references to related data, like IDs to other compos, for instance using EHR_URIs or DV_IDENTIFIER instead of LINK. Sadly VERSION_OBJECT_ID is not a datatype, which would be useful to have IDs to specific COMPOs within other COMPOs.

Best,
Pablo.

Hello Pablo,
Thank you for the detailed explanations, which are easy to understand.

We have both use cases.

  1. Start the query via a folder to find the corresponding compositions.
  2. Start the query via a composition where you want the corresponding administrative case (e.g. a list of all chest X-ray findings for a patient, including the administrative case, where the examination has different administrative cases).

Therefore, a solution with RM-LINKS seems to be the better option, except for the possibility of including an archetype or cluster directly in the template/composition with the administrative case.

However, we are faced with the challenge that two AQL statements are always required for this. We have tested a JOIN approach, but this does not work for WHERE or MATCHES due to different data formats.

Are there any plans for the further development of AQL that would address this issue, such as the introduction of Regex or data field functions to query different data formats?

Best regards,
Jean-Pierre

1 Like

Hi Jean-Pierre,

quick question: for the RegEx, do you envision to know the Encounter-ID before fetching all compositions including the encounter? Or do you envision this to work like a subselect where the sub-select retrieves the Encounter IDs which are then used in the WHERE clause of the surrounding SELECT?

The second option applies. There is a Composition 1 (radiology report) and a Composition 2 (diagnosis). Composition 1 is linked to Composition 2 with an RM link. Composition 1 should be queried via AQL, and some data fields from Composition 2 should also be queried in the same AQL statement. We started with a JOIN, as described in the EHRBase Doc. However, this does not work; it should be possible to take the URL of Composition 2 from the link in Composition 1. I found the possibility with matches, which was discussed but ultimately not pursued


HI JP,

Currently only including a cluster/admin entry archetype in every composition is ‘widely’ supported, I believe.
I do agree with Pablo FOLDERs seems the nicest way to model this. The big advantage is you get a representation of the ‘case’ as an object in the CDR. And this FOLDER can be archetyped. So you can record (meta) information about the case in a single place. The downside is limited support.
LINKs have the downside they are not archetype able: you can’t give any consistent meaning to a link that’s query able. And, at least to me, the needed joins are somewhat outside of the design perspectives of openEHR.
All three options are recorded on one side of the reference only.
But it’s quite straightforward to imagine an api the returns all incoming references. Nedap has implemented this: API’s | Nedap Ons API it’s not standard openEHR REST api (yet). But it’s good for understanding the idea. And I guess it might be added to the openEHR REST api.

So I guess it depends on how you value the current support vs nice modelling.

2 Likes

This feels more like a get FOLDER by ID / type / time range, then traversing it’s contents than a single query.

It should be possible to query for FOLDERs that contain an item with a given ID.

LINKs might be able to do both, though AQL is not there yet IMO, though I still feel FOLDERs is a better option (I don’t see a use case that can’t use FOLDERs).

Since my last review/fixing round some years ago while working on HiGHmed, I didn’t heard others mentioning to add more things to the AQL spec, though the openEHR JIRA accepts proposals for new features: Jira - openEHR JIRA

Best,
Pablo.

Thank you very much for your support!

The folder approach is considered a good and preferred solution. May I ask a few questions about FOLDERS?

I have created an example based on various openEHR literature sources and attached it to this entry with the following ideas:

  • Create a separate folder called ‘Administrative Cases’ below the root folder for administrative cases
  • Create a separate subfolder for each administrative case
  • This subfolder has a description in the form of “archetype_node_id”, where the desired attributes are stored under ‘details’.
  • The archetype is usually listed as ‘archetype_node_id’: ‘openEHR-EHR-FOLDER.generic.v1’. I have used “archetype_node_id”: ‘openEHR-EHR-FOLDER.administrative_case.v1’.
  • All compositions can then be stored in a folder of the corresponding administrative case under ‘Items’.
  • All modifications are automatically versioned in the CDR
  • Additional folders can be created at the same level as ‘Administrative Cases’ below the root folder, e.g. for diagnostic results, visits, etc.

My questions:

  • Does this example/suggestion correspond to best practices?
  • Currently, you have to load all folders to add a composition to a specific folder or subfolder (Get/Put). Is it possible to load and update only a subfolder, e.g. only the ‘Administrative Cases’ folder without always loading all folders?
  • Is it advisable to create a separate folder with subfolders for each case to map the administrative cases?
  • An archetype is used to store the attributes for a case, by default ‘openEHR-EHR-FOLDER.generic.v1’. Is it permissible and better to use other archetypes for a folder, e.g. ‘openEHR-EHR-FOLDER.administrative_case.v1’?
  • How can I load my FOLDER archetypes into the CDR so that they are available for validating the details when saving?

POST Create directory (tested in EHRBase)

{
  "_type": "FOLDER",
  "name": { "_type": "DV_TEXT", "value": "root" },
  "archetype_node_id": "openEHR-EHR-FOLDER.generic.v1",
  "folders": [
    {
      "_type": "FOLDER",
      "name": { "_type": "DV_TEXT", "value": "Administrative Cases" },
      "archetype_node_id": "openEHR-EHR-FOLDER.generic.v1",
      "folders": [
        {
          "_type": "FOLDER",
          "name": { "_type": "DV_TEXT", "value": "Admin Case 1" },
          "archetype_node_id": "openEHR-EHR-FOLDER.administrative_case.v1",
          "details": {
            "_type": "ITEM_TREE",
            "items": [
              {
                "_type": "ELEMENT",
                "name": { "value": "case_id" },
                "value": { "_type": "DV_IDENTIFIER", "id": "AC-2025-001" }
              },
              {
                "_type": "ELEMENT",
                "name": { "value": "case_description" },
                "value": { "_type": "DV_TEXT", "value": "Inpatient cardiology case" }
              },
              {
                "_type": "ELEMENT",
                "name": { "value": "start_date" },
                "value": { "_type": "DV_DATE_TIME", "value": "2025-01-10T09:00:00Z" }
              },
              {
                "_type": "ELEMENT",
                "name": { "value": "end_date" },
                "value": { "_type": "DV_DATE_TIME", "value": "2025-01-15T17:00:00Z" }
              }
            ]
          },
          "folders": [],
          "items": []
        },
        {
          "_type": "FOLDER",
          "name": { "_type": "DV_TEXT", "value": "Admin Case 2" },
          "archetype_node_id": "openEHR-EHR-FOLDER.administrative_case.v1",
          "details": {
            "_type": "ITEM_TREE",
            "items": [
              {
                "_type": "ELEMENT",
                "name": { "value": "case_id" },
                "value": { "_type": "DV_IDENTIFIER", "id": "AC-2025-002" }
              },
              {
                "_type": "ELEMENT",
                "name": { "value": "case_description" },
                "value": { "_type": "DV_TEXT", "value": "Inpatient dermatology case" }
              },
              {
                "_type": "ELEMENT",
                "name": { "value": "start_date" },
                "value": { "_type": "DV_DATE_TIME", "value": "2025-03-03T08:30:00Z" }
              },
              {
                "_type": "ELEMENT",
                "name": { "value": "end_date" },
                "value": { "_type": "DV_DATE_TIME", "value": "2025-03-03T12:00:00Z" }
              }
            ]
          },
          "folders": [],
          "items": []
        }
      ],
      "items": []
    },
    {
      "_type": "FOLDER",
      "name": { "_type": "DV_TEXT", "value": "Folder 2" },
      "archetype_node_id": "openEHR-EHR-FOLDER.generic.v1",
      "folders": [],
      "items": []
    },
    {
      "_type": "FOLDER",
      "name": { "_type": "DV_TEXT", "value": "Folder 3" },
      "archetype_node_id": "openEHR-EHR-FOLDER.generic.v1",
      "folders": [],
      "items": []
    }
  ],
  "items": []
}

I think the biggest challenge with the Folders approach is that in order to “put” a composition into a folder, you need to get the composition uid in the first step and then do an update of the folder in the next, which then is also clunky.

We could consider adding a new parameter to the header of the REST API which would contain the case ID and then does the management of folders in the background. Challenge is however, that there would have to be an equivalent when sending a contribution. Unfortunately, we won’t be able to address this short-term in EHRbase due to other project obligations . However, I agree that the pattern needs to be defined finally, this has been open for way too long.

It could be a single contribution right now correct?
It’s already somewhat common practice to update multiple compositions in a single contribution and thus single api call, agree?

I think you will need to retrieve the Folder first, add it to the contribution, let the client provide the composition uid and add it to the folder and the new composition as part of the contribution. So not really straight forward.

Edit: We also should consider adding Patch to add uids to the folder. Always fetching the whole thing is inefficient. I think @pablo raised this quite a while ago.