Creating JSON-LD documents from within Mumps

In yesterday’s posting, I explained how the _getDocument() and _setDocument() methods work within EWD.js, mapping between JavaScript objects and corresponding Mumps Globals.

In this posting, I want to focus on something that is gaining interest within the VistA community: representing the complex network of data that is held for each patient within FileMan as a JSON-LD graph.  JSON-LD (JSON for Linking Data) is a W3C initiative, and provides a JSON-based alternative to the XML-based RDF standard for representing Semantic Web data.

Conor Dowling has recently published an article on his latest work with FMQL which aims to generate its output in terms of JSON-LD, so it’s perhaps timely to describe how EWD.js can be used to make the creation of the JSON-LD-formatted data a very simple task.  I’m not going to examine or discuss how the data that constitutes a VistA-derived JSON-LD document is gathered, formatted and referenced: that’s the job of FMQL.  What I’m going to focus on is how, once you have that data available within the VistA Mumps environment, you can represent it within a Mumps Global so that it automatically translates into a JSON-LD document within JavaScript.

Let’s start with an example of a JSON-LD document – I’m going to use one of Conor’s own examples of a patient’s Vitals.  For the purposes of clarity, I’ve stripped down the example somewhat, to the extent that it might not be entirely valid JSON-LD in terms of its internal cross-referencing.  However, I’m wanting to focus here on the mechanics of what’s involved rather than the semantic structure and syntax of JSON-LD.

Here’s the example JSON-LD document that we want to ultimately create:

{
  "@context": {
    "@base": "http://livevista.caregraf.info/",
    "@vocab": "http://datasets.caregraf.org/vs/",
    "date_time_vitals_entered-120_5": {
      "@type": "xsd:dateTime"
    },
    "date_time_vitals_taken-120_5": {
      "@type": "xsd:dateTime"
    },
    "entered_by-120_5": {
      "@type": "@id"
    },
    "fmso": "http://datasets.caregraf.org/fmso/",
    "generatedAt": {
      "@id": "prov:generatedAtTime",
      "@type": "xsd#date"
    },
    "hospital_location-120_5": {
      "@type": "@id"
    },
    "id": "@id",
    "label": {
      "@id": "rdfs:label"
    },
    "list": "@list",
    "owl": "http://www.w3.org/2002/07/owl#",
    "patient-120_5": {
      "@type": "@id"
    },
    "prov": "http://www.w3.org/ns/prov#",
    "qualifier-120_5": {
      "@container": "@list",
      "@type": "@id"
    },
    "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
    "sameAs": {
      "@id": "owl:sameAs",
      "@type": "@id"
    },
    "skos": "http://www.w3.org/2004/02/skos/core#",
    "type": "@type",
    "va": "http://schemes.caregraf.info/va/",
    "value": "@value",
    "vital_type-120_5": {
      "@type": "@id"
    },
    "vs": "http://datasets.caregraf.org/vs/",
    "xsd": "http://www.w3.org/2001/XMLSchema#"
  },
  "@graph": [
    {
      "date_time_vitals_entered-120_5": "2008-04-01T07:12:22Z",
      "date_time_vitals_taken-120_5": "2008-04-01T07:12:06Z",
      "entered_by-120_5": "200-64",
      "hospital_location-120_5": "44-23",
      "id": "120_5-284",
      "label": "2008-04-01T07:12:06Z",
      "patient-120_5": "2-9",
      "qualifier-120_5": [
        "120_52-6"
      ],
      "rate-120_5": 101,
      "type": "vs:120_5",
      "vital_type-120_5": "120_51-2"
    },
    {
      "date_time_vitals_entered-120_5": "2008-04-01T07:12:22Z",
      "date_time_vitals_taken-120_5": "2008-04-01T07:12:06Z",
      "entered_by-120_5": "200-64",
      "hospital_location-120_5": "44-23",
      "id": "120_5-285",
      "label": "2008-04-01T07:12:06Z",
      "patient-120_5": "2-9",
      "qualifier-120_5": [
        "120_52-59",
        "120_52-50"
      ],
      "rate-120_5": 98,
      "type": "vs:120_5",
      "vital_type-120_5": "120_51-5"
    },
    {
      "id": "120_52-64",
      "label": "CUFF",
      "type": "vs:120_52"
    },
    {
      "id": "120_52-66",
      "label": "ADULT",
      "type": "vs:120_52"
    },
    {
      "id": "120_52-59",
      "label": "PALPATED",
      "sameAs": "va:4688669",
      "type": "vs:120_52"
    },
    {
      "id": "vs:120_51",
      "label": "GMRV VITAL TYPE",
      "type": "fmso:File"
    },
    {
      "id": "vs:2",
      "label": "PATIENT",
      "type": "fmso:File"
    },
    {
      "id": "va:4688703",
      "label": "GMRV VITAL QUALIFIER-SITTING",
      "type": "skos:Concept"
    }
  ],
  "generatedAt": "2013-11-20",
  "id": "fmql__T120_5__F.02=2-9&.01>2008-04-01__O0__C10"
}

