Stream: questionnaire
Topic: Repeating answers to a top-level question
Josh Mandel (Sep 01 2021 at 16:21):
(Bonus points if someone can help point me to the relevant docs; I just spent ~20min trying to find this in guidance at http://build.fhir.org/questionnaire and http://build.fhir.org/questionnaireresponse.)
Josh Mandel (Sep 01 2021 at 16:30):
/poll Let's say I have a Questionnaire with a single question ("tell me the name of a color you like"), and it's marked as "repeats". Now, a user supplies answers indicating they like "Red", and "Green", and "Blue". Does the resulting QuestionnaireReponse...
Lloyd McKenzie (Sep 01 2021 at 17:02):
Looks like there isn't explicit guidance that says you can't do #2 - we never contemplated anyone would try. (The fact that QuestionnaireResponse.item.answer repeats I would have thought made the answer obvious.) Feel free to submit a tracker item.
Paul Lynch (Sep 01 2021 at 17:06):
I don't think that's obvious. In its internal (non-FHIR) format, for a string data type (i.e. not a list) that repeats, LHC-Forms would have three items for these three answers. LHC-Forms converts those on export to QuestionnaireResponse into a single item with multiple answers.
Josh Mandel (Sep 01 2021 at 18:11):
@Lloyd McKenzie are there circumstances where any of the following would not hold?
Top-level items with the same linkId
QuestionnaireResponse
.item.where(value.exists())
.linkId
.isDistinct()
I'm guessing this is always true.
Sibling answer.items with the same linkId
QuestionnaireResponse
.descendants()
.answer
.select(
item
.where(value.exists())
.linkId
.isDistinct())
.allTrue()
I'm guessing this is always true?
Sibling item.items with the same linkId
QuestionnaireResponse
.descendants()
.item
.select(
item
.where(value.exists())
.linkId
.isDistinct())
.allTrue()
I'm guessing this is always true.
(As you can see, I'm thinking about how to formalize this, and trying to test my understanding at the same time. I suspect there's some interaction with item.type=group that I'm not quite grokking. I'm using .where(value.exists())
as a kind of proxy for question-type items, since the item type is only modeled in the Questionnaire, not surfaced in the QuestionnaireResponse.)
Josh Mandel (Sep 01 2021 at 18:35):
The definition of "repeats" today says:
An indication, if true, that the item may occur multiple times in the response, collecting multiple answers for questions or multiple sets of answers for groups.
but if I'm understanding Lloyd's answer to the poll correctly, then "collecting multiple answer for questions" is not a use case where the item will occur multiple times in the response (rather, the item will occur once and there will be multiple answers within it). I've submitted FHIR-33335 to start, but I don't understand enough to know what exactly to propose.
Lloyd McKenzie (Sep 01 2021 at 19:38):
If your root item is a group and it repeats, then there will be multiple items with the same link id at the root.
Lloyd McKenzie (Sep 01 2021 at 19:38):
Same is true at the non-root level
Lloyd McKenzie (Sep 01 2021 at 19:39):
It's only sibling questions that must have distinct linkIds.
Josh Mandel (Sep 01 2021 at 19:55):
Cool -- so I think the fhirpath I wrote above is all roughly correct, and if so can be lumped together in a constraint like:
QuestionnaireResponse
.union(descendants())
.select(
item.where(answer.value.exists())
.linkId
.isDistinct())
.allTrue()
Josh Mandel (Sep 01 2021 at 20:02):
I've updated the issue to propose this in addition to language tweaks.
Paul Lynch (Sep 02 2021 at 00:01):
@Josh Mandel That doesn't look correct to me, for that case Lloyd mentioned of a repeating group. A QuestionnaireResponse that had two instances of a repeating group will repeat not just the group linkIDs, but the linkIDs of the answered questions within the group.
Paul Lynch (Sep 02 2021 at 00:02):
Example:
Paul Lynch (Sep 02 2021 at 00:02):
{
"linkId": "/54126-8/54137-5",
"text": "Diseases history panel",
"item": [
{
"linkId": "/54126-8/54137-5/54140-9",
"text": "History of diseases",
"answer": [
{
"valueCoding": {
"code": "3982",
"display": "Abdominal pain"
}
}
]
}
]
},
{
"linkId": "/54126-8/54137-5",
"text": "Diseases history panel",
"item": [
{
"linkId": "/54126-8/54137-5/54140-9",
"text": "History of diseases",
"answer": [
{
"valueCoding": {
"code": "2315",
"display": "Back pain"
}
}
]
}
]
}
Paul Lynch (Sep 02 2021 at 00:10):
(That example comes from https://lhcforms.nlm.nih.gov/sdc. After picking a form to view, you can choose "Show As"->"QuestionnaireResponse".)
Josh Mandel (Sep 02 2021 at 00:42):
I don't think the example you shared here would violate the constraint I proposed -- at least, I believe ".where(item.value.exists())" should effectively ignore any group-type items because they have no direct answers. Does this make sense, or am I still confused?
Paul Lynch (Sep 02 2021 at 00:45):
You called descendants(), which will pick up the children of the repeated group, namely the items with the same linkId that have answers.
Paul Lynch (Sep 02 2021 at 00:54):
But I think I misread your FHIRPath expression. I thought it was checking that all of the linkId for items with answers are distinct. Did you mean to put the isDistinct() inside the select(...)?
Josh Mandel (Sep 02 2021 at 00:58):
I am checking that the linked IDs for items that are sibling members of the same array and also have answers... are unique. I want this to be true for each items array, but not a cross them. In other words, for every item array "A" at any level of the hierarchy: look at just the items of A that have answers, and and sure their link IDs are unique within A.. That's why isDistinct() sits inside of the select.
Paul Lynch (Sep 02 2021 at 01:05):
Okay, that looks correct then. Sorry for the confusion. I should have tested it first.
Lloyd McKenzie (Sep 02 2021 at 03:11):
It's a hard expression to parse in your head :)
Josh Mandel (Sep 02 2021 at 13:18):
I'm very open to ideas for better ones. The main thrust is to capture what seems to be implicit knowledge of the rules in an explicit + computable fashion
Paul Lynch (Sep 02 2021 at 13:29):
@Josh Mandel Do you see any issue with replacing QuestionnaireResponse
.union(descendants()) with QuestionnaireResponse.union(repeat(item))? It avoids picking up a bunch of nodes that won't have items as children.
Josh Mandel (Sep 02 2021 at 13:34):
Won't that just find paths like .item.item.item and miss paths like .item.answer.item ?
Josh Mandel (Sep 02 2021 at 13:35):
There may be a variant of repeat(item | answer)
that works though.
Josh Mandel (Sep 02 2021 at 17:46):
(QuestionnaireResponse | repeat(answer | item))
.select(
item.where(answer.value.exists())
.linkId.isDistinct())
.allTrue()
This works, I think.
Paul Lynch (Sep 02 2021 at 18:14):
Yes, that looks good. Another issue with descendants() is would pick up resources under "contains"-- so this is better.
Brian Postlethwaite (Sep 03 2021 at 01:41):
Fhirpath ninjas!
Last updated: Apr 12 2022 at 19:14 UTC