docs.intersystems.com
Home  /  Application Development: Language Bindings and Gateways  /  Using Node.js with InterSystems IRIS  /  Using iris.node NoSQL Methods


Using Node.js with InterSystems IRIS
Using iris.node NoSQL Methods
[Back]  [Next] 
InterSystems: The power behind what matters   
Search:  


This chapter introduces some basic concepts and describes how to connect iris.node to the database. The following topics are discussed:
Comparing Globals and JSON Objects
In this section we will look at the basic relationship between JSON objects in the Node.js environment and InterSystems IRIS.
Consider the following global node:
   ^Customer(1)="Jane K. White"
Note:
By convention, global names are prefixed with the ‘^’ character in InterSystems IRIS. However, this convention need not be followed in the corresponding JSON representation.
The equivalent JSON construct will be:
   {global: "Customer", subscripts: [1], data: "Jane K. White"}
Adding further nodes to this data construct:
globals:
   ^Customer(1)="Jane K. White"
   ^Customer(1, "Address", 1)="London"
   ^Customer(1, "Address", 2)="UK"
   ^Customer(1, "DateOfRegistration")="1 May 2010"
JSON:
   {  global: "Customer",
      subscripts: [1],
      data: "Jane K. White"
   }
   {  global: "Customer",
      subscripts: [1, "Address", 1],
      data: "London"
   }
   {  global: "Customer",
   subscripts: [1, "Address", 2],
   data: "UK"
   }
   {  global: "Customer",
   subscripts: [1, "DateOfRegistration"],
   data: "1 May 2010"
   }
iris.node Methods: (Synchronous vs. Asynchronous)
All methods provided by iris.node can be invoked either synchronously or asynchronously. While synchronous operation is conceptually easier to grasp and can be useful for debugging, the expectation in the Node.js environment is that all operations should be implemented asynchronously with a completion event raised by means of a callback function. For this reason, most of the examples given in this guide are coded to run asynchronously. To run them synchronously simply omit the callback function from the arguments list.
Consider the method to determine the version of the iris.node module in use:
Synchronous operation:
   var result = mydata.version();
Asynchronous operation:
   mydata.version(
      function(error, result) {
         if (error) { // error
         }
         else { // success
         }
      }
   );
The standard convention for reporting errors in Node.js is implemented in iris.node. If an operation is successful, error will be false. If an error occurs then error will be true and the result object will contain the details according to the following JSON construct:
   {
      "ErrorMessage": [error message text],
      "ErrorCode": [error code],
      "ok": [true|false]
   }
For synchronous operation, you should check for the existence of these properties in the result object to determine whether or not an error has occurred.
Opening and Closing the InterSystems IRIS Database
Before any other methods can be called, the iris.node module must be loaded, an instance of the InterSystems IRIS object created and the target InterSystems IRIS database opened before any data (or InterSystems IRIS functionality) can be accessed.
Loading the iris.node Module
If the iris.node module has been installed in the correct location for your Node.js installation, the following line will successfully load it from the default location:
   var irisobj = require('iris');
If the module is installed in some other location the full path should be specified. For example:
   var irisobj = require('/opt/cm/node0120/build/default/iris');
Creating an Instance
The next task is to create an instance of the iris.node object.
   var mydata = new irisobj.Iris();
There are two ways to connect to InterSystems IRIS. First, there is the option of API level connectivity to a local InterSystems IRIS instance and, second, network based connectivity to either a local or remote InterSystems IRIS instance.
Connecting Via the InterSystems IRIS API
Connecting to a local instance of InterSystems IRIS via its API offers high performance integration between Node.js and InterSystems IRIS.
   mydata.open(parameters[, function(error, result){}]);
where:
Example:
   mydata.open({  path:"/iris20151/mgr",
                  username: "_SYSTEM",
                  password: "SYS",
                  namespace: "USER"
               }
               [, function(error, result){}]
   );