In the previous article I explained the bi-directional mapping between Globals and JSON.  So you’ve hopefully realised that all we need to do is to create a Mumps Global that is correctly structured in terms of its subscripts and content, and the _getDocument() method will automatically convert it into the JSON-LD structure shown above.  With that in mind, let’s figure out what that Global needs to look like.  We just apply the rules that I described in the previous article.  Alternatively (and arguably somewhat more easily), we can take the JSON-LD  document and push it through the _setDocument() method and inspect the Global that is created.  To save you the bother of doing this yourself, here’s what that Global looks like (you can choose any name for the Global, but I’ve chosen to use ^jsonld):

^jsonld("@context","@base")="http://livevista.caregraf.info/"
^jsonld("@context","@vocab")="http://datasets.caregraf.org/vs/"
^jsonld("@context","date_time_vitals_entered-120_5","@type")="xsd:dateTime"
^jsonld("@context","date_time_vitals_taken-120_5","@type")="xsd:dateTime"
^jsonld("@context","entered_by-120_5","@type")="@id"
^jsonld("@context","fmso")="http://datasets.caregraf.org/fmso/"
^jsonld("@context","generatedAt","@id")="prov:generatedAtTime"
^jsonld("@context","generatedAt","@type")="xsd#date"
^jsonld("@context","hospital_location-120_5","@type")="@id"
^jsonld("@context","id")="@id"
^jsonld("@context","label","@id")="rdfs:label"
^jsonld("@context","list")="@list"
^jsonld("@context","owl")="http://www.w3.org/2002/07/owl#"
^jsonld("@context","patient-120_5","@type")="@id"
^jsonld("@context","prov")="http://www.w3.org/ns/prov#"
^jsonld("@context","qualifier-120_5","@container")="@list"
^jsonld("@context","qualifier-120_5","@type")="@id"
^jsonld("@context","rdfs")="http://www.w3.org/2000/01/rdf-schema#"
^jsonld("@context","sameAs","@id")="owl:sameAs"
^jsonld("@context","sameAs","@type")="@id"
^jsonld("@context","skos")="http://www.w3.org/2004/02/skos/core#"
^jsonld("@context","type")="@type"
^jsonld("@context","va")="http://schemes.caregraf.info/va/"
^jsonld("@context","value")="@value"
^jsonld("@context","vital_type-120_5","@type")="@id"
^jsonld("@context","vs")="http://datasets.caregraf.org/vs/"
^jsonld("@context","xsd")="http://www.w3.org/2001/XMLSchema#"
^jsonld("@graph",0,"date_time_vitals_entered-120_5")="2008-04-01T07:12:22Z"
^jsonld("@graph",0,"date_time_vitals_taken-120_5")="2008-04-01T07:12:06Z"
^jsonld("@graph",0,"entered_by-120_5")="200-64"
^jsonld("@graph",0,"hospital_location-120_5")="44-23"
^jsonld("@graph",0,"id")="120_5-284"
^jsonld("@graph",0,"label")="2008-04-01T07:12:06Z"
^jsonld("@graph",0,"patient-120_5")="2-9"
^jsonld("@graph",0,"qualifier-120_5",0)="120_52-6"
^jsonld("@graph",0,"rate-120_5")=101
^jsonld("@graph",0,"type")="vs:120_5"
^jsonld("@graph",0,"vital_type-120_5")="120_51-2"
^jsonld("@graph",1,"date_time_vitals_entered-120_5")="2008-04-01T07:12:22Z"
^jsonld("@graph",1,"date_time_vitals_taken-120_5")="2008-04-01T07:12:06Z"
^jsonld("@graph",1,"entered_by-120_5")="200-64"
^jsonld("@graph",1,"hospital_location-120_5")="44-23"
^jsonld("@graph",1,"id")="120_5-285"
^jsonld("@graph",1,"label")="2008-04-01T07:12:06Z"
^jsonld("@graph",1,"patient-120_5")="2-9"
^jsonld("@graph",1,"qualifier-120_5",0)="120_52-59"
^jsonld("@graph",1,"qualifier-120_5",1)="120_52-50"
^jsonld("@graph",1,"rate-120_5")=98
^jsonld("@graph",1,"type")="vs:120_5"
^jsonld("@graph",1,"vital_type-120_5")="120_51-5"
^jsonld("@graph",2,"id")="120_52-64"
^jsonld("@graph",2,"label")="CUFF"
^jsonld("@graph",2,"type")="vs:120_52"
^jsonld("@graph",3,"id")="120_52-66"
^jsonld("@graph",3,"label")="ADULT"
^jsonld("@graph",3,"type")="vs:120_52"
^jsonld("@graph",4,"id")="120_52-59"
^jsonld("@graph",4,"label")="PALPATED"
^jsonld("@graph",4,"sameAs")="va:4688669"
^jsonld("@graph",4,"type")="vs:120_52"
^jsonld("@graph",5,"id")="vs:120_51"
^jsonld("@graph",5,"label")="GMRV VITAL TYPE"
^jsonld("@graph",5,"type")="fmso:File"
^jsonld("@graph",6,"id")="vs:2"
^jsonld("@graph",6,"label")="PATIENT"
^jsonld("@graph",6,"type")="fmso:File"
^jsonld("@graph",7,"id")="va:4688703"
^jsonld("@graph",7,"label")="GMRV VITAL QUALIFIER-SITTING"
^jsonld("@graph",7,"type")="skos:Concept"
^jsonld("generatedAt")="2013-11-20"
^jsonld("id")="fmql__T120_5__F.02=2-9&.01>2008-04-01__O0__C10"

