2.7.0 - STU 3 (2nd ballot)

This page is part of the Structured Data Capture FHIR IG (v2.7.0: STU 3 Ballot 2) based on FHIR R4. The current version which supercedes this version is 3.0.0. For a full list of available versions, see the Directory of published versions

Using Expressions

The Form Behavior, Questionnaire Population and Data Extraction all rely on (or have features that rely on) the use of expressions. This page provides specific guidelines for use and defines some additional conventions that can be leveraged in queries, FHIRPath and CQL by implementations that comply with this implementation guide.

Use of the Expression type

The Expression data type has 5 elements:

  • description provides a human-readable explanation of what the expression is doing. This is development documentation. While not required, it's strongly encouraged unless the expression is so simple there won't be any question as to its intent and function.
  • name allows the result of the expression to be referenced within other expressions. Whether this is necessary depends on the context
  • language indicates the type of expression. SDC only uses three languages FHIR queries as described below, FHIRPath and CQL. Also see the discussion on FHIRPath vs. CQL.
  • expression contains the expression if it is written in-line (as opposed to by reference)
  • reference points to the expression if it's not inline. For SDC purposes, reference must refer to content from a library imported using the cqf-library extension. For CQL, if the expression contains the name of the CQL content, the reference URL can simply be the canonical URL of the library. If the expression is blank for CQL or if conveying FHIRPath or a query, the specific reference URL must look like this:
    [canonical URL of library]#[id element of the 'content' repetition whose Attachment.data holds the expression] E.g. http://somewhere.org/fhir/Library/12345#a2

Expression Extensions

Expressions are introduced into Questionnaires using extensions - none of the 'core' data elements of Questionnaire make use of extensions because they're considered an 'advanced' capability that aren't currently supported by a large portion of the systems that make use of the Questionnaire resource. The extensions that make use of expressions are shown in the table below. Columns are interpreted as follows:

  • Extension: The code for the extension (and a link to the extension definition)
  • Source: Indicates where the extension is defined - the FHIR Core specification (core) or this implementation guide (SDC). Items marked as 'core' will have a canonical URL of http://hl7.org/fhir/StructureDefinition/[code]. Those from SDC will have http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-[code]
  • Location: Indicates whether the extension can appear directly on the Questionnaire (root) on any item (item) or only on items that have at type allowing a question answer (question)
  • Content: Indicates whether the expression.language is restricted to be "text/cql" (cql), "text/fhirpath" (FHIRPath), or "application/x-fhir-query" (query) or is unrestricted (any)
  • Use Cases: Indicates which of the SDC use-cases make use of the extension: Form Behavior (behavior), Questionnaire Population (population), and/or Data Extraction (extraction). Other SDC use-cases don't generally have a need for the expression elements.

Beneath each extension row is an explanation of the purpose of the extension and guidance on its use.

Extension Source Location Content Use Cases
variable Core root or item any all

This expression sets a variable that is available for use in expressions within the same item and any descendant items. It has two main uses:

  • It allows a complex calculation to be done once and used in multiple other places. (E.g. Determining the score for one group within the questionnaire response that will then be used in calculations on subsequent groups.)
  • It allows a calculation to be done closer to the root of the questionnaire response or at the root of the questionnaire response where there is access to more of or all the answers from the questionnaire response. The calculated value might then be used as the answer to a descendant question. (Expressions can't access answers that aren't descendants of the current node.)

The content type of a variable can be pretty much anything. It can be a collection or an individual item. It can be a simple element, a complex type, a resource or even a Bundle of resources. The variable can be referenced by its name. Variable expressions SHALL specify a name.

initialExpression SDC question FHIRPath or CQL all

This expression serves the same purpose as the initial.value on Questionnaire.item, except that rather than specifying a fixed value, the value is calculatable. The value is set on initial creation of the QuestionnaireResponse or upon the question first becoming enabled. If used for a readOnly and/or hidden element, the expression essentially calculates a 'fixed value' for the element. If used for an editable element, this extension sets the initial value for the element. However, the value may subsequently be overridden by the user authoring the response.

In theory, this expression can rely on the values of the answers to child questions or on variables calculated from the values of other questions, however such questions would have to initial values. (See the discussion below on dependencies.) Most typically, it will draw on launchContext and itemContext to populate questionnaire elements based on information available from outside the QuestionnaireResponse.

Examples of use for Form Behavior might include defaulting one date or provider in a form to one entered earlier in the form, defaulting a selected location based on patient address, etc. For population, this is the primary mechanism of populating fields. Information passed in launchContext or queried from the health record is used to determine the initial value for the field. The response author then can review and potentially adjust the values. Finally, for extraction, this can be used to fill in 'hidden' elements that are needed for extraction but are not actually seen or edited by the user (for example, FHIR resource ids when updating existing records).

It's possible for the value of an initialExpression to be referenced by name within other sibling expressions or by expressions on descendant items. However, this is not likely to be common. As such, initialExpressions only need to declare a name if their value will be referenced elsewhere.

candidateExpression SDC question FHIRPath or CQL population