The InterSystems IRIS Principal Device
The InterSystems IRIS principal input and output device for the iris.node session can be specified by defining the input_device and output_device properties respectively.
Example (using the standard input/output device):
   mydata.open({  path: '/opt/iris20151/mgr',
                  username: "_system",
                  password: "SYS",
                  namespace: "user",
                  input_device: "stdin",
                  output_device: "stdout"}
               [, function(error, result){}]
   );
The default is to use the NULL device for iris.node sessions.
Connecting to InterSystems IRIS Via the Network
Network based connectivity to InterSystems IRIS can be used to connect to either a local or remote instance of InterSystems IRIS. This method is particularly useful for connecting to InterSystems IRIS systems installed on platforms for which Node.js is not natively available.
Also, if Node.js is hosting a public facing web application then network based connectivity to InterSystems IRIS will provide the basis for a secure architecture in which the database is physically separated from the web server tier:
   mydata.open(parameters[, function(error, result){}]);
where:
Example:
   mydata.open({  ip_address: "127.0.0.1",
                  tcp_port: 56773,
                  username: "_SYSTEM",
                  password: "SYS",
                  namespace: "USER"
               }
               [, function(error, result){}]
   );
Multiple Instances of InterSystems IRIS Connectivity
The iris.node module will allow multiple instances of the InterSystems IRIS class to be created per hosting Node.js process if (and only if) TCP based connectivity to InterSystems IRIS is used.
Example 1: Creating two instances of the InterSystems IRIS class
   var irisobj = require('iris');
   var user = new irisobj.Iris();
   var samples = new irisobj.Iris();

   user.open({ ip_address: "127.0.0.1",
               tcp_port: 51773,
               username: "_SYSTEM",
               password: "SYS",
               namespace: "USER",
            }
   );

   samples.open({ ip_address: "127.0.0.1",
                  tcp_port: 51773,
                  username: "_SYSTEM",
                  password: "SYS",
                  namespace: "SAMPLES",
               }
   );

   console.log("'user' instance in namespace: " + user.get_namespace());
   console.log("'samples' instance in namespace: " + samples.get_namespace());

   user.close();
   samples.close();
If InterSystems IRIS API based connectivity is used, it is still the case that only one instance can be created in a single Node.js process. This is because the InterSystems IRIS executable to which the hosting Node.js process binds is inherently single threaded. However, it is possible to create one connection using API based connectivity together with one or more TCP based connections.
Example 2: Creating two instances of the InterSystems IRIS class (one API and one TCP)
   var irisobj = require('iris');
   var user = new irisobj.Iris();
   var samples = new irisobj.Iris();

   // API based connection to InterSystems IRIS
   user.open({ path:"/iris20152/mgr",
               username: "_SYSTEM",
               password: "SYS",
               namespace: "USER",
            }
   );

   // TCP based connection to InterSystems IRIS
   samples.open({ ip_address: "127.0.0.1",
                  tcp_port: 51773,
                  username: "_SYSTEM",
                  password: "SYS",
                  namespace: "SAMPLES",
               }
   );

   console.log("'user' instance in namespace: " + user.get_namespace());
   console.log("'samples' instance in namespace: " + samples.get_namespace());

   user.close();
   samples.close();
Closing Access to the InterSystems IRIS Database
The following method will gracefully close a previously opened InterSystems IRIS database.
   mydata.close([function(error, result){}]);
Optional open() Settings
Specifying Character Encoding
The default character encoding used in iris.node is UTF-8. Alternatively, 16-bit Unicode (UTF-16) can be used by defining this character encoding in the open() method.
Example:
   mydata.open({  path:"/iris20151/mgr",
                  username: "_SYSTEM",
                  password: "SYS",
                  namespace: "USER",
                  charset: "UTF-16"
               }
                  [, function(error, result){}]
   );
Disabling the InterSystems IRIS Serialization Lock
An InterSystems IRIS process is inherently single threaded. Events fired asynchronously in the Node.js environment depend on the use of multiple threads to complete tasks concurrently within the context of a single process. For this reason, a serialization lock is applied to all InterSystems IRIS operations (invoked through iris.node) to ensure that only one command at a time is submitted to the associated InterSystems IRIS process.
However, locking is expensive. As a performance enhancement, the serialization lock may be disabled in the open() method if it can be ascertained that the Node.js application will only submit one task at a time to InterSystems IRIS. The lock should not be disabled if iris.node methods are used asynchronously. Even if synchronous mode is used throughout (with respect to the iris.node methods), it must be ascertained that calls to InterSystems IRIS fired synchronously cannot overlap as a result of events firing asynchronously elsewhere in the application. If in doubt, the lock should remain enabled. If an application crash occurs with the lock disabled then it should be re-enabled as a first step in diagnosing the problem.
Example: Disabling the serialization lock
   mydata.open({  path:"/iris20151/mgr",
                  username: "_SYSTEM",
                  password: "SYS",
                  namespace: "USER",
                  lock: 0
               }
               [, function(error, result){}]
   );
Enabling Debug Mode
A trace facility is included in the iris.node module to facilitate easier debugging when problems occur. The trace will record all calls to the InterSystems IRIS Call-in API, the input arguments used and the result returned. If TCP based connectivity is used the trace will record all request buffers submitted to, and response buffers received from, InterSystems IRIS.
The trace facility is enabled by defining the debug property in the open() method.
Example 1: Write a trace to the console (i.e. stdout)
   mydata.open({  path:"/iris20151/mgr",
                  username: "_SYSTEM",
                  password: "SYS",
                  namespace: "USER",
                  debug: 1
               }
               [, function(error, result){}]
   );
Example 2: Write a trace to a file (debug.log)
   mydata.open({  path:"/iris20151/mgr",
                  username: "_SYSTEM",
                  password: "SYS",
                  namespace: "USER",
                  debug: "debug.log"
               }
               [, function(error, result){}]
   );
Example trace:
Node.js Code:
   var irisobj = require('iris');
   var mydata = new irisobj.Iris();
   mydata.open({  path:"/opt/iris20151/mgr",
                  username: "_SYSTEM",
                  password: "SYS",
                  namespace: "USER"
                  debug: "debug.log",
               });
   mydata.set("Person", "1", "Jane K. White");
   var data = mydata.get("Person ", 1));
   mydata.close();
This will produce the following trace (or similar):
       >>> 000007FEF9E39340==fopen(debug.log, "a")
           (Debug trace file opened)
   >>> iris_open
       >>> 0000000180000000==sys_dso_load(
          /opt/iris20151/bin/libiris.so)
       >>> 0==IrisSetDir(/opt/iris20151/mgr)
       >>> 0==IrisSecureStartA(0000000000126CC0(_SYSTEM),
           0000000000116CA0(SYS),
           000000000010EC90(Node.JS), 56, 15,
           0000000000106C80(//./nul), 000000000012ECD0(//./nul))
   >>> iris_set
       >>> 0==IrisPushGlobal(6, Person)
       >>> 00000000269E7010==IrisExStrNew(000000002682D770, 2)
       >>> 0==IrisPushExStr(000000002682D770)
           >>> 1
       >>> 00000000269E7010==IrisExStrNew(000000002682D788, 11)
       >>> 0==IrisPushExStr(000000002682D788)
           >>> Jane K. White
       >>> 0==IrisGlobalSet(1)
       >>> 0==IrisExStrKill(000000002682D770)
       >>> 0==IrisExStrKill(000000002682D788)

   >>> iris_get
       >>> 0==IrisPushGlobal(6, Person)
       >>> 00000000269E7010==IrisExStrNew(000000002682D770, 2)
       >>> 0==IrisPushExStr(000000002682D770)
           >>> 1
       >>> 0==IrisGlobalGet(1, 0)
       >>> 0==IrisPopExStr(000000000013F410)
       >>> 0==IrisExStrKill(000000000013F410)
           >>> Jane K. White
       >>> 0==IrisExStrKill(000000002682D770)

   >>> iris_close
       >>> 0==IrisEnd()
Specifying an Interrupt Signal Handler
The cache.node module includes an optional signal handler to facilitate the clean interrupt of Node.js processes connected to InterSystems IRIS. Ideally, instead of relying on this facility, applications should include a suitable termination signal handler in the JavaScript code.
For example: to respond to the SIGINT signal (Control-C) in JavaScript code:
   process.on( 'SIGINT', function() {
      // clean up and close down gracefully
   });
In the absence of such an application level signal handler, the iris.node module can be instructed to register its own termination signal handler. Use the stop_signal property in the open() method to specify that iris.node should terminate execution on receiving signal SIGINT or SIGTERM (or both).
For example, to instruct iris.node to close the Node.js process on receiving either a SIGINT or SIGTERM signal:
   user.open({path: "/iris20163/mgr",
              username: "_SYSTEM",
              password: "SYS",
              namespace: "USER",
              stop_signal: "SIGINT,SIGTERM"
             }
   );
The signal handlers implemented in the iris.node module do no more than simply close down connectivity to InterSystems IRIS and halt the hosting Node.js process.
Utility Functions
Pausing a Node.js Operation: sleep()
When troubleshooting Node.js applications, particularly those making extensive use of asynchronous completion techniques, it is often convenient to pause a function. The iris.node module contains a sleep() method to pause the current thread of execution for a period of time (in milliseconds) defined as the argument.
Example: (sleep for 5 seconds)
   mydata.sleep(5000);
Note:
This facility should only be used for troubleshooting as Node.js is architected according to the principle that no operation should block the main thread of execution.
Getting Version Information: version() and about()
The version() and about() methods return basic version information about the iris.node module in use and the associated InterSystems IRIS database (if open).
Synchronous:
   var result = mydata.version();
Or:
   var result = mydata.about();
Asynchronous:
   mydata.version(function(error, result){});
Or:
   mydata.about(function(error, result){});
Example:
   mydata.version(
      function(error, result) {
         if (error) { // error (see result.ErrorMessage and result.ErrorCode)
         }
         else { // success
         }
      }
   );
Result:
If the InterSystems IRIS database is not open:
   Node.js Adaptor for InterSystems IRIS: Version: 1.1.112 (CM)
If the InterSystems IRIS database is open:
   Node.js Adaptor for InterSystems IRIS: Version: 1.1.112 (CM); InterSystems IRIS Version: 2016.3 build 168
Invoking an InterSystems IRIS Function: function()
Functions contained within the InterSystems IRIS environment can be called directly via the function() method.
Synchronous:
   var result = mydata.function(iris_function);
Asynchronous:
   var result = mydata.function(iris_function, function(error, result){});
The Math routine
The examples in this section will be based on calls to the following InterSystems IRIS routine:
   Math           ; Math Functions
                  ;
   Add(X,Y)       ; Add two numbers
                  Quit (X * Y)
                  ;
   Multiply(X,Y)  ; Add two numbers
                  Quit (X * Y)
                  ;
This is a simple InterSystems IRIS routine called Math containing functions to perform basic mathematical functions (Add and Multiply).
Example 1 (Synchronous/Non-JSON)
   result = mydata.function("Add^Math", 3, 4);
result: 7
Example 2: (Synchronous/JSON)
   result = mydata.function({function: "Add^Math", arguments: [3, 4]},
result:
   {
      "function": "Add^Math",
      "arguments": [3, 4],
      "result": 7
   }
Example 3: (Asynchronous/JSON)
   mydata.lock(
      {function: "Add^Math", arguments: [3, 4]},
      function(error, result) {
         if (error) { // error (see result.ErrorMessage and result.ErrorCode)
         }
         else { // success
         }
      }
   );
result:
   {
      "ok": 1
      "function": " Add^Math ",
           "arguments": [3, 4],
           "result": 7
   }