Take a while to compare this Global structure with the JSON-LD document’s structure and you should begin to get the hang of what’s involved in the mapping process.  It’s actually remarkably simple and intuitive.

The Mumps developer’s task therefore becomes one of marshalling the JSON-LD document contents into the appropriate Global structure in terms of its subscripting and data values.  The Mumps developer doesn’t need to worry about parsing or generating correctly-nested JSON strings: it’s all just simple creation of Global nodes – standard, straightforward Mumps coding that any Mumps developer will be familiar and comfortable with.

Once this Global has been created, it can be consumed as JSON-LD by any JavaScript developer using EWD.js.  The JavaScript logic is trivially simple:

var jsonDoc = new ewd.mumps.GlobalNode('jsonld', []);
var jsonLD = jsonDoc._getDocument();

Bingo! jsonLD is a JavaScript object that holds the JSON-LD contents.

In the example above, I’ve just used a simple Mumps Global named ^jsonld that only holds our example JSON-LD document.  In a real-world scenario, we’d probably design a Global that can contain lots of JSON-LD documents.  That’s simply a matter of adding a few more initial subscripts to the Global structure.  For example, we might want the ^jsonld global to hold JSON-LD documents for many patients (identified by their patientId) and, for each patient, hold multiple document types (eg Vitals, Problems, Allergies etc).

So let’s modify that example Global (I’ll just show the first few nodes for clarity), so that it relates to the vitals for patient 123456:

^jsonld(123456,"vitals","@context","@base")="http://livevista.caregraf.info/"
^jsonld(123456,"vitals","@context","@vocab")="http://datasets.caregraf.org/vs/"
^jsonld(123456,"vitals","@context","date_time_vitals_entered-120_5","@type")="xsd:dateTime"
^jsonld(123456,"vitals","@context","date_time_vitals_taken-120_5","@type")="xsd:dateTime"
...etc

The JavaScript developer simply needs to modify the GlobalNode object reference to match the new initial subscripting and home in on the document that’s required:

var jsonDoc = new ewd.mumps.GlobalNode('jsonld', [123456, 'vitals']);
var jsonLD = jsonDoc._getDocument();

and the JSON-LD document is once again correctly extracted.

Of course, this approach isn’t just restricted to JSON-LD documents: any JSON document can be created within the Mumps environment simply by generating an appropriately-structured Global.  Neither is this restricted to VistA: any Mumps application can make use of this technique to map data stored in the Mumps database into JSON data for consumption within the JavaScript environment provided by EWD.js.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: