# Is it possible to follow a link in AQL? **Category:** [AQL](https://discourse.openehr.org/c/aql/43) **Created:** 2025-07-17 16:44 UTC **Views:** 217 **Replies:** 25 **URL:** https://discourse.openehr.org/t/is-it-possible-to-follow-a-link-in-aql/6994 --- ## Post #1 by @emmanuel.eschmann 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? --- ## Post #2 by @ian.mcnicoll The simple answer right now is no. This has bern under discussion to be added to the spec though --- ## Post #3 by @ian.mcnicoll It is possibl that some local implementations do something better. --- ## Post #4 by @emmanuel.eschmann Thank you very much for your clear answer! --- ## Post #5 by @pablo 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?). --- ## Post #6 by @Seref Are you able to discuss your requirements for following the links? --- ## Post #7 by @JPMesserli 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 + “$”} --- ## Post #8 by @JPMesserli 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 `links`RM 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](https://specifications.openehr.org/releases/RM/latest/common.html#_link_class) : “Values for meaning include those described in Annex C, ENV 13606 pt 2” * `type`: “administrative_case_number” * `target`: the `uid`of 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? --- ## Post #9 by @pablo 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 https://specifications.openehr.org/releases/RM/latest/common.html#_overview_4 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? --- ## Post #10 by @JPMesserli 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? --- ## Post #11 by @pablo [quote="JPMesserli, post:10, topic:6994"] Access to folder information and the list of compositions contained within is primarily via the folder, rather than the composition. [/quote] Why is that a disadvantage? You have a specific endpoint to get a folder and its contents: https://specifications.openehr.org/releases/ITS-REST/latest/ehr.html#tag/DIRECTORY/operation/directory_get_at_time [quote="JPMesserli, post:10, topic:6994"] ‘AQL on Folders’, which provides direct access to folders containing a composition, is still experimental. [/quote] Agree, though that is more support than the AQL for LINKs. [quote="JPMesserli, post:10, topic:6994"] Filing of a composition in a folder is not clearly visible as folders are not an explicit part of the template. [/quote] 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. [quote="JPMesserli, post:10, topic:6994"] Folders are not returned when getting a composition (neither by REST nor by standard AQL) [/quote] 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. --- ## Post #12 by @JPMesserli 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 --- ## Post #13 by @birger.haarbrandt 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? --- ## Post #14 by @JPMesserli [quote="birger.haarbrandt, post:13, topic:6994"] 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? [/quote] 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 ![image|546x312, 50%](upload://h8j3uYVvWgIgvd1uAlIR3yDnxGO.png) ![image|690x284, 50%](upload://sP6QWqicHRibAcwICwemGTkeKEr.png) --- ## Post #15 by @joostholslag 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: https://ons-api.nl/techniek/apis/APIS.html#/openehr.CompositionWrapper/openehr.CompositionWrapperAPI.incomingLinks 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. --- ## Post #16 by @pablo [quote="JPMesserli, post:12, topic:6994"] Start the query via a folder to find the corresponding compositions. [/quote] This feels more like a get FOLDER by ID / type / time range, then traversing it's contents than a single query. [quote="JPMesserli, post:12, topic:6994"] Start the query via a composition where you want the corresponding administrative case [/quote] 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). [quote="JPMesserli, post:12, topic:6994"] 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? [/quote] 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: openehr.atlassian.net/browse/SPECPR Best, Pablo. --- ## Post #17 by @JPMesserli 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": [] } ``` --- ## Post #18 by @birger.haarbrandt 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. --- ## Post #19 by @joostholslag 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? --- ## Post #20 by @birger.haarbrandt 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. --- ## Post #21 by @yampeku [quote="birger.haarbrandt, post:20, topic:6994"] 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. [/quote] Being worked out in REST API WG :smiley: --- ## Post #22 by @birger.haarbrandt That's good to hear. Wish I would finally manage to participate more actively :unamused_face: --- ## Post #23 by @pablo [quote="birger.haarbrandt, post:20, topic:6994"] 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. [/quote] Yes! the idea was to allow commits to sub-structures in the main EHR.directory without the need to committing the whole directory each time, though I'm not sure what's the current status. --- ## Post #24 by @pablo [quote="JPMesserli, post:17, topic:6994"] Is it possible to load and update only a subfolder, e.g. only the ‘Administrative Cases’ folder without always loading all folders? [/quote] That's the update for subfolders proposal for the SM / API (it could be PATH for the REST API) https://openehr.atlassian.net/issues/?jql=textfields%20~%20%22subfolder%2A%22&selectedIssue=SPECPR-338 [quote="JPMesserli, post:17, topic:6994"] Is it advisable to create a separate folder with subfolders for each case to map the administrative cases? [/quote] I don't think there is a best practice that applies to every case, I would suggest to adopt/adapt folders in the way you need to structure them. [quote="JPMesserli, post:17, topic:6994"] 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’? [/quote] I would say the generic one is kind of a place holder, you might need to create your own archetype for the folder details if the current ones don't adapt exactly to your requirements. Then, you can share it with the community in the CKM if you want. [quote="JPMesserli, post:17, topic:6994"] How can I load my FOLDER archetypes into the CDR so that they are available for validating the details when saving? [/quote] I think most CDRs work with Operational Templates (OPT), not with archetypes directly, though there are exceptional cases depending on the implementation. In our implementation (https://atomik.app/) we have OPTs for everything: EHR_STATUS, COMPOSITION, FOLDER, PERSON, PARTY_RELATIONSHIP, ROLE, ORGANISATION, etc. For testing, we upload the OPTs on the openEHR Toolkit to generate instances that comply with the OPTs (https://toolkit.cabolabs.com/). So the data validation happens always against the OPTs. Depending on the modeling tool you use, some might not support exporting the FOLDER archetype as a template. We have a tool in the openEHR SDK to do that (https://github.com/CaboLabs/openEHR-SDK), which will be added to the Toolkit soon. About managing stuff in the FOLDER, I'm assuming you can control the client side. So you can create the episode folder in the same "transaction" as the first document you want to include in the folder, and you can do it in any way: 1. create folder 2. create doc 3. update folder with doc Or: 1. create doc 2. create folder with doc Of course if there is a workflow that happens way before the first document is available but the episode itself needs to be created, you need to create the folder with its details previously, in a different transaction, then add the document later in another transaction, and that's normally how you add the rest of the documents to the episode. Best, Pablo. --- ## Post #25 by @JPMesserli Thanks a lot again to everyone! I still have a few questions about RM links. The links consist of 'meaning', 'type' and 'target' * meaning = contains the semantic relationship – here only the code (e.g. LINK-E1i) from ISO 13606-3:2019 * type = domain category of the link (e.g. ‘episode_of_care’, ‘administrative_case_number’) for classification/filtering based on meaning Questions about LINK.Meaning: * ISO 13606-3:2019 is not accessible. Is it possible to use an HL7/FHIR value set e.g. https://build.fhir.org/codesystem-related-artifact-type.html * If yes, can the terminology used be stored in LINK with 'mappings', as sketched below? Questions about LINK.type: * There is no official, global value list in openEHR für 'type'. Is that correct? * According to my research, there are frequently used terms. I have tried to list them in an Excel file with their meanings and also tried to map them to HL7 (attached below). What is your opinion on this? Or are there best practices or official lists? Snippet with ISO 13606-3:2019 Code for meaning ``` "links": [{ "meaning": { "value": "LINK-E1i" }, "type": { "value": "administrative_case_number" }, "target": { "value": "ehr://composition/" } }] ``` Snippet with HL7 and mapping ``` "links": [{ "meaning": { "value": "part-of", "mappings": [{ "match": "=", "target": { "terminology_id": { "value": "http://hl7.org/fhir/related-artifact-type" }, "code_string": "part-of" } }] }, "type": { "value": "administrative_case_number" }, "target": { "value": "ehr://composition/" } }] ``` [openEHR_LINK_type_governance.xlsx|attachment](upload://18gJElA3zzXWWbetKxcd7lui5AK.xlsx) (9.7 KB) --- ## Post #26 by @yampeku [quote="JPMesserli, post:25, topic:6994"] ISO 13606-3:2019 is not accessible. Is it possible to use an HL7/FHIR value set e.g. [Codesystem-related-artifact-type - FHIR v6.0.0-ballot2](https://build.fhir.org/codesystem-related-artifact-type.html) [/quote] In the ISO24305 that relates ISO13606 and ContSys with FHIR we found the valueset http://hl7.org/fhir/ValueSet/document-relationship-type as the closest valueset (pretty sure this did not make the cut onto the final norm text) Notably this FHIR valueset is a subset of the ISO13606 Link codes --- **Canonical:** https://discourse.openehr.org/t/is-it-possible-to-follow-a-link-in-aql/6994 **Original content:** https://discourse.openehr.org/t/is-it-possible-to-follow-a-link-in-aql/6994