A Phoenix Rises

This is the third in a series of articles which explores a way forward for the future of the Mumps database and a solution to recruiting a new generation of developers to support, maintain and onwardly-develop the vast and important legacy of healthcare applications that have been developed using the Mumps language and database.

[Previous articles: Part 1 & Part 2]

In summary, the direction I’m suggesting is to:

  • replace the Mumps language with Javascript, a hugely popular and highly-capable modern language that happens to share the key characteristics of and has a similar free-and-easy feel to the Mumps language;
  • tightly integrate the Mumps database with Javascript in a way that has an equivalent level of intimacy as found between the Mumps language and database.  In doing so, the Javascript community can benefit from the Universal NoSQL database capabilities afforded by the Mumps database technology, but do so in terms that are meaningful and relevant to them.

In other words, in addition to ensuring a future for legacy Mumps applications, I believe that such an integration should be capable of igniting a new interest in a uniquely powerful database technology that has become side-lined and forgotten by the IT industry only because of deficiencies in its integrated language.

When I refer to Javascript,  I’m talking, of course, about server-side Javascript using Node.js, a technology that I’ve discussed in some detail in an earlier blog article.

Node.js Interface to Mumps

The first requirement is a high-performance interface / language binding between Node.js and Mumps.  It needs to be high-performance so that programming in Javascript doesn’t incur any significant penalty compared with coding in the Mumps language.

As mentioned in that earlier blog article, InterSystems have already created exactly what is required for both Caché and their free GlobalsDB database.  This interface was actually first created for the GlobalsDB database (think of GlobalsDB as Caché with everything stripped out apart from the Global Storage engine).  It provides an in-process binding to a very low-level and exceptionally high-performance interface to the heart of the Global Storage system.  Benchmarks show that it is only a few percentage points slower in use than native Mumps code.  The same interface has also been made available for Caché since version 2012.2.  This latter interface has one difference to the one for the GlobalsDB database: it includes a function() method which allows a Caché function to be invoked from within Node.js.

How about GT.M, the other major implementation of Mumps?  As it happens, the GT.M Open Source development community has recently created an equivalent interface to the one developed by InterSystems.  NodeM is an Open Source Node.js Addon module that binds to GT.M’s C Callin interface.  It has been designed to be API-compatible with the InterSystems interface.  Like the InterSystems interface, it not only allows access to GT.M’s Global Storage, but also allows Mumps functions to be invoked from within Node.js.

The one current deficiency in the GT.M interface is its performance.  Some simple, initial benchmarks by David Wicksell, the author of NodeM, suggest that it’s about half the speed of the InterSystems interface when run on the same hardware:

  • Caché and GlobalsDB were capable of saving data to Global Storage at a rate of about 95,000 sets/second
  • GT.M, by comparison, managed about 40,000 sets/second

This difference in performance is probably due to the GT.M Callin interface’s APIs not being as low-level as those provided for Caché and GlobalsDB.  Nevertheless, this level of performance is more than adequate for most practical purposes.  I’m hoping that it can be further improved.

So this means that we have a high-performance interface available for Caché, GlobalsDB and GT.M, all of which work identically as far as a Node.js developer is concerned: this is fantastic!  Does this mean our Phoenix is ready to fly?  The answer turns out to be no, not yet.  A quick look at a simple example of using this interface – setting a value into a Global – demonstrates the problem:

var node = {global: 'myGlobal', subscripts: ['x','y'], data: 'Hello World'};
db.set(node);

This is the equivalent to a Mumps developer invoking the following code:

set ^myGlobal("x","y")="Hello World"

You can immediately see that the Javascript version is:

- a lot more verbose than the Mumps version and not particularly clear as to what it’s doing.  Actually it’s pretty ugly code!
– pretty meaningless to a Javascript developer unless they know and understand Global Storage and understand the Mumps equivalent command.

The importance of the latter issue was highlighted in Part 2 of this article.

So we’re nearly there, but there’s one final piece needed for the Phoenix to spread its wings: a dynamic Object Orientated (OO) abstraction of Global Storage.

Creating an OO Abstraction of Global Storage

It turns out that it’s possible to create a direct mapping between an object hierarchy in Javascript and a Mumps Globals.  This is best demonstrated with a simple example:

