Slicing with Sushi
May 1, 2020 11 Comments
As readers will know, I’m a big fan of FHIR Shorthand as a way of making it much easier to create Implementation Guides and examples in conjunction with the IG publisher. In fact, I’m coming to the conclusion that all systems that expose FHIR API’s should have an IG that describes it (along with the CapabilityStatement resource of course) – and unless you have experience with creating StructureDefinitions directly, then FSH – and SUSHI – is the way to go.
Of course, creating IG’s is still not easy (it’s just easier) so I thought it would be a good idea to write about common patterns that you might want to express – and serve as a reminder for me when I forget!
So here’s the first one.
Suppose you are hosting a FHIR API that can create or update Patient resources and you want to say:
All patients must include their medical records number (MRN). Other identifiers are allowed, but the MRN must be present.
In order to do this, you’ll need to ‘slice’ the Patient.identifier element. Slicing is conceptually quite simple. It involves taking an element that can repeat – like the patient identifier – and create individual ‘slices’ each of which has specific characteristics – such as that it must be present. In this example you want to create a specific slice that describes the MRN, and make it required.
Slicing can become very complex, but fundamentally you define a ‘discriminator’ – which describes how to distinguish different slices from each other. There are a number ways of doing this, but a simple one is using the value of the slices as the discriminator.
So in our example, we’re slicing the Patient.identifier element, which uses the Identifier datatype. This datatype has 2 key sub-elements (there are others):
- The system, which is a url that defines the ‘namespace’ within which the value of the identifier is unique.
- The actual identifier value.
So when someone issues an MRN, the namespace that it is unique within is identified by the system url. Examples outside of healthcare include drivers licenses and passports – the issuer of those identifiers will have a system value so that a client knows what the identifier means.
How the system values are created and tracked is beyond the scope of this post – some are defined by the spec, but it’s possible for anyone to create one if needed.
So in our example, we want to set the discriminator to use the system sub element to distinguish between slices, and then specify that there must be one (and only one) identifier in a conformant resource that has that system.
Here’s how you do that in sushi.
Line 1 gives the profile name (which will also be the id of the StructureDefinition that is produced by SUSHI)
Line 2 indicates that the new profile is constraining the core Patient profile. (It’s quite common for profiles to constrain other profiles rather than core – often when there is a base national set of profiles – like US core or Australia base)
Lines 7 to 9 is where the discriminator is defined. We say that it’s the value of the system subelement that distinguishes between slices, and that it’s OK to have identifiers that are not explicitly defined in the profile (line 9).
In lines 12-13 we indicate that there is a single slice named NHI. There’s nothing to stop us having other named slices as well if we wanted to. For example, we might want to specify the specific set of identifiers that we allow – in which case there would be named slice for each one, and line 9 (slicing rules) would have the value ‘closed’.
And finally in line 15 we specify that the NHI slice must have the value ‘https://standards.digital.health.nz/id/nhi’. Note that it has the word ‘(exactly)’ on the end. We need this as we are specifying the exact uri that should be present in the instance.
Note that for this profile I included a couple of example Patient instances – one that should pass validation in the IG builder (or using the $validate operation if POSTed to a server) and another that should fail. This is a good way to ensure that you have defined it the way you meant to.
When sushi builds the instance, it adds the profile that it is claiming conformance to (see lines 20 and 29) in the resource meta.profile element as shown in the diagram below. This is how the validator knows to check that it actually is conformant.
The validator function within the IG Publisher QA report does extremely detailed validation. You’ll want to remove – or comment out – the failing example when you’re sure that everything is working as expected.
I hope you find that useful – I’ll add others as they come to mind. If there are other patterns you’d like to talk about then let me know in the comments and I’ll see what I can do.
Excellent post David – nicely explained. As close to ‘slicing made simple’ as I’ve seen anywhere!
Thanks Peter 🙂
so if you have a data set with over a thousand different observation resources, will it be ‘the system’ which holds open all the different name spaces? Which system? FHIR or the Aps exposing the FHIR API?
I’m sorry, I don’t understand the question. Can you expand?
that’s my problem too! You give an example of an MRI which is chosen for your ‘slicing’ example because ‘involves taking an element that can repeat – like the patient identifier – and create individual ‘slices’ each of which has specific characteristics – such as that it must be present. In this example you want to create a specific slice that describes the MRN, and make it required.’ Suggesting an Ophthalmological analogy, an IOP (intra-ocular pressure reading) which has particular values in individuals at a particular date and time, a kind of ‘slice’ I suppose, but we have over a thousand such data elements, is any instance a ‘slice’?
Don’t worry, I need to read more, you don’t need to reply.
Hi Mike – no worries. The slicing is for repeating elements within a single resource. In your example, each reading would be an individual Observation resource with a .code to identify it as IOP pressure, a date and a value[x] – likely valueQuantity which has the actual value and the units…
Thanks David, but surely you mean that each reading would be an individual instance of an intra ocular pressure observation resource, which I take it is an extension of the ‘observation resource’ concept (which is what nearly all of Ophthalmology becomes, as far as I can work out..). If every reading was its own resource we would have an unimaginable proliferation of resources which would defeat the whole purpose of having ‘classes’ and instances of classes in the first place..
MikeMair
Hi Mike. I suspect you’re mixing ‘instance’ with ‘type’. There is a single Observation type, of which there will be many instances. The .code value in each instance indicates what the observation is about – a Loinc code of 29467-3 would indicate that the observation was for a body weight, while 56844-4 would indicate that the observation was for IOP. In other words, there isn’t a specialised ‘class’ for each one – just a distinguishing code.
It is possible to create a ‘profile’ on the ‘type’ which allows you to say “for recording IOP, use this specific code – and there must also be a date, and a value which is in millimeters of mercury’. This could be what you’re thinking of as a ‘specialization’ perhaps? They say how to use the more generic observation type for a specific purpose.
And yes, there will be a lot of them!
Hi David,
Thanks for the very useful post. I have a query on slicing Observation.value[x] to allow either of valueCodableConcept or valueString, please guide me how can I achieve this using Sushi? I tried the followings:
* value[x] contains
valueCodeableConcept 0..1
valueString 0..1
* value[x] contains
valueCodeableConcept 0..1 or valueString 0..1
none of them worked.
Regards,
Shovan
i tried the followings, though I’m unsure if this is the right way to solve this
* value[x] 0..1
* value[x] ^slicing.discriminator.type = #type
* value[x] ^slicing.discriminator.path = “$this”
* value[x] ^slicing.rules = #open
* value[x] ^slicing.ordered = false // can be omitted, since false is the default
* value[x] ^slicing.description = “Slice based on the value[x] type”
* value[x] contains
valueCodeableConcept 0..1 and valueString 0..1
Thanks for the reference to this in our meeting today David. I’ve only heard about the FHIR Shorthand recently for some reason – but it totally accords with the way I work – little DSLs that can generate sometimes complex and ugly specs that you wouldn’t want to try and hand craft. I think of Slices just like good old fashioned Unions in C, but with better discriminators (and extensible/renamable)…. not as scary as some people imagine!
cheers,
…|<