Stream: conformance
Topic: GraphDefinition as a conformance resource
René Spronk (Sep 27 2019 at 14:20):
About two months ago I volunteered to speak about 'GraphDefinition' at the upcoming Amsterdam DevDays - volunteering to speak about a subject I know little of is a good way to motivate me to start looking at something in earnest. It turns out there are many open issues related to the exact use cases as well as the specification of the resource.
Question for this forum: if we regard GraphDefintion as a conformance resource (for it defines a constraint in the form of a specific graph of resource types), could/may one claim conformance to it in a resource (e.g. a "graph=canonical_url" meta tag) ? Think of a Composition resource which wishes to claim conformance to a particular document structure (graph).
Related question: can a message claim conformance to a MessageDefintion resource?
Lloyd McKenzie (Sep 27 2019 at 17:01):
We had talked about the notion of DomainResource.meta.profile being able to point to GraphDefinition, but I don't think we have any language that specifically allows that yet. (We'd need to think about the implications for backward compatibility too.)
Lloyd McKenzie (Sep 27 2019 at 17:02):
For messaging, obviously a more 'RESTful' way for messages to work is to drop 'event' and just point to the canonical URL of the MessageDefinition. However, that might freak people out a bit. It might be appropriate to have a choice? (eventCode or Reference(MessageDefinition))
René Spronk (Sep 27 2019 at 17:05):
And would we want GraphDefintion declared on the root-resource of a Graph, or on the bundle (if the graph is contained in the bundle, a graph need not have a root node) ? The current GraphDefinition resource is a rooted graph, there are pros/cons to that.
René Spronk (Sep 27 2019 at 17:08):
Re: messaging - well, the trigger could be "A01", but depending on the context the actual structure could differ on a system by system basis. Same trigger, different GraphDefinition. After all, next to the focal resources there's a lot of variability in message content.
Lloyd McKenzie (Sep 27 2019 at 17:28):
GraphDefinition, as designed, is rooted. So yes, it would appear on the root resource.
René Spronk (Sep 28 2019 at 07:22):
If GraphDefinition were not to have a root, then it could appear on any, some, or all resources of the instance-graph. I'm not saying I'd actually design it that way, just pointing it out as an observation.
René Spronk (Sep 28 2019 at 13:46):
See http://graphml.graphdrawing.org/primer/graphml-primer.html , the example in section 2.1 - I'd like to propose we restructure GraphDefinition in a similar way, with identified nodes (in FHIR, either UUIDs or '#' local references). The current GraphDefinition (which is tree-like) is problematic in that one can't define a relationship between a node and some other node previously defined in the definition. FWIW @Michel Rutten agrees that the current structure is problematic.
Grahame Grieve (Sep 28 2019 at 18:32):
what kind of re-use do you think we could get?
Lloyd McKenzie (Sep 28 2019 at 19:30):
I don't really understand the point of a rootless GraphDefinition, though I'm not opposed if you make a case for it. In terms of "identified nodes", that's easy enough to do, but what about situations where you have a Patient which is referenced by multiple Encounters and there's a need to ensure that certain content links to specific encounters.
René Spronk (Sep 29 2019 at 08:51):
Re: identified nodes: In the latter case you'd have a graph with multiple encounter nodes (explicit nodes), not just one generic node which could occur /be instantiated multiple times. This way you can define that one of the encounter nodes has to have an inbound reference from some other resources.
René Spronk (Sep 29 2019 at 08:58):
A rooted graph only makes sense if one has a rooted instance-graph, which may be true for documents or messages but won't be true for all fhir instance graphs.
Let's assume that GraphDef 'example' defines [a graph with a Patient resource, which must have a 1..1 inbound reference from a weight Observation resource, and the Patient resource shall have a 1..1 outbound reference to an Organization].
If I instantiate this and have 3 resources Pat, Obs and Org, then either one of those (or all of them) can claim conformance to 'example'. Effectively the resource would state "I'm a node in a instance-graph, and seen from here I'm compliant to a specific GraphDef", or "As a node in an instance graph I was created in compliance with a specific GraphDef".
Lloyd McKenzie (Sep 29 2019 at 15:01):
In the encounter scenario, the graph wouldn't identify how many encounters there should be, only the expected behavior for content nested within a particular encounter. So the graph couldn't assign a specific 'id' to each encounter.
In your example, I'm looking for a practical benefit. For FHIR IG validation, so long as one of the resources declared the graph, you'd get identical validation. (And declaring the graph 3 times would actually force triple the work - and a need to suppress duplicate errors/warnings.) So I'm not really seeing the benefit.
René Spronk (Sep 29 2019 at 15:44):
The only benefit is in situations were it would be difficult to identify a root, other than by randomly picking one of the resources in the graph and declare a profile meta tag on it. E.g. a graph of resource instances which conforms to a data set (or dare I say: a graph of logical model definitions). Such instance graphs don't have a root. Randomly picking one will work however.
René Spronk (Sep 29 2019 at 16:01):
In the encounter scenario, the graph wouldn't identify how many encounters there should be, only the expected behavior for content nested within a particular encounter. So the graph couldn't assign a specific 'id' to each encounter.
Why not? A node in a GraphDefintion defines what profiled resource type we're talking about, plus expresses expectations as to inbound and outbound references. Thus, if there are encounters which differ in their definition (wrt strucDef profiles, inbound/outbound references) from other encounters, they are represented by two different nodes in the graphDefinition, and hence have different ids.
In mother/child scenarios, with 2 encounters, one of them must have an outbound reference (the one about the child), and another must have an inbound reference (the one about the mother). If we want to have the encounter (for the child) to also have a 1..* inbound reference from a Observation [profiled, to be a Billirubin observation], the two encounters nodes have different definitions in the GraphDef. In order for the Billirubin Observation type [a node in the GraphDef] to be able to reference the 'correct' encounter type, we need an id on all nodes in the GraphDef (just defining 'this needs a reference to Encounter' won't cut it [this is what the current GraphDef does], because there are two encounter types/nodes in the GraphDef). Theoretically one could create a single encounter-instance that would comply with both the encounter types as defined in the GraphDef, so we'd need an additional rule [akin to the rules we have in the current GraphDef] to state that the encounter instances should be different, and that there should be one of node X and one for node Y.
The confusing thing in Graphdefs is that we're talking about a resource-type graph, which may have many resource-instance graphs as valid instances of it. That's certainly one of the key messages I'll try to convey during my presentation.
Lloyd McKenzie (Sep 29 2019 at 16:39):
The primary use-cases we have for GraphDefinition right now are documents, messages and queries (e.g. all diabetes data for a patient, data that should be included in the discharge summary for an encounter). All of those have a root. I'm not really understanding your 'data set' or 'logical model' examples - why would those not have a root?
I'm not arguing that you couldn't have specific individual identified nodes in a graph that you'd want to point to. I'm saying that there will be other cases where you might want to point to a non-identified node. E.g. A graph could have 0..* encounters. However, within the subgraph beneath those encounters, all of the resources would be required to point to the encounter they're a sub-graph of. If we can do that, then presumably we can handle the individual identified node situation too.
René Spronk (Sep 30 2019 at 07:54):
Agree that in the described sub-graph scenario the id would be superfluous, because it can be implicitly identified as well. But for other scenarios, you'd need an id, so in my mind we'd better ensure that all nodes have a unique id.
René Spronk (Sep 30 2019 at 07:58):
I do understand that the primary use cases happen to have a root - but the mother/child+encounters+observations model mentioned earlier on does not. Nor does a 'QA dataset based on lab observations for multiple patients'. One could have a rule that says that in a single-patient graph, that the patient shall be seen as the root, but that's very arbitrary, one could also pick the encounter, or any other resource to be the root. In my mind it doesn't matter that much, as long as the GraphDefintion defines the graph not as a a tree-like structure (which is what the current GraphDef does), but as a 'true graph'.
Lloyd McKenzie (Sep 30 2019 at 15:16):
We have the ability to put ids everywhere already. It's more a question of how to reference. If the graph definition nodes are x..1, then the id will refer to a single node in the instance. What I'd like to figure out is what referencing the id would mean if the referenced node was x..*.
In the mother-child case, you'd either have a graph for a pregnancy, where root is mother and graph includes all children, or you'd have graph for a single birth where root is a child and graph includes just the mother. I don't understand the use-case for rootless graph in that scenario. I don't understand the QA dataset example. Having a rootless graph increases complexity of definition and of referencing, so we should have a pressing use-case if we're going to go that way.
René Spronk (Sep 30 2019 at 16:56):
I don't want the id to be about resource-instances at all, but I'd like them to define a node in the type-definition-graph. if I have a node which is a "us-core-patient which shall have a 1..* inbound reference from a us-core-observation", it needs an id. Otherwise I'll never be able to distinguish it from a "us-core-patient which shall have a 1..* inbound reference from a us-core-encounter" node. If I'm at any other node somewhere in the graph you'd need to express "..and from this node, you'd have a 1..1 outbound reference to node [someId]". Currently nodes in Graphdef don't have an id. That's exactly what the problem is. The endpoint of an edge is currently defined by using a StucDef name - that's not sufficient, in my example that would be "us-core-patient". A node in a GraphDef is about more than just what's in a StrucDef.
René Spronk (Sep 30 2019 at 16:57):
To me the above issue needs to be resolved. Whether Graphs have a root or not doesn't matter that much, but the id issue needs to be solved.
Lloyd McKenzie (Sep 30 2019 at 19:01):
Nodes in Graphdef do have an id. Every node in every resource inherits 'id' from Element.
Lloyd McKenzie (Sep 30 2019 at 19:02):
(actually, there are a couple of limited exceptions. E.g. you can't have 'id' on Extension.url.
Michel Rutten (Oct 01 2019 at 08:47):
If I understand correctly, Rene is hinting at allowing GraphDefinition to first define nodes in the graph with "virtual" identifiers (separate from resource id), then define the relationships between the nodes by referring to the previously introduced identifiers. A single virtual node id could represent a group/category of instances.
Lloyd McKenzie (Oct 01 2019 at 13:57):
Right. But if a node represents a group/category but you want others below a particular member of that group to point to that specific member, we need to do more than just point back to the group.
René Spronk (Oct 01 2019 at 14:21):
It depends. This morning I came to the realisation that one can see GrahpDefinition as something that closely mimics the resulting instance-graph. Thus a GraphDef for a "Patient which SHALL have 3 inbound subject references from Observations, and which SHALL have 3 inbound references from Encounters" could be fully expanded to a GraphDef with 7 nodes. Or, as I had in mind originally, it could be a concise type graph, so there'd be only 3 nodes (there are only 3 types). Now what if we added a rule to state that "each Observation shall have a reference to a different Encounter resource" - that's easier to define in the 7 node (expanded) expression than in the 3 node (concise) expression. In the latter case we'd need some special rules around the instances, perhaps using some FHIRPath expression. The current GraphDef has a similar set of rules on instances, using compartments.
The expanded model however doesn't work if we have open ended cardinalities e.g. 1..*. As such we'll always have problems related to what i called the concise graph definitions, and we'll need some special stuff to make statements about instances.
Michel Rutten (Oct 01 2019 at 14:22):
Hmmm... Each virtual identifier would be associated with e.g. a single profile with a matching set of 0...* resources. But within a single graph, each virtual identifier would represent a single resource instance. Right?
René Spronk (Oct 01 2019 at 14:23):
If I understand id correctly, we could have an id (node id) on each node (lets say we have a defintional repeating structure as part of a GraphDef resource), in definitions of edges we can introduce a sourceNode (string) and destinationNode (string) data elements which refer to the nodes.
Lloyd McKenzie (Oct 01 2019 at 14:36):
If you know ahead exactly how many repetitions of each thing there will be, you could indeed express every node in the instance as a node in the graph. However if the number of nodes in the instance is variable, but certain relationships must remain within a given sub-graph, that's trickier.
Michel Rutten (Oct 01 2019 at 14:40):
As long as each instance can be matched by a unique profile, it should be possible. But if two separate instances (nodes) in the graph conform to the exact same profile (without any discerning attribute), then this approach would be ambiguous.
Lloyd McKenzie (Oct 01 2019 at 14:46):
I'd expect the instances to match the same profile
Lloyd McKenzie (Oct 01 2019 at 14:48):
Imagine a graph for an outbreak where there are multiple cases (0..n) and within each case, the data needs to be for the same exposure. All of the cases will have the same profile, but we want to tie the records beneath that case to the same instance. Different cases will have different instances.
Michel Rutten (Oct 01 2019 at 14:54):
Great example! I understand the challenges.
René Spronk (Oct 04 2019 at 09:32):
René Spronk (Oct 04 2019 at 09:33):
I'm not an expert on public health scenarios, but I think an analogy is represented in the above graph. n1 has multiple n2's associated with it. Now how does one ensure that references from n3,n4,n5 are all to the same instance of n2 (instead of potentially to other instances of n2?)
René Spronk (Oct 04 2019 at 09:35):
I see two potential ways to support this (perhaps you see others): define a subgraph for n2,n3,n4,n5 and make some instance-oriented constraint on that subgraph, or define a constraint on the edges, e.g. "e2, e3, and e4 SHALL all reference the same instance of n2".
Michel Rutten (Oct 04 2019 at 11:19):
Thinking about Lloyd's example scenario. We could describe constraints for such a graph by introducing multiple "virtual" node identifiers for the same profile. The edge list would describe how the different nodes are linked within the graph. For example, node list introduces patients P1 and P2, both conforming the MyPatientProfile
. Some edges would connect to P2, other edges to P2. The edge list describes the shape of the graph. The algorithm to verify conformance would need to be more advanced than a simple tree walk, i.e. memorize already visited nodes to handle cycles.
Michel Rutten (Oct 04 2019 at 11:20):
Not sure if scenario's exist that would validate the additional complexity and indirection, but theoretically I think it is possible.
René Spronk (Oct 04 2019 at 13:03):
If a structure allows for * patients (n2's in my graph), there's no knowing ahead of time how many patients will be instantiated, so taking care of it that way sounds problematic. IMHO a constraint on the edges sounds like a solution that's easy to understand and implement.
Michel Rutten (Oct 04 2019 at 13:24):
I don't think it would be problematic. The actual number of instances that fit a specific position in the graph is initially unbounded ( 0...*
) and can be constrained by the graph definition. Each node represents a set of 0...*
instances in a specific position in the graph, associated with a unique virtual identifier that is referenced by edge definitions. Different nodes can conform to the same profile, but connected to different edges. A validator would need to match actual instances to nodes (advanced, but algorithms exist).
Such an approach would allow defining constraints on any type of graph, not limited to trees. Again, not sure if the additional complexity is warranted, but it seems possible if required.
Lloyd McKenzie (Oct 04 2019 at 15:52):
Really what we'd want to do is define pseudo compartments on the fly
Michel Rutten (Oct 05 2019 at 10:00):
With pseudo compartments, do you mean something like a subgraph?
Jose Costa Teixeira (Oct 05 2019 at 12:11):
Not sure if related but feels so:
I've been looking for a way to define a set of data elements (across resources).
One use would be for access control: there are data sets that have a special category when put together - for example ZIP code is not PII, neither is age - but in the NL, a set of ZIp+age can potentially be PII.
Jose Costa Teixeira (Oct 05 2019 at 12:12):
I would like to have a way to express this in the definitional space: resource1.elementA and resource2.elementB have a special category (which then is used for access control engines). I do not want to replicate the access control description (@John Moehrke pointed to XACML) but a way to define this in FHIR. My idea would be to put this in a "Permission" resource.
Lloyd McKenzie (Oct 05 2019 at 15:28):
A pseudo-compartment would essentially define a compartment based on the resource at a particular level in a graph that could be applied to the subgraph beneath that point.
René Spronk (Oct 06 2019 at 06:45):
Are you assuming that subgraph to not have any edges which exit that subgraph? i.e. for it to be wholly self/internally referencing?
Lloyd McKenzie (Oct 06 2019 at 15:06):
For this scenario, yes. If the graph was fully enmeshed, then pseudo-compartments wouldn't make much sense.
René Spronk (Oct 07 2019 at 06:39):
Patient may have multiple encounters, and encounters are referenced by tons of clinical resources (let's call it graph C) associated with that encounter. But so is the patient (lots of references from C), and a Concern resource may have references from multiple encounters (C graph instances). So if we define a sub graph C, we'll have to accept it has external references. If it's not allowed to have any of those, then having subgraphs will only help in a small subclass of the problem space.
Constraining edges with 'this edge e1 shall refer to the same instance as edge 2' type constraints could solve the issue in general. But it would certainly be more verbose.
René Spronk (Oct 12 2019 at 13:25):
Another issue is the 'direction of the edges'. Observation has a reference to Patient. But in the GraphDef one needs to define that a #1 'Patient SHALL have 3 Observations'. (and not: the Observation shall have references to 3 Patients). We could have the requirement (#2) "Observation shall have a 1..1 reference to Patient". To me #1 is best taken care of in an edge directed from patient to Observation (the current GrapDef has a 'reverse' indicator and a FHIRPath based on the target node), and #2 in an edge directed from Observation to Patient.
Lloyd McKenzie (Oct 12 2019 at 15:14):
The need for reverse relationships is a known requirement, but I'm not sure there's a change request for it. Care to submit one?
René Spronk (Oct 15 2019 at 07:03):
Reverse relationships are already covered by the current GraphDef (I think). link.target.params "criteria for reverse lookup".
What Michel and I are advocating is an overhaul of GraphDef, not just adding some tweaks to it. Before a proposal can be made some aspects have to be discussed first, to ensure the new approach (defining a GraphDef as a collection of nodes and edges) will indeed work.
René Spronk (Oct 15 2019 at 07:08):
(At the Amsterdam DevDays I aim to present our [collective] thinking about this subject, in the full knowledge we'll need more discussions before a proposal can be made how a new GraphDef should look like).
René Spronk (Oct 16 2019 at 10:27):
René Spronk (Oct 16 2019 at 10:29):
See image for proposed new structure. The old GraphDef allowed for one single profile per node, this version allows for multiple profiles on a node. This obviously complicates matters significantly. Question is whether we need to support multiple profiles. IMHO if a node conforms to two profiles, where one is not a specialization of the other, then we'd need to support multiple profiles on a node.
Kevin Mayfield (Nov 11 2019 at 17:04):
I’m not far off that with my view. (Thanks @René Spronk )
Kevin Mayfield (Nov 11 2019 at 17:28):
I know GraphML has node and edge separate but is their a reason why?
link.sourceId and link.targetNodeEquals[0..*] seems to compatible with graphML (without link.targetId). Also node and link may have a 1..1 relationship.
René Spronk (Nov 11 2019 at 18:43):
? nodes may have heaps of edges, sometimes an edge corresponding to a slice in a strucdef. You may be tempted to use a structure like n * ( node, all-edges-which-have-node-as-source ). That's mappable to graphML, the source of an edge need however not be the source of a reference, so the above grouping/ tree like structure will then seem rather arbitrary to some. Needs more discussion anyway.. my approach was to follow a cross-industry standard like GraphML.
Kevin Mayfield (Nov 11 2019 at 19:53):
ok. I need to use STU3 but it I think I can show the gist of your change.
Kevin Mayfield (Nov 11 2019 at 21:16):
Is this correct. A node will refer to resource and a link will refer to a reference.
René Spronk (Nov 12 2019 at 07:09):
basically, yes. The only problem being that an edge is a "requirement (as defined by the source of the edge) when it comes to a reference OR a reverse reference". Let's say a Pat resource is required to have 3 Obs resources. The reference is from Obs to Pat, but the requirement (the edge) as defined from Pat to Obs has (sourceNode=Pat, targetNode=Obs, link.min=3 and link.max=3 link.reversePath=true link.path=patient). There is also an edge from Obs to Path to express that an Obs shall have exactly one Pat subject (sourceNode=Obs, targetNode=Pat, link.min=1 and link.max=1 link.reversePath=false link.path=patient). The latter requirement is probably also expressed either in the core models or in a profile on Obs (node.profile).
Kevin Mayfield (Nov 12 2019 at 08:40):
Ta. I’m wondering if reverse edges are needed, Obs[3..3] to node patient[1..1] via path=node.
Is clear, the list of nodes reflect bundle entries and relationships in the bundle.
(I believe I can generate graphML (and so a graph), tabular version of graphdefinition and a Skelton bundle. Which is helping a developer several ways)
René Spronk (Nov 12 2019 at 08:57):
Dunno, if we throw slicename into the mix (which may be different depending on the direction of the requirement) it gets hairy pretty soon. I'm not saying collapsing a ref and a reverse-ref into one structure won't work, but it's an optimalization which can perhaps be added later. Right now I'm waiting on feedback from this proposal as it will be presented at DevDays, let's take it from there..
Eric Haas (Nov 12 2019 at 17:26):
I addition to cardinality I am wondering if we need to provide conformance expectations for each node or edge. namely to say RE in v2 speak. Right now have 1..1 = R, 0..1 = O or ?. 0..1 + MustSupport flag (or extension) would seem like a logical choice to me. there is already a rule element for compartment but I frankly don't understand the compartment bit and ignore it.
René Spronk (Nov 13 2019 at 07:02):
We probably don't want to replicate anything which is already there in StrucDef (for nodes). But I could imagine a mustSupport type of flag on the edges. As for the compartment bit: this allows one to ensure that e.g. there's just one single patient resource instance in a graph. In my proposed structure we deal with that challenge in a different way - a more flexible way, but also less elegant way. Using compartments doesn't scale, it only solves a small subset of the problem space.
René Spronk (Nov 13 2019 at 07:13):
Kevin Mayfield (Nov 13 2019 at 08:53):
Wondering if my requirements are slightly different. What I'm planning to suggest to HL7 UK is to make use of GraphDefinition to:
-
Produce a tabular list which is similar to HL7v2 (on purpose). So an overview (gist) of what is in the response
pasted image. This is a list of the Nodes. -
Produce a diagram which shows relationships. Like this but probably a graph.
Starting at the key resource and going out (so for the ADT^A04 this would be the PV1/Encounter) [This is sounding like a MIM!]
- It will be used to validate the contents of a Bundle. Especially that resources conform to the profiles stated in the GraphDefinition .
Wondering if the GraphDefinition edges could have multiple profiles. e.g. for vital signs Observations, it has to follow the HL7 profile (loinc code mandatory) and another that states a SNOMED code is mandatory. The supplied resource would be validated against all the profiles stated. [Might be a bit of an overkill but it's clearer to a developer as to what the requirement is]
Kevin Mayfield (Nov 13 2019 at 09:03):
- Possibly bundle autogeneration. In the diagram for 2 above, we have a List to active allergyIntolerance's (clinicalStatus=active). To document that rule it should be a derived profile but from a autogeneration pov the params clinicalStatus=active would work also (to restrict the resources referenced and returned in the bundle)
Lloyd McKenzie (Nov 13 2019 at 12:47):
I don't think you can create a v2-like diagram of a FHIR message because FHIR intrinsically allows you to link to the same resource from many places and there's no intrinsic ordering to the list of resources. While you could theoretically constrain out those things, it'd be very bad practice to do so. (You're not supposed to impose order constraints where order has no meaning and prohibiting re-use is going to force exposing the same instances with different URLs which would be confusing to most systems.)
One of the challenges is you can't necessarily know what profiles a given instance will be validated against. If practitioners get validated against profile p1 when referenced as an encounter admitter, p2 when validated against an encounter dischargers and p3 when validated against an order author, you may have some messages where the same practitioner is pointed to by all 3 places and must be valid against all 3 profiles. In other messages, there'll be distinct practitioners for each.
Kevin Mayfield (Nov 13 2019 at 14:33):
It would be exact but flat GraphDefinition seems possible (and supports graphML conversion).
Green boxes are resource (nodes) and links (edges) green text.
pasted image
Kevin Mayfield (Nov 13 2019 at 14:37):
Re validation, as a minimum, I like the idea of the GraphDefinition (nodes) setting which profile to use for resource validation.
Eric Haas (Nov 13 2019 at 23:22):
Ok So this (hand-rendered table) of simple graph listing the resource and path, kind of like (A--> B) , (B-->C).
http://build.fhir.org/ig/HL7/davinci-alerts/branches/master/GraphDefinition-admit-discharge.html
This is what I think of for graphdefinition and I think is more in line with what Rene thought process is.... ( It may not scale to more complex graphs - I haven't thought it through.) but that second part ... graph generation would be great except when it gets too busy.
Lloyd McKenzie (Nov 14 2019 at 01:06):
Except that in a GraphDefinition, it's not a source profile or target profile, it's a source node and a target node. The same profiles might appear on multiple nodes, but there could be different constraints on outbound and inbound relationships for different nodes
Eric Haas (Nov 14 2019 at 01:45):
Fair point, but right now there is no way to uniquely id the node name so all you have is the source Resource. the path and the target...
Eric Haas (Nov 14 2019 at 01:47):
think of my link ( row #) as the node edge node id :-)
Kevin Mayfield (Nov 14 2019 at 06:06):
That's how I'm seeing it, I have link Id equating to a nodeId.
Kevin Mayfield (Nov 14 2019 at 06:13):
@Lloyd McKenzie do you have an example where a node isnt a profile? (Would this be inside a profile? Maybe in a composition)
Kevin Mayfield (Nov 14 2019 at 06:33):
So composition.section could be a sub node and from a diagram perspective this makes sense.
Top level nodes must have a profile, sub nodes shouldn't?
For cognitive reasons, the top level nodes should resemble bundle.entries/response.
René Spronk (Nov 14 2019 at 07:13):
One of the reasons for the newly proposed GraphDef is a requirement to have nodeIds. The current R4 GraphDef uses the 'profile name' as a unique identifier for a node, but given there can be multiple nodes that claim conformance to the same profile that won't work other than in some simple use cases.
@Kevin Mayfield as for profiling sub-parts (e.g. a section) of a resource: that's a much wider ranging/separate methodology issue. StrucDefs are currently associated with the resource level.
Kevin Mayfield (Nov 14 2019 at 09:20):
I'm not seeing a need (at present) for subParts.
I've started a GraphDefinition (STU3) for a FHIR Document https://github.com/nhsconnect/careconnect-cli/blob/master/src/main/resources/FHIRResources/reference/eDischage-GraphDefinition.xml
I am using different profiles in the nodes(root link) from the edges (other links), this is for experimental reasons.
Kevin Mayfield (Nov 14 2019 at 09:21):
I'm using sourceNodeId and targetNodeId to do the linking (not profiles)
René Spronk (Nov 14 2019 at 11:38):
So basically you only have links, where path indicates the 'from', and target the type-of-destination. I don't see any id's in your example. If multiple links have the same node as theird efintion, with the same structureDefinitions applied to it, then how would you accomplish this? Your solution will probably work for composition, which is a tree-like structure and not a fully interconnected graph.
Lloyd McKenzie (Nov 14 2019 at 12:26):
If you're using GraphDefinition and don't need to constrain anything about a given node other than inbound and outbound relationships, there's no need to have a profile on that node.
Kevin Mayfield (Nov 14 2019 at 14:44):
@Lloyd McKenzie yes but I may want to.
@René Spronk Maybe I'm actually building a BundleMessage profile on GraphDefinition
Eric Haas (Nov 14 2019 at 15:17):
If you're using GraphDefinition and don't need to constrain anything about a given node other than inbound and outbound relationships, there's no need to have a profile on that node.
@Lloyd McKenzie can you explain what you mean? Why is the profile element present in the resource if like @Kevin Mayfield pointed out , you can't use it to define a bundle profile? That's my use case too...
Lloyd McKenzie (Nov 14 2019 at 15:36):
I'm not saying that you can't use a profile, just that when using GraphDefinition you won't necessarily assert a profile for every node.
René Spronk (Nov 15 2019 at 14:14):
In the R4 GraphDefinition resource, one can (loosely reworded) define that an instance-constraint applies, some "edge/reference" references have to point to the same/different resource instance. 4 options were listed in R4 to state the relationship between the instances referenced by two different references, they can be:
- Identical: have the same literal reference
- Matching: be about the ‘same thing’, but the reference may be different
- Different: be different
- Custom: comply with a FHIRPath expression
My current draft is based (only) on 1. What were the use cases for 2,3,4? This to see whether or not those uses casesare covered by the current draft..
Lloyd McKenzie (Nov 15 2019 at 14:37):
Different might be if you're doing a merge or dealing with a mother-newborn situation
Kevin Mayfield (Nov 16 2019 at 14:00):
It's not possible to have a link internal to a resource is it?
In the graph below I think it would be useful to link from Composition (node) to Composition.section (subNode) and then to destination node.
Screenshot-2019-11-16-at-13.54.09.png
Kevin Mayfield (Nov 16 2019 at 14:03):
NodeId's are very useful (they are the #'s in the image). I'm using them as shortened description of the links (I may revisit this, probably need a name element for this)
Screenshot-2019-11-16-at-14.02.14.png
René Spronk (Nov 16 2019 at 14:05):
Y. we currently have node Ids in our draft.
René Spronk (Nov 16 2019 at 14:09):
as for your Composition problem: one can define a Composition node which has a StrutDef defined for it which only constraints the non-section-parts of a composition, and another Composition node which e.g. has a strucDef which only covers the AdmissionDetails section. Currently we have to have a reference between the two, so even though GraphDef is a 'type graph' we can't really specify that these two Compsition nodes need to be implemented as one single Composition resource.
As long as FHIR deals with strucdefs solely at the resource level I don't think we should even attempt to create a 'workaround' in GraphDef.
René Spronk (Nov 16 2019 at 14:16):
@Kevin Mayfield and thanks for the graph screenshot, I've added it to my DevDays presentation for next week ;-)
Kevin Mayfield (Nov 16 2019 at 21:05):
May have it online this week. Screenshot-2019-11-16-at-21.03.59.png
Kevin Mayfield (Nov 18 2019 at 15:00):
Work in progress but examples:
FHIR Document
https://project-wildfyre.github.io/conformance/graph/1
FHIR Message
https://project-wildfyre.github.io/conformance/graph/2
Operation
https://project-wildfyre.github.io/conformance/graph/3
Eric Haas (Nov 18 2019 at 23:43):
is the code to generate those open source?
Kevin Mayfield (Nov 19 2019 at 05:39):
Yes. It's here https://github.com/project-wildfyre/conformance/tree/master/src/app/components/graph-definition-detail
René Spronk (Nov 21 2019 at 11:04):
Friday at the DevDays in Amsterdam, there'll be a tutorial about GraphDefinition at 11:05 in The Richard.
René Spronk (Jan 15 2020 at 15:07):
https://jira.hl7.org/browse/FHIR-25474 - proposal to modify GraphDefinition based on presentations/discussions in Amsterdam
Grahame Grieve (Feb 04 2020 at 01:08):
@René Spronk we made comments on https://jira.hl7.org/browse/FHIR-25474 that need discussion here:
- Don't understand minInstances and maxInstances
- don't understand must-support
- is profile[] ordered? why 1..*?
- need to more graph in MessageDefinition to focus node, not message
- how does equalEdgeTarget etc deal with condition/required and other compartment rules
- need to support difference between links that are not included and links that are prohibited in a bundled context even if they are defined?
- explore what equaledgeTarget and equaledgeSource actually do
René Spronk (Feb 04 2020 at 10:30):
@Grahame Grieve
We use a graph of type definitions (aka nodes) to describe a graph of instances (aka resources).
node.minInstances/node.maxInstances : the resulting instance graph SHALL have between minInstances and maxInstances instantiations of this node (thus allowing one to state that there must be 2 different patient resources, or 2 different encounter resources, or that there shall be a max of 1 patient resource).
node.must-support : StrucDefs allow for must-support on OUTBOUND references. If you want to state that a "Patient resource shall have 3 INBOUND references from Observation resources" then node.mustSupport allows you to flag that inbound reference. StrucDoc doesn't allow you to do this.
node.profile is 1..* - * becasue it could comply with multiple profiles. 1 because we need at least a base/core profile (resource type, even if that's as basic as the Resource resource type to represent ANY). Node is a type definition, so we'd need at least one profile.
edge.equalTarget - edge is a type definition of a reference. If A has a ref (ref1) to B, and C has a ref (ref2) to B, how does one state that 'both instances of B' have to be one and the same instance, if node.minInstances is > 1 ? ref1 has the same 'target' as ref2. "ref1.equalTarget = ref2"
need to more graph in MessageDefinition to focus node, not message
Could you elaborate?
How does this fit with compartments? No idea - I've never understood their use (and I've tried) beyond certain academic access control scenarios. My starting point for the proposal were discussions around common use cases, not on copying all of the functionalities of compartments.If all of the above can be accomplished with compartments then by all means, bring it on. But also make sure it'll work for any compartments, also compartments based on logical models, e.g. an expression of CDA in FHIR, or Dutch ZIBs in FHIR.
Grahame Grieve (Feb 04 2020 at 12:33):
-
we could really see why mininstance/maxinstances for the whole set - as opposed to on the edges - made sense. where's a use case where you'd use it?
-
must-support - I think you mean, must-exist then? But isn't that min/max on the edge? if not, how is it different
-
do you need the base profile if you have another profile do you?
-
I guess that explained equalTarget, but it's still not clear what equal source has to be - isn't the source the source?
-
the message definition can have multiple focus nodes. each focus has it's own graph, so graph needs to be on focus rather than the definition as a whole
-
compartments don't do any of the stuff you did, they do something else. It's fundamental: I want to retrieve all the stuff, but I want to make sure it all references the same patient or encounter and doesn't cross into others - and if it does, I want you to ignore the links or raise an error. We don't have conpartments in logical models
René Spronk (Feb 04 2020 at 13:50):
- mininstance/maxinstances: e.g. for wishing to make sure there's only one single encounter resource, or patient resource. Probably similar to what what you'd do using compartments. But compartments don't allow you to specify that there shall be 2 patients (e.g. mother/child observations), right?
- must-support, as in https://build.fhir.org/profiling.html#mustsupport , i.e. not a cardinality, but a conformace type statement on inbound references, given that one cant declate must support for inbound references anywhere else
- no need for base if one has a derived profile.
- equalSource: sometimes you'd like to make a statement about the source of an edge (edge = reference, or reverse reference) being the same instance as the source of some other edge. Just being able to make statements about equalTargets won't be sufficient.
- agree that each focus in a message would be its own graph, although I'd actually like to have GraphDefs without a rootnode anyway. Don't need a root when one has a graph. All focus references could be to one and the same graph. But as long as we have a root node, we'd need multiple (overlapping/overlaying) GrahpDefs to describe a message (which is juck, both conceptually as from an implementation perspective). I tried to stay away from having to define subgraphs and joining graphs, but as long as messages are allowed to have multiple different focus types, and GraphDefs have a rootnode, we're stuck.
Eric Haas (Feb 05 2020 at 03:36):
@René Spronk are you saying don't need a root node defined so Root Node would 0..1 and just follow the graph from any point in it?
Grahame Grieve (Feb 05 2020 at 03:38):
the current compartment thing is exercised at the edge so it doesn't address the total
Grahame Grieve (Feb 05 2020 at 03:38):
I think of it as a poor proxy for what the compartment thing is trying to do
Grahame Grieve (Feb 05 2020 at 03:40):
the point about must-support is interesting but I think that this is the wrong place for a system to declare must-support
Eric Haas (Feb 05 2020 at 03:40):
I look at must support as way to say the edge isn's optional as 0..1
may suggest to an implementer, but is a requirement if the target node exists in their system.
René Spronk (Feb 05 2020 at 07:34):
- If we don't have a rootNode on a Graph then the validator will have to be smart enough to somehow match the type-graph to the instance-graph (which need not have a rootNode either). So it's (a lot harder) for a validator.
- regarding messsage: if we have a Graph starting at MessageHeader, there'd be no need for subGraphs (for multiple focus resources). Node currently references a cononical(StrucDef), but we could extend that to be canonical (GraphDef) so at least we'd be able to define some node to be equal to a rootNode in another Graph.
- Compartments: as I understand the situation Compartments are trying to do a different thing then what I'm trying to do in my proposal, with some area of overlap. Question is then: can/should compartments be enhanced to support of of the functionality (is that within scope for compartments), or are we talking about something similar yet different? And yes, I'd like any solution also to work with logical models.
Mohammad Afaq Khan (Mar 31 2020 at 01:56):
I saw the GraphDefinition XML profile. The attributes do make an edge connection between nodes, but how will you run traversals and operations without a stack or array object to encapsulate the traversals? I mean I guess if the XML/JSON is created in order of the graph's traversal you can parse portions of it from top to bottom, which leads to the question how will the traversals take place in the XML? @Kevin Mayfield @René Spronk.
René Spronk (Mar 31 2020 at 06:29):
Graphdef R4 ? Or the proposed Graphdef which is based on the GraphML standard ?
Mohammad Afaq Khan (Mar 31 2020 at 13:04):
@René Spronk I am not sure I was looking at this url: https://github.com/nhsconnect/careconnect-cli/blob/master/src/main/resources/FHIRResources/reference/eDischage-GraphDefinition.xml. It seems like the edges are the mappings of attributes on a FHIR object, but in a graph where lets say we have several paths that lead to the desired node how would the Node and Link structure in XML decide the most efficient way to get to the desired result? Kind of like a Djikstra or something of that sort. This might not be applicable here but just a general questions :slight_smile: ?
Keith Boone (Jun 17 2020 at 17:53):
There might not be choice in certain situations about conforming to orthogonal profiles, e.g., jurisdictionally required profiles (such as US Core) for a resource, where an IG might impose other, non-jurisdictionally required constraints. On the other hand, it's easy enough to create profile C which has all the requirements of profile A and profile B. Having multiple profile support in GraphDefinition isn't an absolute requirement to support multiple profile conformance. Honestly, I think it depends on tooling support. If there's appropriate tooling to combine constraints from multiple profiles and apply them at once, this is technically feasible. Existing tools (e.g., validation) can be used to test for multiple profiles. So I don't see adding multiple profiles to graph definition as being that much more complexity, and I do see cases where it might be required to ensure conformance to jurisdictional constraints in addition to IG constraints (to localize an IG to a jurisdiction).
Last updated: Apr 12 2022 at 19:14 UTC