This expression is used to support data entry. It is only invoked when the user is entering/editing the answer for the question linked to the candidateExpression. It is used when a population process can't determine the specific value to fill in to a form, but it can identify a list of candidates to choose from. For example, forms might ask for relevant co-morbidities, other practitioners to inform, etc. The specific answers require human decision-making, but this expression can provide a list of initial candidates based on the patient's recorded health conditions, the patient's care team members or others involved in their care, etc. Another example might be filtering a list of possible dosage instructions based on the patient's age, gender and the previously selected indication but where there are still possibly multiple recommended options.

While this expression could be based on other answers within the questionnaire, most typically it will be based on information found within the health record. The expression must evaluate to a collection of elements with the same type as the question answer. For choice and Reference questions, optionColumns can be used to manage display.

NOTE: the set of candidate expressions is intended to be a starter set, not exhaustive. The user should be free to enter or look-up additional values. Systems should allow multiple selections in situations where the question is marked as 'repeating' and multiple answers are therefore valid, but should only allow one answer for non-repeating questions.

CandidateExpressions should rarely, if ever, declare a name element.

contextExpression SDC question FHIRPath or CQL population and extraction

This is similar to candidateExpression, in that it is also only used when the user is editing the answer for the question associated with the candidateExpression. However, instead of giving candidate answers that can simply be "selected", this option instead presents potentially relevant information. For example, a question might be "Has the patient been admitted for this or a related condition in the past year?". To help guide the answers, the contextExpression could retrieve a list of admission reasons for that patient for encounters in the past year. The user completing the questionnaire could then use that information to guide whether they choose to answer 'yes' or 'no.

Also, like candidateExpressions, contextExpressions should rarely, if ever, declare a name element.

calculatedExpression SDC question FHIRPath or CQL behavior

This expression allows for dynamic calculation of answers to questions as other questions are answered. It behaves similarly to initialExpression, but instead of only setting its value when the QuestionnaireResponse is originally created or when a question is enabled, the value updates continuously as the answers to dependent questions change. This expression will be most commonly used for displaying scores, but can be used for any calculated element - patient age (based on current date and birth date), BMI (based on recent weight and height), estimated cost (based on selected items and quantities), etc.

Managing the updating of calculatedExpressions can be tricky as they can have many dependencies. Implementing an event-based listener approach may prove more efficient than simply recalculating all expressions every time any value in the questionnaire changes anywhere. Also, refer to the section below on dependencies which explains how to handle order of evaluation and other concerns.

In most cases, 'calculated' answers should be marked as 'readOnly', however in some cases it may be legitimate to override a calculated element, for example if the calculation can't be made due to non-available information, but the user still knows the calculated value. For example, it might be possible to enter an age but not a date of birth. If a user has edited a calculated value, it should no longer be changed when other answers in the questionnaire response change. When loading a previously persisted QuestionnaireResponse, the determination of whether a user has edited the calculated field can be determined by whether the calculated value differs from invoking the calculation on the current values in the questionnaire. (As a side-effect, this means that calculated values based on 'context' information such as an age calculated based on 'now' will be treated as user-edited when subsequently opening the QuestionnaireResponse and will not update to reflect the new current date.)

Like initialExpression, it's possible for the value of a calculatedExpression to be referenced by name within other sibling expressions or by expressions on descendant items. However, this is not likely to be common. As such, calculatedExpressions only need to declare a name if their value will be referenced elsewhere.

enableWhenExpression SDC question FHIRPath or CQL behavior

This expression does the same thing as the Questionnaire.item.enableWhen structure, but allows for more complex logic. It's possible to calculate a score based on the answer to several questions and then enable other questions based on the score. It's also possible to enable or disable questions based on data passed in as context or retrieved from queries.

Like calculatedExpression, enableWhenExpression needs to be evaluated each time any of the answers it depends on changes. The same event monitoring approach may be appropriate (and even shared). And the dependencies section will be relevant here as well.

The Expression.name element only needs to be populated in the unlikely circumstance the expression is going to be referenced by other expressions on the same item or in descendant items.

itemContext SDC any query population, extraction

This expression identifies the resource(s) that correspond either to an overall questionnaire, a group within the questionnaire or, occasionally, a single question. The results of executing the specified query are used to indicate the resources used to populate the item when performing population and identifies the data to replace when performing an extraction - and also indicates the relevant resource type.

If a query results in multiple repetitions for a single for the root Questionnaire or for an item where 'repeat' is false, it is an error and no population or extraction can occur. If the query results in no elements when extracting, then the extracted data is treated as a create. If data is returned then the extracted data should replace what is returned, either as an update or a delete + create.

Expression.name SHALL be populated when this extension is being used for FHIR-path based population. It doesn't necessarily need to be present for definition-based extraction, as that relies on item.definition rather than other expressions. However, including it in general is a good idea.

Other extensions

There are a couple of additional extensions that are also relevant when working with expressions. They are:

Extension Source Location Use Cases
library core root all

Library allows queries, CQL and FHIRPath expressions to be maintained independently from the Questionnaires that use them. This means that the "programmatic" content of the Questionnaire can be updated and adjusted without revising the Questionnaire, which may be appropriate in certain form management environments. It also means that the population and extraction mechanisms can be adjusted as data representations evolve or be adjusted to reflect the needs of additional data repositories.

This extension identifies libraries that the questionnaire relies on for some or all its expressions. (The mechanism by which expressions point to library content is discussed above.)

launchContext SDC root all

The launch context allows information to be passed into the FHIRPath evaluation environment based on the context in which the Questionnaire is being evaluated. For example, what patient, what encounter, what user, etc. is "in context" at the time the questionnaire response is being completed. Note that this context could potentially change if a questionnaire is edited after it was initially created, which means that if launchContext elements drive calculatedExpression elements rather than initialExpression elements, they'll be updated to reflect the new user.

Launch context information is passed to the QuestionnaireResponse evaluation process by the launching application. It will pass whatever contexts it is aware of. SDC systems SHOULD support all four of the contexts defined in this specification, but are free to support others. The selected contexts mirror those used by the SMART on FHIR specification.

The resource corresponding to each context will be made available at the indicated launch context name as if the value had been set in a variable.

x-fhir-query enhancements

The Expression type indicates that one of the allowed languages is application/x-fhir-query. This is defined as a FHIR search string. Normally this will not include the base URL and is intended to be invoked against the system that is completing the QuestionnaireResponse (or whatever FHIR repository the Form Filler is configured to use). However, in some cases, a full URL might be specified, for example if the author of the questionnaire wants values to always be retrieved from a specific source.

In addition to the base query syntax, SDC allows the injection of FHIRPath into the query expressions. This is to allow the queries to take advantage of context when they are invoked. The FHIRPaths are denoted by surrounding them with double curly-braces (i.e. {{ fhirpath goes here }}) in the same way expressions are denoted in the Liquid templating language.

For example:

      Observation?code=http://loinc.org|65972-2&date=gt&subject=
   

would return all Observations with the specified LOINC code made in the last week for the specified patient (the %patient variable would likely have been set using launchContext.)

Dependencies between expressions

Expressions will often have dependencies on other expressions. For example, a calculatedExpression might depend on variables that in turn depend on the answers to other questions which might themselves have initialExpressions which might rely on itemContext queries that in turn have embedded FHIRPaths that reference elements defined in launchContext. If care isn't taken, it's possible to construct a questionnaire where the value of an answer depends indirectly on itself, or where filling out an answer disables the question being answered or other inappropriate behavior. Questionnaire rendering systems should check for and gracefully handle such situations. (I.e. Raise appropriate error messages rather than crashing or locking in an endless loop.)

When evaluating expressions, the typical order of calculation will be in the order the expressions appear in the Questionnaire. I.e. Those at the root are evaluated first, then items are evaluated in order, evaluating on a depth-first basis. Children of the first item in a group are evaluated before the second item in the group.

However, in some cases, updates to later elements may trigger an update to earlier elements, which then cascade through again. Systems should allow for this and allow for iterative updating of expressions until values reach a stable state - or until an unstable looping condition is detected.

FHIRPath and Questionnaire

The FHIRPath language defines a set of contexts that get passed into expressions and allows the definition of additional contexts and functions. The SDC provides the following guidance for evaluating FHIRPath (and CQL and FHIR Queries) in the context of a Questionnaire:

  • The %context variable will be set to the QuestionnaireResponse if the expression extension is defined on the Questionnaire root. Otherwise, it will be set to the QuestionnaireResponse.item for items whose linkId match the linkId of the Questionnaire.item the expression extension was defined in. I.e. While the extensions and expressions are defined in the Questionnaire, they are evaluated in the context of the corresponding item(s) in the QuestionnaireResponse.
  • A new %questionnaire is defined for that corresponds to the Questionnaire resource resolved to by the QuestionnaireResponse.questionnaire element. It is in scope for all expressions defined in the Questionnaire.
  • All expressions that specify a 'name' can be accessed by other expressions appearing on the same item or any descendant item as though that name was part of context. The same is true for the 'name' element in the launchContext. For example, a variable with the name 'score' could be accessed in FHIRPath or CQL using %score. (Note: It is an error if a questionnaire is designed such that there is more than one element in the same scope with a colliding context name.)

FHIRPath vs. CQL

SDC supports both FHIRPath and CQL as languages for defining expressions. In practice, FHIRPath is a proper subset of CQL, so the question of whether to use one or the other comes down to whether any of the additional capabilities of CQL (such as defining internal variables, iterating loops, etc.) is needed. FHIRPath is more widely implemented than CQL, so questionnaires that only make use of FHIRPath will typically be more widely supported.

Missing information

Sometimes information needed for an expression won't be available. Perhaps the system can't pass encounter information - or maybe the form is being filled in outside the context of an encounter at all. Perhaps a query results in an error because of a server issue or perhaps the user or system filling in the questionnaire doesn't have permission to access the relevant data. Systems should fail gracefully in such situations. Populated or calculated elements can be left empty. Extracted elements can be omitted. If control logic such as enableWhen is impacted, either display a helpful error message or permit entry of potentially relevant information if it can't be determined whether the data should be allowed or not.