Suppose we have a set of patient records that we wish to represent as objects.  We can define a top-level object patient that represents a particular physical patient.  Usually we’d have some kind of patient identifier key that distinguishes our particular patient: for purposes of this example, let’s say that patient identifier is a simple integer value: 123456.

Javascript’s object notation would allow us to represent the patient’s information using properties which could, in turn, be nested using sub-properties, for example:

  patient.name = "John Smith"
  patient.dateOfBirth = "03/01/1975"
  patient.address.town = "New York"
  patient.address.zipcode = 10027
  .. etc

This patient object could be represented as follows in physical Global Storage:

  ^patient(123456. "name") = "John Smith"
  ^patient(123456, "dateOfBirth") = "03/01/1975"
  ^patient(123456, "address", "town") = "New York"
  ^patient(123456, "address", "zipcode") = 10027
  .. etc

So there’s a direct one-to-one correspondence that can be made between an object’s properties and a Global’s subscripts.

When data is stored into a Global, we tend to refer to Global Nodes.  A Global Node is the combination of a Global’s name (in this case patient) and a number of subscripts.  A Global Node may effectively have any number of subscripts, including zero.  If no subscripts are specified, then we are referring to the top-level Global Node.  Data is stored at a Global Node.

Each level of subscripting represents an individual Global Node.  So, in our zipcode example above, we can represent the following Global Nodes:

  ^patient
  ^patient(123456)
  ^patient(123456, "address")
  ^patient(123456, "address", "zipcode") = 10027

Note that data has only been stored in the last Global Node shown. All the other Global Nodes exist but are just intermediate nodes without any data.

There is nothing in Mumps Global Storage that will tell us that subscripts of “address” and “zipcode” have been used in this particular Global other than by introspection of the Global Nodes: ie there is no built-in data dictionary or schema that we can reference.  Conversely, if we want to add more data to this Global we can just add it, arbitrarily using whatever subscripts we wish.  So we could add a County record:

 
  ^patient(123456, "address", "county") = "Albany"

Or the patient’s weight:

 
  ^patient(123456, "measurement", "weight") = "175"

Note that I could have used any subscripting I liked: there was nothing that forced me to use these particular subscripts (though in an application you’d want to make sure all records consistently used the same subscripting scheme).

So now we can bring the syntactical power of Javascript into play, using its objects and functions to hide all the nasty complexity of the Node.js / Mumps interface and wrap it up as a clean OO abstraction.

With the mapping of object properties to Global Nodes and their corresponding subscripts in mind, it’s possible to create a Javascript abstraction of Global Storage by defining a GlobalNode Object which has two arguments:

  • Global Name
  • an array of subscripts

For example:

 
  var patient = new GlobalNode('patient', [123456]);

A GlobalNode Object represents a physical Global Node in a Mumps database and has available to it a set of properties and methods that allow it to be manipulated and examined. An important feature of a GlobalNode Object is that it may or may not actually physically exist when first instantiated, and that may or may not change later during its existence within a Javascript/Node.js session.

A key property of a GlobalNode Object is _value. This is a read/write property that allows you to inspect or set its value, eg:

 
  var patientAddress = new GlobalNode('patient', [123456, "address"]);
  var name = patientAddress._value;  // John Smith
  console.log("Patient's name = " + name);

When you access the _value property, you’re accessing the physical Global Node’s value on disk, but of course we’re doing so via a Javascript object, in this case patientAddress.

Of course, ideally we’d like to be able to do the following:

 
  var patient = new GlobalNode('patient', [123456]);
  var name = patient.name._value;

But the problem is that the dynamic schema-free nature of Global Storage means that there is no way in advance of knowing that the patient GlobalNode has a subscript of “name” and therefore no way to know to instantiate a corresponding property (name). In theory we could instantiate every subscript within the specified Global Node as a property. However a Global might have thousands or tens of thousands of subscripts: it could take a significant amount of time and processing power to find and instantiate every one as a property and it would consume a lot of memory within Javascript in doing so.

In order to deal with this, a GlobalNode Object has a special method available to it called _getProperty(), normally abbreviated to $() (taking a leaf out of jQuery’s book!). The $() method does two things:

  • instantiates the specified subscript name as a property of the parent GlobalNode
  • returns another GlobalNode object that represents the lower-subscripted physical Global Node.

For example:

 
  var patient = new GlobalNode('patient', [123456]);
  var nameObj = patient.$('name');
  var name = nameObj._value;

What this code does is to first create a GlobalNode object that points to the physical Global Node:

 
  ^patient(123456)

The second line does two things:

  • adds the property named ‘name’ to the patient object;
  • returns a new GlobalNode object that points to the physical Global Node:
 
  ^patient(123456, "name")

These two effects of using the $() method are very interesting and powerful. First, now that we’ve used it once as a pointer to the name subscript, name is now properly instantiated as a known property of patient, so we can now refer directly to the property instead of using $() again, eg we can now change the patient’s name by simply doing the following:

 
  patient.name._value = "James Smith";

This is the equivalent of the Mumps code:

 
  set ^patient(123456,"name") = "James Smith";

Secondly, because $() returns a GlobalNode object (which may or may not exist or have a data value), we can chain them as deep as we like. So we can get the patient’s town like this:

 
  var town = patient.$('address').$('town')._value;

This is the equivalent of the Mumps code:

 
  set town = ^patient(123456, "address", "town")

Each of the chained $() method calls has returned a new GlobalNode object, representing that level of subscripting, so we now have the following Global Nodes defined as objects:

 
  patient - representing ^patient(123456)
  patient.address - representing ^patient(123456,"address")
  patient.address.town - representing ^patient(123456,"address","town")

So we can now use those properties instead of using $() again for them, eg to get the zip code, we just need to use the $() method for the zipcode property (since we’ve not accessed it yet):

 
  var zip = patient.address.$('zipcode')._value;

But if we want to report the patient’s town, we have all the properties defined, so we don’t need to use the $() method at all, eg:

 
  console.log("Patient is from " + patient.address.town._value);

So we now have Javascript objects that provide us with direct access to the data on disk, in a way that is analogous with the Mumps language. All we needed to do was to instantiate an initial GlobalNode object, and then use the $() method to access its lower-level properties. Behind the scenes, it’s still using that ugly syntax of the InterSystems interface or NodeM, but we no longer see it like that any more: it just looks like we’re using normal Javascript objects!

If the physical Global is known to be relatively small with a reasonable number of subscripts, then, if we want, we can use a shortcut method: _fixProperties(). This recurses through every subscripting level within the global and “fixes” each subscript as a property. For example, we could have done the following with our patient object:

 
  var patient = new GlobalNode('patient', [123456]);
  patient._fixProperties();

The _fixProperties() method has processed its way down the entire Global Node “tree” and found all its lower subscripts, creating GlobalNode objects for each one. This can be an expensive thing to do: each GlobalNode object consumes a finite amount of memory which can soon add up. However, for a relatively small Global, it provides the developer with the convenience and clarity of being able to use any of the Global’s subscript names as property names, without the need to use the $() method for any of them. So, for example, we can now very simply get the patient’s zip code and town as follows:

 
  var zip = patient.address.zipcode._value;
  console.log("Patient is from " + patient.address.town._value);

Other methods that are available to a GlobalNode object include:

  • _delete(): physically deletes any data for the specified GlobalNode and physically deletes any Global Nodes with lower-level subscripting beneath the specified GlobalNode. Note that any Javascript GlobalNode objects for lower-level subscripts continue to exist, but their properties that relate to their physical values will have changed.
  • _increment(): performs an atomic increment of the GlobalNode’s value
  • _forEach(): returns each subscript/property for the GlobalNode. Each one can be processed in a call-back function
  • _forRange(): same as forEach() but limited to a specified alphanumeric range
  • _forPrefix(): same as forEach() but limited to all subscripts/properties matching the specified prefix
  • _parent(): returns the GlobalNode’s parent as a GlobalNode object
  • _first: returns the GlobalNode’s first subscript/property value
  • _last: returns the GlobalNode’s last subscript/property value
  • _next(): returns the GlobalNode’s subscript that follows the one specified
  • _previous(): returns the GlobalNode’s subscript that precedes the one specified
  • _hasValue: returns true if the GlobalNode has a _value property defined
  • _hasProperties: returns true if the GlobalNode has one or more properties / subscripts
  • _exists: returns true if the GlobalNode exists on disk

