FHIR Chat · Clarification on contentReference · IG creation

Stream: IG creation

Topic: Clarification on contentReference


view this post on Zulip Chris Moesel (Feb 10 2020 at 16:58):

I'd like to ensure that we are handling contentReference correctly. There are two points in the wording of the spec documentation that we'd like clarified.

The definition says:

Identifies an element defined elsewhere in the definition whose content rules should be applied to the current element. ContentReferences bring across all the rules that are in the ElementDefinition for the element, including definitions, cardinality constraints, bindings, invariants etc.

The comment says:

ContentReferences can only be defined in specializations, not constrained types, and they cannot be changed and always reference the non-constrained definition.

We find the current wording a little confusing, as the definition implies that "rules" (including cardinality constraints and bindings) should be "brought over" -- but the comment implies that "contraints" should not be brought over (i.e., contentReferences "always reference the non-constrained definition"). We're not sure if we're misunderstanding the definition (e.g., is it actually referring to "all the rules" on the element w/ the contentReference, not the pointed-to element?), or if we're misunderstanding what is meant by the "non-constrained definition."

view this post on Zulip Chris Moesel (Feb 10 2020 at 17:02):

So... Let's say a profile on Questionnaire has some constraints on item -- say a required extension and a binding on code. Does item.item inherit those constraints or not? We think the intent is that it does not. We think that the proper way to think of contentReferences is that they start w/ the unconstrained definition from the base FHIR resource, and then retain any constraints/definitions that were put on the element that contains the contentReference. Any constraints in the profile on the original element pointed to by the contentReference are not inherited -- as the context of the contentReference may be quite different than the original occurrence in the profile. Is that right?

view this post on Zulip Lloyd McKenzie (Feb 10 2020 at 17:53):

It's a bit of a mess - as there are constraints that apply to the 'type' and others that apply to the 'element'. We'd like the former to apply to all uses and the latter to not carry across. Unfortunately we don't have a mechanism to differentiate. @Grahame Grieve, do you remember where we ended with this?

view this post on Zulip Grahame Grieve (Feb 10 2020 at 19:55):

I'm not sure that the type/element thing matters here.

view this post on Zulip Grahame Grieve (Feb 10 2020 at 19:57):

I'm not actually sure what the answer is (or should be). The implication of the rules cited is that @Chris Moesel is right, but then I'm not sure how to get re-use in a profile - I can't remember whether we discussed that or not

view this post on Zulip Grahame Grieve (Feb 10 2020 at 19:58):

the comment implies that "contraints" should not be brought over

no, it means that you can't do it in a constraint, and anything in the constraint doesn't matter.

view this post on Zulip Chris Moesel (Feb 10 2020 at 20:00):

Thanks, @Grahame Grieve -- I was specifically referring to "always reference the non-constrained definition" when I suggested it implies that constraints should not be brought over (from the referenced element to the element w/ the contentReference). Sorry for the confusion.

view this post on Zulip Grahame Grieve (Feb 10 2020 at 20:02):

actually, what I think it's saying is that you don't touch contentReference in the profiles. It always refers back to the base definition.

view this post on Zulip Grahame Grieve (Feb 10 2020 at 20:02):

you can reuse constraints using this extension:

view this post on Zulip Grahame Grieve (Feb 10 2020 at 20:04):

http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element

view this post on Zulip Chris Moesel (Feb 10 2020 at 20:04):

actually, what I think it's saying is that you don't touch contentReference in the profiles. It always refers back to the base definition.

Ah. OK. That's a clearer interpretation. Got it.

view this post on Zulip Chris Moesel (Feb 10 2020 at 20:14):

you can reuse constraints using this extension:
http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element

I think I'm going to need to think about this one some more... Let me see if I understand... So if an element with a contentReference uses this extension to point to the constrained version of the element it references (in its own profile), then it takes on the constraints of that one? Is that what you mean?

The element w/ the contentReference doesn't have a type -- and this extension is supposed to go on type.profile -- so is the implication that you have to copy over the type from the referenced element into the element with contentReference, add a profile in the type that points to itself, and then apply this extension on that profile, pointing to the same path as the contentReference?

view this post on Zulip Grahame Grieve (Feb 10 2020 at 22:46):

I'm not sure. What I'm sure about is that we don't have a test case for this for the validator. So let me design one and then we can talk about. Do you have a scenario you are working with?

view this post on Zulip Grahame Grieve (Feb 10 2020 at 22:46):

(e.g. what element are you looking to constrain?)

view this post on Zulip Chris Moesel (Feb 11 2020 at 00:44):

I don't have a very specific use case. It came up because someone was using FSH to write an instance of SDC Questionnaire -- and asking about the extensions on item and if they applied to item.item. They pointed out that the individual narrative definitions of the extensions said they applied to both item and item.item, but that this is not as clear in the profile view.

view this post on Zulip Chris Moesel (Feb 11 2020 at 00:47):

They assumed that any constraints/extensions on item automatically applied to item.item because of the contentReference -- and since that was not my understanding (as a general concept), I wanted to double-check with the FHIR community. (Especially since we implemented contentReferences in SUSHI based on the interpretation that I expressed in the first messages of this thread).

view this post on Zulip Grahame Grieve (Feb 11 2020 at 02:06):

ok I'll do my exmaple based on that

view this post on Zulip Chris Moesel (Jul 24 2020 at 12:53):

Hi @Grahame Grieve -- this question has come up again in the #shorthand stream (this time in reference to PlanDefinition.action and how you can have an extension apply to all recursive instances of action (e.g., PlanDefinition.action.action, PlanDefinition.action.action.action, etc.)). Did you ever get a chance to flesh this out in an example and think more on how it actually should work?

view this post on Zulip David Hay (Jul 24 2020 at 19:06):

If it helps - this is the IG I'm working on that triggered the question. It uses PlanDefinition that I'm trying to simplify for a regimen use case - using one of the examples of that resource type.
@Bryn Rhodes - I had intended to ping you at some point for comment on whether I was doing this correctly anyway...

view this post on Zulip Grahame Grieve (Jul 24 2020 at 20:31):

the extension automatically applies to the recursive instances

view this post on Zulip Chris Moesel (Jan 07 2021 at 13:43):

Resurrecting this topic... We've established that in a profile a contentReference will inherit extensions declared on the source element (in the profile) that the contentReference points to. Are there any other contraints, potentially added by the profile to the source element, that should be inherited as well? Or only extensions? While it would be easy to say they all apply, I imagine this gets complicated when considering implications for something like ValueSet.compose.exclude (since its source element, ValueSet.compose.include, has literally the opposite meaning -- so all constraints from include might not be semantically appropriate for exclude).

view this post on Zulip Chris Moesel (Jan 07 2021 at 14:28):

Furthermore, even if we do bring over extensions from the source element to the contentReference, which constraints on the extension element(s) are brought over? Is cardinality carried over? What about metadata like "short" and "definition" if those were modified in the profile use of the extension? How about if the extension's valueCodeableConcept was bound to a value set _in the profile use_ (not the extension definition). Does that get carried along into the content reference? Or is it just the original extension definition that gets carried over?

view this post on Zulip Chris Moesel (Jan 20 2021 at 16:44):

Grahame Grieve said:

the extension automatically applies to the recursive instances

Hi @Grahame Grieve. I'm looking into this further and have a question. I'm testing using a slimmed down PlanDefinition profile from the NZ CcaImplementationGuide. In the profile, action.extension contains a sact-plan-instructions extension, and act.act.extension adds a sact-timing-of-days extension. This is what the differential looks like:
image.png

view this post on Zulip Chris Moesel (Jan 20 2021 at 16:45):

Based on your comment above, I assumed I do not need to redeclare thesact-plan-instructions on action.action.extension in the differential, because "the extension automatically applies to recursive instances."

view this post on Zulip Chris Moesel (Jan 20 2021 at 16:49):

But when I look at the snapshot (after running IG Publisher), I don't see the sact-plan-instructions applied to action.action.extension. It shows only the extension I explicitly declared on action.action.extension. Is this an issue w/ the publisher, or do I need to redeclare the extension from the element the contentreference refers to (even though it is supposed to be automatically applied)?
image.png

view this post on Zulip Chris Moesel (Jan 21 2021 at 14:14):

@Ewout Kramer @Ward Weistra -- you have a snapshot generator, so I'll ask you too. ;-). Any thoughts on how differentials should work w/ contentReferences, particularly in regard to extensions on the element the contentReference points to? See above few messages for details...

view this post on Zulip Ewout Kramer (Feb 01 2021 at 16:38):

Hi Chris. These are all great questions, and the reason it remains silent is probably because......I don't know. I do remember discussing this years ago at FHRI-I, but we've just let the topic rest since we did not came up with definite answers. We can check what our current snapshot generator is doing, but that's not to say that's the correct thing to do!

view this post on Zulip Chris Moesel (Feb 01 2021 at 16:47):

Thanks, @Ewout Kramer. I suspected that the silence might reflect that this is a topic to no one has clarity on. For now, we've implemented it like this:

  • When a content reference refers to an element that has an extension defined, SUSHI will consider that extension -- and any constraints on that extension -- as applicable the content reference as well.
  • If SUSHI has to manifest any sub-paths of the content reference in the differential, it will also manifest the "inherited" extension in the differential.

The second bullet above ensures that the snapshot will be generated with the extension in place and should be harmless since redundant elements in the differential are basically like no-ops (except when they're not, like in this case!).

view this post on Zulip Ewout Kramer (Feb 02 2021 at 08:43):

Hi Chris - I looked at our implementation. We are applying the constraints on the referring element on top of the referred element. Actually, we are applying them over the original definition in the core profile of the referred element. So, if you profile Questionnaire and put constraints on Questionnaire.item, Questionnaire.item.item will not inherit the constraints from the profiled Questionnaire.item but rather on top of the original element in the core definition. It's clear from the code that this was not always the case - originally they were applied on top of the constraints on the referred element (profiled or not) - but in april 2018 Michel has changed this bit - no doubt after a discussion with Grahame on this topic.

view this post on Zulip Chris Moesel (Feb 03 2021 at 20:28):

Thanks, @Ewout Kramer. If I understand you correctly, what you're describing is exactly how SUSHI used to work. We understood the content reference to refer to the element from the base resource, not the current profile. So constraints on the referring element were merged w/ a copy of the referred element from the base resource. This effectively meant that if an extension was required on Questionnaire.item in a profile, that extension was not implied to be required (or even defined) on Questionnaire.item.item.

But... in the thread above, @Grahame Grieve said "the extension automatically applies to the recursive instances", which means that the former approach of referring only to the element in the base resource definition was incorrect. We then implemented it that way in SUSHI, but it led to more questions, as evidenced here. Still, we have the basic use case of recursively applying extensions implemented and working now. But.. it's not yet released, so if that is in fact wrong, I'd like to fix it before it goes out to the world in a release.

view this post on Zulip Grahame Grieve (Feb 03 2021 at 20:32):

umm there might be a misunderstanding here

view this post on Zulip Grahame Grieve (Feb 03 2021 at 20:33):

it may be on my part

view this post on Zulip Grahame Grieve (Feb 03 2021 at 20:34):

There's 2 different things that we could be talking about

  • how to understand StructureDefinition.context
  • how to profile an element

I thought we were talking about the first. I agree with Ewout about the second

view this post on Zulip Chris Moesel (Feb 03 2021 at 20:37):

Oh... So when you said "the extension automatically applies to the recursive instances", you meant that the Extension definition context does not need to list out Questionnaire.item, Questionnaire.item.item, etc. If the StructureDefinition.context references an element that is the target of a contentReference, then it implies that it is valid for all referencing elements as well.

view this post on Zulip Grahame Grieve (Feb 03 2021 at 20:37):

yes

view this post on Zulip Chris Moesel (Feb 03 2021 at 20:39):

But if a profile on Questionnaire adds a required extension on Questionnaire.item, then it is required only for Questionnaire.item, and not Questionnaire.item.item. If you wanted to have the extension be required on all nested items, you'd need to do that via an invariant (if that's even possible to express)?

view this post on Zulip Grahame Grieve (Feb 03 2021 at 20:40):

no, you could refer back to your profile recursively by applying it to it's own children

view this post on Zulip Chris Moesel (Feb 03 2021 at 20:44):

Do you have an example of what that would look like? I see that you did reference the profile-element extension in the thread above, but it's still not clear to me how it is used in a use case like this.

view this post on Zulip Chris Moesel (Feb 03 2021 at 20:47):

Actually, I guess I already asked this in a more detailed way here. So all those questions still apply. ;-)

view this post on Zulip Grahame Grieve (Feb 03 2021 at 20:50):

well, here:

view this post on Zulip Grahame Grieve (Feb 03 2021 at 20:50):

<StructureDefinition xmlns="http://hl7.org/fhir">
  <url value="http://hl7.org/fhir/test/StructureDefinition/document-structure"/>
  <differential>
    <element>
      <path value="Composition.section"/>
      <min value="1"/>
      <type>
        <profile value="http://hl7.org/fhir/test/StructureDefinition/document-structure">
          <extension url="http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element">
            <valueString value="Composition.section"/>
          </extension>
        </profile>
      </type>
    </element>
 </differential>
</StructureDefinition>

view this post on Zulip Grahame Grieve (Feb 03 2021 at 20:51):

this says that there must be a composition section, and that each section must have a section, recursively.

view this post on Zulip Grahame Grieve (Feb 03 2021 at 20:51):

good luck trying to satisfy that ;-)

view this post on Zulip Chris Moesel (Feb 03 2021 at 21:05):

OK. So the extension goes on the element that is being referred to. So in the case of the Questionnaire example, that extension would go on Questionnaire.item's type[0].profile[0] element. Then when I process Questionnaire.item.item, when I see the contentReference to #Questionnaire.item, I first look at Questionnaire.item in the current profile. If it has that extension, I copy over all the constraints to Questionnaire.item.item. If it doesn't, I just use the element definition from the base Questionnaire resource. Is that right?

view this post on Zulip Grahame Grieve (Feb 03 2021 at 21:20):

So in the case of the Questionnaire example, that extension would go on Questionnaire.item's type[0].profile[0] element.

well, no - it would be a type[0].profile[0] on Questionnaire.item.extension

Then when I process Questionnaire.item.item, when I see the contentReference to #Questionnaire.item, I first look at Questionnaire.item in the current profile.

no. as you are processing, you have 2 levels of reference. contentReference points at the underlying base definition. The current profile has nothing to say about that. But when you want to know what profile is applied to the underlying content found in contentReference, you look at the stated profile

If it has that extension, I copy over all the constraints to Questionnaire.item.item. If it doesn't, I just use the element definition from the base Questionnaire resource. Is that right?

I think you're reading the element-profile extension backwards. it points forward - 'this profile applies' comes from type[0].profile[0]. the element-profile says 'when you apply this profile, start at the element' (since profiles must start at an resource, but you are already inside the resource (the element types must match)

view this post on Zulip Chris Moesel (Feb 03 2021 at 21:33):

OK. I am obviously confused. I'll see if someone else on my team can help me understand it better, because I think I am a little lost.

I am also confused because up until an hour ago, I thought that if a type has a profile, then the profile must be a constraint on the definition specified by type.code (e.g., if type.code is Quantity you can't specify type.profile to be Observation) -- but this approach seems to violate that. And if the extension changes the meaning of type.profile, then shouldn't it be a modifierExtension?

view this post on Zulip Grahame Grieve (Feb 03 2021 at 21:42):

I am also confused because up until an hour ago, I thought that if a type has a profile, then the profile must be a constraint on the definition specified by type.code (e.g., if type.code is Quantity you can't specify type.profile to be Observation) -- but this approach seems to violate that.

sort of. The profile must express a constraint on the type. In the absence of the element-profile extension, that will be assessed on the root element in the profile (which must match StructureDefinition.type). But with this extension present, then you base the decision on a different element. The type must still match.

And if the extension changes the meaning of type.profile, then shouldn't it be a modifierExtension?

It doesn't change the meaning; it changes how you use it.

view this post on Zulip Chris Moesel (Feb 03 2021 at 21:55):

OK. I guess it depends on how you interpret "If any profiles are specified, then the content must conform to at least one of them." But anyway... I'll try to wrap my head around the rest then. I want to be sure I get this right. Because I already have to delete a lot of code from when I got it wrong the last time. ;-)

One more question (which may be misguided based on my lack of understanding), but... when this extension is used to point to an element in a profile (e.g., Foo.x), would constraints on subpaths of Foo.x (e.g., Foo.x.y) in the profile also apply? Or would you also need to independently apply the extension to Foo.x.y?

view this post on Zulip Grahame Grieve (Feb 03 2021 at 21:59):

would constraints on subpaths apply

yes.

view this post on Zulip Chris Moesel (Feb 03 2021 at 23:20):

I think fleshing out another example might help. The following is a differential from the MyQuestionnaire profile. Is this the right differential array to attempt to say:

  • all recursive instances of item (e.g., item.item, item.item.item) should conform to Questionnaire.item from the MyQuestionnaire profile
  • and Questionnaire.item from MyQuestionnaire requires a questionnaire-itemControl extension
  • therefore all recursive item's also require a questionnaire-itemControl extension

Differential from MyQuestionnaire profile structure definition:

[
  {
    "id": "Questionnaire.item",
    "path": "Questionnaire.item",
    "type": [{
      "code": "BackboneElement",
      "profile": ["http://example.org/StructureDefinition/MyQuestionnaire"],
      "_profile": [{
        "extension": [{
          "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-profile-element",
          "valueString": "Questionnaire.item"
        }]
      }]
    }]
  },
  {
    "id": "Questionnaire.item.extension:item-ctl",
    "path": "Questionnaire.item.extension",
    "sliceName": "item-ctl",
    "min": 1,
    "max": "1",
    "type": [{
      "code": "Extension",
      "profile": ["http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl"]
    }]
  }
]

view this post on Zulip Grahame Grieve (Feb 05 2021 at 02:18):

yes that's what it says

view this post on Zulip Ewout Kramer (Mar 01 2021 at 10:55):

Thanks for the example, @Chris Moesel . Never thought we'd use this extension to solve this problem. Quite elegant.


Last updated: Apr 12 2022 at 19:14 UTC