Stream: fhirpath
Topic: iif()
Aaron Nash (Feb 05 2021 at 21:52):
Hey folks, I'm looking at the FHIRPath spec and cannot seem to reconcile what's written with the behavior I see in fhirpath.js.
The spec's definition is iif(criterion: expression, true-result: collection [, otherwise-result: collection]) : collection
When I use fhirpath.js I see Patient.address.first().text.iif(contains('Rainbow'), $this, {}))
return '534 Erewhon St PeasantVille, Rainbow, Vic 3999'
yet I would expect the result to be the Patient resource. Is the implementation wrong or I am misunderstanding something about the spec. I would expect true-result
to have an expression
type in order to see the behavior I'm seeing.
Lloyd McKenzie (Feb 05 2021 at 22:04):
$this is the 'current context node', which in this case is the first repetition of address.text. If you want to return the context resource, use %resource
Aaron Nash (Feb 05 2021 at 22:08):
That seems wrong to me. Take for example Patient.address.first().text.union($this)
that returns '534 Erewhon St PeasantVille, Rainbow, Vic 3999' | Patient
Both the first parameter of union
and the second parameter of iif
are of type collection
Why would they have different results?
Lloyd McKenzie (Feb 05 2021 at 22:10):
@Grahame Grieve @Paul Lynch @Bryn Rhodes
Paul Lynch (Feb 05 2021 at 22:51):
From http://hl7.org/fhirpath/#functions:
Paul Lynch (Feb 05 2021 at 22:51):
If the function takes an expression as a parameter, the function will evaluate the expression passed for the
parameter with respect to each of the items in the input collection. These expressions may refer to the special $this and $index elements, which represent the item from the input collection currently under evaluation, and its index in the collection, respectively.
The function iif takes an expression as its first parameter, so $this gets set each item in turn in the array to which iif is applied.
Aaron Nash (Feb 05 2021 at 23:09):
I would expect the spec to say "If the function takes an expression as a parameter, the function will evaluate the expression passed for every parameter with respect to each of the items in the input collection" or something along those lines if that applied to every parameter. Separately, if that's the behavior we expect shouldn't the signature be iif(criterion: expression, true-result: expression [, otherwise-result: expression]) : collection
and not iif(criterion: expression, true-result: collection [, otherwise-result: collection]) : collection
Paul Lynch (Feb 08 2021 at 13:07):
It doesn't say every parameter has to be an expression; it is enough that one of them is. That's how I read it, anyway. @Bryn Rhodes ?
Bryn Rhodes (Feb 08 2021 at 14:07):
Only the arguments indicated as expression
are iterated. It's not clear what the behavior would mean otherwise, cartesian product? In fact, it's not clear to me in any of these examples that the $this
reference is well defined? $this is only accessible within an iteration context.
ryan moehrke (Feb 08 2021 at 15:12):
is iif iterated though? I wouldn't expect to get multiple true/false results from an iif expression
Paul Lynch (Feb 08 2021 at 16:07):
@ryan moehrke Good point. Immediately above description for iif, it says, "The functions in this section operate on collections with a single item. If there is more than one item, the evaluation of the expression will end and signal an error to the calling environment." fhirpath.js throws an error for more than one argument.... but it still sets "iterates" over the one item and sets "$this", because it takes an expression.
(3).iif($this>1, 'a', 'b') => ['a']
Paul Lynch (Feb 08 2021 at 16:14):
@Bryn Rhodes fhirpath.js' implementation views that iteration context at the iif call, not just its argument, so that when $this is set, the same value is available to any of the arguments, not just the expression argument.
Bryn Rhodes (Feb 08 2021 at 16:45):
@Paul Lynch , that's a reasonable interpretation given it's a singleton function, I'm looking at what the CQL engine would do here and I think it's actually aligned with the JS engine there. @Grahame Grieve , does the FHIRPath engine support the true-result and otherwise-result arguments to iif as expressions with access to the iteration context?
Aaron Nash (Feb 08 2021 at 18:56):
To separate out the discussion regarding the appropriate use of $this from the original inquiry, you could replace $this in my original examples with first(). That is, Patient.address.first().text.iif(contains('Rainbow'), first(), {}))
and Patient.address.first().text.union(first())
Aaron Nash (Feb 08 2021 at 18:57):
Nonetheless, Bryn's last question is a good summary of what I'm asking.
ryan moehrke (Feb 08 2021 at 19:04):
Patient.where(address.first().text.contains('Rainbow'))
Patient.where(address.text.where(contains('Rainbow'))) if you want any address that contains Rainbow
iif(Patient.address.first().text.contains('Rainbow'), Patient, {})) if you really want to use iif
should all work for your usecase too (fhirpath has a lot of options)
Aaron Nash (Feb 08 2021 at 20:59):
Thanks, Ryan. To be clear, my issue isn't about "how do I do this particular thing" but, rather, what does the spec say should happen in this particular situation.
Last updated: Apr 12 2022 at 19:14 UTC