So by creating this simple representation of a GlobalNode as an object with a relatively small number of methods and properties, we now have a fully-dynamic OO representation of Mumps Globals, mapped directly to Javascript objects: ie Mumps Globals, but represented in a way that can now make sense to a Javascript developer.

There’s one final pair of methods that further elevate this projection into a fully-fledged Native JSON Database:

  • _getDocument(): returns all child GlobalNodes beneath the specified GlobalNode object as a JSON document;
  • _setDocument(document): saves a JSON document into child GlobalNode objects of the specified GlobalNode;

These two documents give our Phoenix similar storage capabilities to MongoDB, but there is a key difference: when a JSON document is stored using _setDocument(), not only is it possible to subsequently retrieve the entire document again using _getDocument(), it is also possible to access information deep within the document directly from the Global that it’s stored in.  For example, consider the following:

 
var patient = new ewd.GlobalNode("patient", [123456]);
patient._delete();

var document = {
  "birthdate": -851884200,
  "conditions": [
    {
      "causeOfDeath": null,
      "codes": {
        "ICD-9-CM": [
          "410.00"
        ],
        "ICD-10-CM": [
          "I21.01"
        ]
      },
      "description": "Diagnosis, Active: Hospital Measures - AMI (Code List: 2.16.840.1.113883.3.666.5.3011)",
      "end_time": 1273104000
    }
  ]
};

patient._setDocument(document);

This has now saved the JSON document into the patient object and as a result it’s now stored into a Mumps Global using subscripting that reflects it property hierarchy, ie if you examined the actual Mumps Global you’d find:

 
^patient(123456,"birthdate")=-851884200
^patient(123456,"conditions",0,"causeOfDeath")=""
^patient(123456,"conditions",0,"codes","ICD-10-CM",0)="I21.01"
^patient(123456,"conditions",0,"codes","ICD-9-CM",0)="410.00"
^patient(123456,"conditions",0,"description")="Diagnosis, Active: Hospital Measures - AMI (Code List: 2.16.840.1.113883.3.666.5.3011)"
^patient(123456,"conditions",0,"end_time")=1273104000

So the JSON document hasn’t been stored as a blob or some other representation: its entire contents have been mapped directly into corresponding Global Nodes at each of its object hierarchy levels. This means that not only can we retrieve the complete original document, eg:

 
 var document = patient._getDocument();

but we can also directly access its internal properties and in doing so we’re actually touching the values on the disk, eg:

 
 var desc = patient.$('conditions').$(0).$('description')._value;

Alternatively we could fix all the properties and then access the data using them directly using standard Javascript object notation that corresponds to the original JSON document also!  For example:

 
 patient._fixProperties();
 var desc = patient.conditions[0].description._value;

We can even retrieve sub-sections of the JSON document as JSON documents, eg:

 
 patient._fixProperties();
 var codes = patient.conditions[0].description.codes._getDocument();

 /*
 codes = 
{ 'ICD-10-CM': { '0': 'I21.01' },
  'ICD-9-CM': { '0': '410.00' } }
*/

If you think about it, this is powerful stuff: a Native JSON database!

Try out the Phoenix for yourself

And so our Phoenix now arises from the ashes of Mumps.  All the power of Mumps, all its capabilities and benefits, but now couched in terms that any Javascript developer can understand, and creating a powerful new genre of NoSQL database while we’re at it.

You’ll hopefully now itching to try it all for yourself.  How to do so depends on whether you’re using Caché, GlobalsDB or GT.M

Caché or GlobalsDB

If you’re using Caché or GlobalsDB you’ll need:

Create a text file containing the Javascript you wish to run and then invoke it from within Node.js.  Your Javascript file must first initialise and start the Node.js/Caché interface, and then must load the OO wrapper module, for example:

 
//initialise interface etc

var ewd = require('./ewdGlobals');
var globals = require("c:\\Program Files\\nodejs\\cache");
var db = new globals.Cache();
var params = {
  path:"c:\\InterSystems\\Cache\\Mgr",
  username: "_SYSTEM",
  password: "SYS",
  namespace: "USER"
};
db.open(params);
ewd.init(db);

// initialisation complete

var patient = new ewd.GlobalNode("patient", [123456]);
patient._delete();

var document = {
  "birthdate": -851884200,
  "conditions": [
    {
      "causeOfDeath": null,
      "codes": {
        "ICD-9-CM": [
          "410.00"
        ],
        "ICD-10-CM": [
          "I21.01"
        ]
      },
      "description": "Diagnosis, Active: Hospital Measures - AMI (Code List: 2.16.840.1.113883.3.666.5.3011)",
      "end_time": 1273104000
    }
  ]
};

patient._setDocument(document);

patient._fixProperties();

var codes = patient.conditions[0].codes._getDocument();
console.log(codes);

db.close();

You may need to amend the file-paths I’ve used in the example to correspond with the directories you use.

GT.M

The best way to try the Phoenix out with GT.M is to use the pre-built, pre-configured, ready-to-run dEWDrop Virtual Machine. Everything is set up and ready to run in the latest version that you’ll find at the FourthWatch Software site.

If you want to use your own GT.M system, you’ll need:

Create a text file containing the Javascript you wish to run and then invoke it from within Node.js.  Your Javascript file must first initialise and start the Node/Cache interface, and then must load the OO wrapper module, for example:

 
//initialise interface etc

var ewd = require('./ewdGlobals');
var globals = require('/home/vista/mumps');
var db = new globals.Gtm();
db.open();
ewd.init(db);

// initialisation complete

var patient = new ewd.GlobalNode("patient", [123456]);
patient._delete();

var document = {
  "birthdate": -851884200,
  "conditions": [
    {
      "causeOfDeath": null,
      "codes": {
        "ICD-9-CM": [
          "410.00"
        ],
        "ICD-10-CM": [
          "I21.01"
        ]
      },
      "description": "Diagnosis, Active: Hospital Measures - AMI (Code List: 2.16.840.1.113883.3.666.5.3011)",
      "end_time": 1273104000
    }
  ]
};

patient._setDocument(document);

patient._fixProperties();

var codes = patient.conditions[0].codes._getDocument();
console.log(codes);

db.close();

You may need to amend the file-paths I’ve used in the example to correspond with the directories you use.

You can see that apart from small differences in the initialisation, the OO projection is identical for Caché, GlobalsDB and GT.M.

And there we have it: the Phoenix arises from the ashes of Mumps.

Mumps is Dead. Long Live Mumps!

About these ads

20 comments

  1. I think the VA should standardize its new development using these tools, but encapsulate them in a Java-based “Kernel” to isolate the programmer and prevent his use of propriatary code.

  2. kevin furze · · Reply

    Rob.
    in the latter example you have
    //initialise interface etc
    here, you start to set username, password.
    whats the method of preventing the end-user from just loading the JS code, viewing the username,password etc and then “playing” with its own commands. ?

  3. Kevin

    This is server-side Javascript,running in Node.js. It’s not running in a browser!

  4. Many thanks Rob,

    This clearly is a landmark article and deserves to be well read.
    To that end, could I ask you to clarify a few points please, either with revising the text or in reply to this comment..

    I’d suggest your first paragraph summarises in a few lines the very important direction you are suggesting here.. ie a powerful combination of
    1. Javascript as a very popular flexible language (client&server side) +
    2. Mumps as the ideal Universal NoSQL database.

    re performance, you mention the NodeM as an exciting API standard for interfacing between Javascript and Mumps. Could you give a view on the relative performance between native Mumps and this Javascript oriented approach? I’m sure folk will want to know..

    In terms of the syntax, in looking at the example code you mention to explain the $() method…
    var patient = new GlobalNode(‘patient’, [123456]);
    var nameObj = patient.$(‘name’);
    var name = nameObj._value;
    ..could you please contrast this syntax with the other approaches to setting up variables in Javascript and /or equivalent code in Mumps..
    It would be helpful to see this key area explained in very simple terms, i.e. I’d also appreciate a clearer explanation of the move from setting up that patient to affixing the name “James Smith”..forgive my novice level of understanding here, but I’m keen to share this article with other novices!

    Also could I ask you to please help explain the difference between the _getProperty() and _fixProperties() methods. I’m sure there is an important reason for having these 2 approaches, though to be honest would appreciate an even simpler rationale..

    Many thanks again

    Tony

  5. Tony

    Many thanks for your comments. I’ve amended the posting and expanded some of the explanations. Let me know if this helps clarify how it all works!

  6. Thanks Rob
    Those improvements do help.
    Again in terms of syntax:
    If playing devils advocate, am assuming the Javascript approach is naturally more wordy.. trying to gauge how much. Am trying to compare like with like here:

    Javascript code:
    var patient = new GlobalNode(‘patient’, [123456]);
    var nameObj = patient.$(‘name’);
    var name = nameObj._value;
    patient.name._value = “James Smith”;

    Mumps code:
    set ^patient(123456,”name”) = “James Smith”;

    Is that the right and fair comparison?

    I’m comfortable with the Javascript code as is, though would Mumps fans say it multiples the wordcount?
    No doubt there are advantages either way, perhaps you’d comment on the pros/cons of this issue.

  7. Also another mention re : fixProperties()
    “This recurses through every subscripting level within the global and “fixes” each subscript as a property”

    Could you explain this for the uninitiated? Would Javascript developers be used to this approach?
    If not how can we best explain that fixing subscripts is a worthwhile “overhead”?
    thanks
    Tony

  8. Tony

    No – the comparison is as follows:

    Javascript code:
    // the following line is needed only once to instantiate the pointer to the physical global:
    var patient = new GlobalNode(‘patient’, [123456]);

    // then it’s just a matter of:

    patient.$(‘name’)._value = “James Smith”;

    which compares with the equivalent Mumps code:

    set ^patient(123456,”name”) = “James Smith”;

    You actually find that more complex manipulation of data becomes much slicker and more succinct and clear in Javascript than the corresponding Mumps code, eg a good example looping through the subscripts of a global:

    patient._forEach(function(key) {
    console.log(key);
    });

    as opposed to:

    set key=””
    for set key=$order(^patient(123456,key)) quit:key=”” do
    . write key,!

    1. ok Rob, so that’s great news.
      i.e your suggested approach can be both short and tidy!
      thanks
      Tony

  9. I wouldn’t get too hung up about the _fixProperties() method. It’s just a convenient (and arguably lazy) way of avoiding the need to explicitly use the $() method to locate and instantiate properties within a Global. Most of the time, I’d actually recommend people use $(), and _fixProperties() should only be used with small amounts of data at a time.

    _fixProperties() does provide a nice way of gaining easy-to-read access to every property at every level within an entire Global in just one hit rather than lots of explicit $() method calls.

    1. ok thanks Rob
      That will do for now.
      Look forward to seeing this take off!
      Tony

  10. I am of the opinion that a data dictionary of some kind is an important thing to have for a database. The “Free Form” save any property with a free text name might be OK for prototyping, but a mature application will need to validate data, check dependencies and run ad hoc reports without having to hard code every property and validation check. Do you envisage a data dictionary created per application like VA Fileman, or would some outside M standard like an XML DTD work better?

    1. The Mumps philosophy is that any data dictionary or schema is something that is the responsibility of the application or application developer to create and maintain, as needed for that application. The Mumps database is a schema-free database, as is now the trend with NoSQL databases.

      Fileman is a good example: the authors of VistA decided that they would create and maintain the data dictionary/schema for VistA using a set of APIs they would define. However, most non-VistA Mumps applications don’t use Fileman, and neither should they.

      My EWD web app framework, for example, exposes the Global Storage it uses through my own set of APIs which are built upon themselves, starting from the lowest-level “primitive” ones that do the actual physical manipulation of the Global Nodes needed to store the data and create/maintain the necessary indexes to that data. So, most of EWD’s logic uses high-level API function calls rather than deal with the physical Global Storage – that’s buried at the bottom of a stack of APIs that I’ve built up over the years.

      See my mountaineering analogy in:

      http://gradvs1.mgateway.com/download/extreme1.pdf

      If you think you need the safety and assurance of a built-in schema/data dictionary, then a Mumps database is probably not for you. For me, I know it will do anything I want to use it for and will allow me to build whatever I want in an exceptionally short time, and will not only be extremely robust, but also will perform and scale better than pretty much any other database technology I’ve ever come across.

      Rob

  11. Rob,

    The tight-coupling of the MUMPS language to its underlying database (one of the magic ingredients you mention in part 2) is one of its features that often frustrates me the most. It certainly is a boon to doing quick development, but tightly coupling two parts of a system together is usually frowned upon, for reasons of extensibility. Also, it makes certain modern best practices like automated unit testing very difficult, if not downright impossible, since ideally, such tests should not be hitting the database. Are you recommending that your Javascript library be used directly in Javascript-based business logic (similar to how a traditional MUMPS-based program was written)? Or are you assuming that it would only be used in some kind of Javascript-based data access module acting as a layer of separation between your business logic and the actual MUMPS database?

    I like where you’re going with this, but I fear that carrying forward the tightly-coupled nature of MUMPS code to its database might be ill-advised, though I very well may be reading more into your sample code than you’re intending. Thoughts?

    Thanks!
    Derek

    1. Derek

      I think the Phoenix approach satisfies your needs. Firstly, you need to understand that the tight-coupling is a syntactic one, not a physical one. What I mean by this is that the OO abstraction creates the appearance that you’re handling Javascript objects, but you’re actually hitting the database records directly. The physical connection is via one or other of two gateways: the InterSystems Node.js interface to Cache or the NodeM interface to GT.M. So immediately, you can see that your Javascript logic isn’t tied directly to a specific Mumps database – you can seamlessly switch between GT.M and Cache using exactly the same Javascript application logic.

      Take that a bit further and you’ll realise that it’s my ewdGlobals.js module that requires the physical interface to GT.M or Cache and does the mapping to physical Global Nodes. It would be quite feasible for an alternative to ewdGlobals.js to be written that *doesn’t* interface to GT.M or Cache, but simply uses in-memory Javascript objects or that maps and interfaces to some other database – ie a version that mocks a back-end connection to GT.M or Cache that you can use for unit tests. I’d be very pleased if someone were to write such a mocking alternative to ewdGlobals.js. It really shouldn’t be difficult.

  12. Rob,

    Great article. I’m new to Healthcare IT, Mumps and GlobalNode DB. Reading through I was initially confused with the terminology such as subscripts and Global, etc. Coming from JS and Node.js background subscripts means nothing to me and when you say Global I’m thinking global variables. Not major but speaking JS rather than Mumps will really help get people understanding faster.

    The other question I have is why did you try to maintain symmetry between Mumps and JS syntax? If I’m writing an app that uses GlobalNode as a DB I don’t care about Mumps syntax, I want to use my normal JS object notation to read/write, iterate, etc. through data. The _fixProperties and _getDocument/_setDocument methods are nice but seem almost as an after thought and not very usable when it comes to high transaction rates.
    Again, I’m comparing the experience and usability of this DB to CouchDB, MongoDB, Redis, etc. If the experience and learning curve is so different it’s a major barrier to entry. Personally, I feel like if you can make the Node.js library abstract away the Mumps syntax and limitations and make it feel more like it would be to access a normal NoSQL DB your Phoenix will definitely rise quicker.

    One topic that missing is search. How do you go about searching a GlobalNode DB? What if I want to find all patients that have x-ray appointments on a particular date.

    Also, are there mechanisms for an application to subscribe to DB updates so I can build logic that manages new appointments as they arrive or notifies patients as soon as their results are recorded, etc. rather than constantly querying an already busy DB.

    Lastly, just a minor feedback regarding the coding style of the library. Using underscore (_) in names typically refers to “private” variables and methods. It’s not a rule but a normal JS convention. Changing your method names to remove underscore would follow JS convention a little closer.

    Anyways, I think this is really awesome, if you can help further abstract Mumps (since I have no time or motivation to learn another syntax/query language) it would really help adoption and I’d love to see your work take off.

    Roy

    1. Roy

      Thanks for your comments. I’ll follow up some of your questions more fully in future articles.

      I agree the Mumps terminology is confusing – unfortunately Globals is what the storage is known as, and you’re right, most non-Mumps people think Globally-scoped variables when they see that term. Someone needs to think of an acceptable alternative name for Globals – unfortunately them the Mumps folks won’t know what we’re referring to! :-)

      Regarding finding records from Mumps Global Storage – it’s all about defining/using index globals which you then use the forEach() method to iterate through and use the value(s) to point to the data records held in other Global Nodes. A good example is a module I’ve written using the OO projection on top of Global Storage: see https://github.com/robtweed/ewdDOM

      This is an implementation of a persistent XML DOM – I’m going to write an article about it in more detail, but for the purposes of answering your query, if you look at methods such as getElementsByTagName, you’ll see that the Javascript logic iterates through an index by tag name, and then returns the pointer(s) to the matching XML Tag Nodes. ewdDOM’s logic maintains the data and index nodes that represent the XML DOM and everything is bootstrapped from primitive methods that look after the physical data and index Global Nodes into APIs that are then layered – a fairly common technique in Mumps programming.

      Regarding the underscores I’ve used for the property and method names – I understand your criticism. However, I had to use something that wouldn’t be typically used as the physical Global subscript values. eg it’s quite reasonable to assume that some legacy Mumps app would have Globals where “value” was a subscript name – in order to prevent a GlobalNode Object’s methods/properties being over-ridden by the actual properties coming back from the physical Global, I had to use a naming convention that would be unlikely in a legacy Mumps database, but would still be reasonable to read in Javascript….so I chose the underscore prefix, though I knew I might be criticised by the JS community for doing so! :-) If you can think of a more acceptable prefix I’d be happy to change, provided it wouldn’t be something you’d commonly see used as a prefix in legacy Globals.

      Regarding translating this stuff to high-scale multi-user environments, I have another article pending that will discuss another module I’ve developed called ewdGateway2 (you can already get hold of it and see it at https://github.com/robtweed/ewdGateway2 ) which will support what you’re asking. All Mumps systems are highly scalable multi-user systems, and we’ll be able to support similar numbers of users via Node.js (if not more). ewdGateway2 supports websockets via some very cool simple APIs I’ve developed, and, for example, activity happening elsewhere in a GT.M or Cache hospital system will be able to trigger events that result in information dynamically updating other users’ browser (or mobile browser) displays. I’m actually very excited by the possibilities we now have available to modernise those legacy Mumps healthcare systems – exciting times lie ahead.

      Rob

  13. Rob,

    Thanks for the response. I don’t have much of an issue with terminology since I can understand what you’re saying tho it does help new comers to get on board quicker. Anyways, this is mainly marketing :)

    I took a look at ewdDOM to get an idea of how to search through the data. It’s still a bit cumbersome and could be improved for usability and readability. Hopefully in your next blog post you can touch a bit more about the querying and searching capabilities of Global and Mumps.

    I also looked at ewdGateway2 which is more inline with the examples you gave in this blog post. I think this module is a great step forward in bridging the gap. You really want a nice REST API to front a Global DB. Building a module that does the heavy lifting of dealing with Mumps and talking to Global while exposing a clean REST API (also websockets) makes life simple for a JS developer (or any web developer for that matter).

    I did want to ask if you knew of a sample dataset that could be used for testing purposes to give people an idea of the structure a medical records could have. We all know what tweets or FB posts look like but I have no clue how medical records look like. Any sort of sample data would really help understand the kind of data we can expect to be working with.

    Lastly, are there any particular use cases you think would be valuable to attempt? any that you believe would benefit from the power of Node.js? Having no background in healthcare IT it’s hard to tell what is really a burning issue that needs to be resolved or just a novelty that sounds good but is not really of major value.

    Thanks,
    Roy

  14. Hi all,

    I know little about Mumps but I understand the necessity for more Mumps developers. I see the language’s widespread use in important legacy systems as the biggest reason that someone new to the game should bother learning Mumps.

    After reading this series of articles it seems that the next step is to build a Mumps to NodeM compiler. Are there plans to build a conversion tool between the two languages?

    Thank,
    Al

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

Follow

Get every new post delivered to your Inbox.

Join 61 other followers

%d bloggers like this: