SDC Examples
The section dives into a variety of examples and common use cases for using the $extract
and $populate
flows with the Commure FHIR API. It explains some of the additional extensions we've introduced that make the processes more ergonomic and flexible.
As a reminder, Commure only supports definition-based extraction and expression-based population. These are experimental features that are a work in progress in both the FHIR specification and Commure's implementation.
This page is a list of JSON-based examples of how to format Questionnaire resources, as a kind of API reference. For a happy-path, realistic clinical scenario, please review the SDC Scenario. This scenario includes example requests to the FHIR server.
Definition-Based Extraction
From the FHIR documentation:
To use this method:
Include the
questionnaire-itemContext
extension either on the Questionnaire root or on 'group' items within the Questionnaire to identify the resource that will serve as the context for any extraction. The itemContext is used to set the context for the item.definition paths. If the itemContext is empty, then the Questionnaire is being used to create a resource. If the itemContext has a resource (or set of resources), then the Questionnaire is being used to update the resource(s).On descendant items of that element, fill in the
Questionnaire.item.definition
to point to the resource or profile element that Questionnaire item corresponds to. (Profiles may be relevant for data that is sliced or has fixed values for some properties.). The definition SHALL have the full canonical URL of the resource (or profile) followed by '#' followed by the snapshot.path of the element the Questionnaire item corresponds to.If necessary, define
questionnaire-hidden
items that haveQuestionnaire.item.initial.value[x]
or that use thequestionnaire-initialExpression
extension to define their content to use to populate resource elements that the user will not be filling in. (The initialExpressions might in turn depend onvariable
andquestionnaire-launchContext
extensions, used as described in the Expression-based population section.
We will cover step 1 and 2 first, and then cover step 3 after we have covered expression-based population.
* Please note that the FHIR spec used to call this the questionnaire-itemContext
extension, but now refers to it more specifically as the questionnaire-itemExtractionContext
extension.
Setting Up a Questionnaire for Extraction
The first step in Resource extraction is to add an itemContext
* extension wrapping around the item
s of the Questionnaire that you want to be extracted into the resource.
- Include the
questionnaire-itemContext
extension either on the Questionnaire root or on 'group' items within the Questionnaire to identify the resource that will serve as the context for any extraction. The itemContext is used to set the context for the item.definition paths. If the itemContext is empty, then the Questionnaire is being used to create a resource. If the itemContext has a resource (or set of resources), then the Questionnaire is being used to update the resource(s).
We recommend that you do this all in the root of the Questionnaire, rather than trying to limit the scope of the itemContext
to just the nested item
s to which it corresponds. This serves as a useful form of documentation: a user can read the top of the Questionnaire and understand what Resources it is intended to extract.
For example, if you are building a Questionnaire that extracts both a Patient and an Account, the top part of your form would look like this:
While the name
field isn't strictly necessary, it is helpful for both self-documenting, and also disambiguating between resources if your Questionnaire extracts more than one resource of the same type.
definition
Extracting a specific field with After having set up the root-level itemContext
s, it's time to map the item
s in your Questionnaire to specific fields in your extracted FHIR resources.
- On descendant items of that element, fill in the
Questionnaire.item.definition
to point to the resource or profile element that Questionnaire item corresponds to. (Profiles may be relevant for data that is sliced or has fixed values for some properties.). The definition SHALL have the full canonical URL of the resource (or profile) followed by '#' followed by the snapshot.path of the element the Questionnaire item corresponds to.
Continuing with our Patient and Account example:
Expression-Based Population
From the FHIR documentation:
To use this method:
- Include the
questionnaire-launchContext
extension to identify any contextual information that needs to be passed into the population process. Typical contexts would be the Patient or Encounter resources in whose context the questionnaire is being completed, but other elements are also possible (e.g. an AdverseEvent if performing an adverse event report). These 'context' elements will be available as expression variables for use in subsequent steps.- If appropriate, use the
questionnaire-itemContext
on group items to establish the context for a group. When populating the questionnaire, this will do two things: it will create a group repetition for each row returned from the query; and it will set the specified variable name to that resource repetition for use in processing items within the group.- For
Full Population
, use thequestionnaire-initialExpression
to cause the initial answer for the question to be set to the specified expression. This should always be a FHIRPath or CQL expression that resolves to an item of the appropriate type unless the element has a type of Reference, in which case the expression can ALSO be a FHIR query - and the item will be populated with a reference to the resource. Note, this extension SHALL NOT appear on groups.
We will cover steps 1 and 3 first, and then cover step 2 in the edge case considerations at the end.
* Please note that the FHIR spec used to call this the questionnaire-itemContext
extension, but now refers to it more specifically as the questionnaire-itemPopulationContext
extension.
Setting Up a Questionnaire for Population
Similar to how we set up itemContext
s at the root of the Questionnaire to kick off extraction, we'll also set up launchContexts
alongside them to kick off population.
- Include the
questionnaire-launchContext
extension to identify any contextual information that needs to be passed into the population process. Typical contexts would be the Patient or Encounter resources in whose context the questionnaire is being completed, but other elements are also possible (e.g. an AdverseEvent if performing an adverse event report). These 'context' elements will be available as expression variables for use in subsequent steps.
These resources are the values that we will expect to be passed in as a subject
or content
Parameter in calls to $populate.
Example of a Patient launchContext
:
The name
section can be any variable name you like; it will be used like this - %patient
- in the ensuing expressions inside the item
s. The type
should be just an all-lowercase string of the FHIR resource type. description
is human-readable documentation.
Using a FHIR path to set the initial value of a Questionnaire item
After setting up the launchContext
, the way to leverage those resources is fairly straightforward if you are familiar with FHIR path:
- For
Full Population
, use thequestionnaire-initialExpression
to cause the initial answer for the question to be set to the specified expression. This should always be a FHIRPath or CQL expression that resolves to an item of the appropriate type unless the element has a type of Reference, in which case the expression can ALSO be a FHIR query - and the item will be populated with a reference to the resource. Note, this extension SHALL NOT appear on groups.
The QuestionnaireResponse will get pre-populated with the value traversed into by the associated FHIR Path expression:
Commure does not currently support CQL expressions in initialExpressions.
Edge cases and quirks
When used in concert with each other, expression-based population and definition-based extraction introduce a few more wrinkles and edge cases. We enumerate some of them here.
Retaining IDs through the populate-and-extract lifecycle
Step 3 in the FHIR documentation for definition-based extraction noted:
- If necessary, define
questionnaire-hidden
items that haveQuestionnaire.item.initial.value[x]
or that use thequestionnaire-initialExpression
extension to define their content to use to populate resource elements that the user will not be filling in. (The initialExpressions might in turn depend onvariable
andquestionnaire-launchContext
extensions, used as described in the Expression-based population section.
So if we are populating a QuestionnaireResponse with a Patient with Patient.id
123, we want the extracted Patient resource to also have Patient.id
123. But we don't want to burden the user with seeing this Patient.id
field. This is how we accomplish this:
Retaining indexes when extracting a pre-populated 0..* or 1..* field
When performing a populate-and-extract flow, it is important for the indexes of 0..*
to be preserved. Absent a marker for indicating, for example, that a certain Patient.name.given
value was at index 0
, the extraction process would assume that we are dealing with an addition to the list of values at Patient.name.given
.
Step 2 in the FHIR documentation for expression-based population noted:
- If appropriate, use the
questionnaire-itemContext
on group items to establish the context for a group. When populating the questionnaire, this will do two things: it will create a group repetition for each row returned from the query; and it will set the specified variable name to that resource repetition for use in processing items within the group.
We piggyback on this approach to population "context" to solve this issue. Commure appends a custom extension, sdc-merge-context
, to index-sensitive values during the $populate call. In order to declare a value as "index-sensitive," you must (1) provide a definition
for the group
's item
that has the 0..*
or 1..*
cardinality, and (2) provide an itemContext
that, similarly to expression-based population's initialExpression
, sets the context as described in the FHIR documentation.
Here is an example with Patient.name
:
Providing those two values will result in output from $populate
that looks something like:
And when you pass a QuestionnaireResponse with that value into $extract
, Commure's extraction process will correctly preserve the index of that value in the list.
Advanced: populating and extracting an extension
Performing SDC on extensions can be tricky because of all the hidden fields and layers involved. However, the concepts to effectively populate and extract them have already been introduced above - with the exception of needing to introduce an enableWhen
to avoid extraction of subtly incomplete fields. Here is a full example of populating and extracting the qicore-military-service extension on Patient: