Node.js Iris retrieve

Hi,

I'm using node.js to access to Iris.

Considering this globals:

^Customer(1, "Address", 1)="London"

^Customer(1, "Address", 2)="UK"

^Customer(1, "Name")="Jane K. White"

^Customer(2, "Address", 1)="Reigate"

^Customer(2, "Address", 2)="UK"

^Customer(2, "Name")="Peter R. Black"

^Customer(3, "Address", 1)="London"

^Customer(3, "Address", 2)="UK"

^Customer(3, "Name")="Paul J. Green"

I retrieve all customers with: 

mydata.retrieve( { global: "Customer" },

"array",

function(error, result) {

if (error) {

// error (see result.ErrorMessage and result.ErrorCode)

} else {

// success

} } );

How could I retrieve just the customers living in London ? Any chance to retrieve filtering with regular expressions or any other type of filters ? I noticed lo and hi but it's very limited.

Thanks,

Fabian

  • + 1
  • 2
  • 154
  • 10
  • 2

Answers

See details in NoSQL Methods for Global Nodes

Especially  Get the Next Global Subscript: next() lets you iterate over first subscript  ^Customer(id)   1, 2, 3, ...

next using Retrieve a Global Node: get() you access ^Customer(id, "Address", 1) and check if it is "London"

from docs mydata.retrieve 

This method will return a list of subscript values that are defined directly beneath a given level in the global.

In your call, you get the full global. but you have to do the detail work yourself.

There is no such thing as SELECT ...... FROM GLOBAL WHERE .......

This is NoSQL.

But you may hide your functionality in a ClassMethod inCaché and use invoke_classmethod()


 

Thank you Robert,

I know I can iterate but it is impossible to do with a large database.

Regarding to your reference this is NoSQL,  you have this kind of queries in NoSQL databases (we are porting our solution from mongoDB to Iris and with mongo node.js client you  can retrieve with and/or, regular expressions,  make sorts, etc) . Also Iris has the way with Object Script. 

I asked here to confirm we don't have a chance to do from node.js. I agree with you the path might be to develop a classMethod with object script.

May be if Intersystems provides the source code of iris node client module  we could improve the "lo hi" current available option and make something generic to have a node.js client similar to the other NoSQL platforms.

My personal preference is to a have a solution NOW.
And not wait for something that may take too long for me.

just the most simple example to solve your issue with a ClassMethod:

Class nodes.Select
{
ClassMethod Address1(town As %String = "") As %String
{ set (list,id)=""
    for  set id=$o(^Customer(id)) quit:id=""  
            if ^(id,"Address",1)=town set list=list_$lb(id) }
   return "["_$lts(list)_"]"
}}

Thank you Robert !!!

I want to implement the ClassMethod without having to iterate the whole base, something using indexes and also get sorting. I was taking a look to ObjectScript but if you could provide a path it would be great.

As you have an installation of Caché you will also have the Documentation with it.

I recommend "Using Caché ObjectScript" to start with ObjectScript.
It is also public accessible https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCOS

I also recommend having a look at the free online training library.
The link is in the header of this forum:  LEARNING

Browse Catalog and search for O bjectScript Basics

All details on individual commands, functions, system variables are again in your local instance or in the
public reference on docs.intersystems.com

You should have a look at the ewd-document-store npm module, this module contains high-level global access methods for Node.js. Btw, it's the underlying module used also by the QEWD.js back-end server for Node.js & Caché/IRIS.

E.g. you can iterate easily over your Customer global using:

var DocumentStore = require('ewd-document-store');

// put the appropriate irisXXX.node version inside node_modules
// and rename it to iris.node
var iface = require('iris');
var db = new iface.IRIS();

// Change these parameters to match your IRIS system:
var ok = db.open({
  path: '/opt/iris/mgr',
  username: '_SYSTEM',
  password: 'SYS',
  namespace: 'USER'
});

// bind the iris.node db instance to the high-level DocumentStore
var documentStore = new DocumentStore(db);
// instantiate a DocumentNode object to access the Customer global
var customerNode = new documentStore.DocumentNode(‘Customer’);
// iterate over customerId's using the forEachChild() method
customerNode.forEachChild(function (customerId) {
  console.log(customerId); // will output 1, 2, 3, ...
  // access sub-nodes using the $() method using method chaining
  // and retrieve the city using the value property
  if (customerNode.$(customerId).$('Address').$('1').value == 'London') {
    // do what's needed with a London customer ...
  }
});

// probably, customerNode.forEachLeafNode() is even more appropriate

// close the db connection to IRIS
db.close();

I haven't completely tested the code above, but you should get the idea. The module provides you with some cool high-level NoSQL access methods to iterate over a global  in JavaScript. The methods of DocumentStore and DocumentNode allow you to access very large globals without size and/or memory limitations (see documentation).

Ward is correct - ewd-document-store is the way to go.

For background on this module, the thinking behind it and some of the considerations you need to be aware of when using it (which ultimately explain what QEWD.js is all about and why it is what it is), see this presentation that I gave a couple of years ago at the UK Symposium:

https://www.slideshare.net/robtweed/data-persistence-as-a-language-feature

Also see here for pointers to further info on the persistent JSON / Document Database abstraction provided by ewd-document-store:

https://github.com/robtweed/qewd/blob/master/up/docs/InteractiveApps.md#...

Note that ewd-document store will work with any version/build of Cache, Ensemble or Iris

Rob

BTW you can further simplify and optimise the performance of Ward's forEachChild() logic like this:

customerNode.forEachChild(function (customerId, childNode) {
  if (childNode.$(['Address', '1']).value === 'London') {
    // do what's needed with a London customer ...
  }
});

ie by using the childNode argument of the forEach() and using the $ array feature

Rob

Thank you Ward and Rob !

You can do the same with the Intersystems node.js client (take a look to Robert answer). The idea is not to have ti iterate every record, it would be impossible with collections of millions of objects. I took a look to QEWD.js and it looks it has similar functionalities than node.js client. If you find any way to filter objects retrieval without iterating please let me know.

To iterate over just the global nodes you need, see my other comment below. You need an index global to do this.

Please note that QEWD.js and the Node.js adapter are very different: the Node.js client is a low-level adapter to access the Caché/IRIS database, while QEWD.js is a full multi-process back-end application server (in the underlying code, QEWD is using the ewd-document-store abstraction and, in turn, uses the IRIS node.js client to access Caché/IRIS). In fact, it removes you from writing a lot of system "plumbing" code to get your front-end code working with Caché/IRIS. You'll find QEWD examples for use with React apps at react-qewd and with Vue.js/Nuxt.js apps at vue-qewd.

Fabian

You misunderstand: ewd-document-store is an abstraction built on top of the InterSystems Node.js client, NOT an alternative to it.  QEWD uses the InterSystems Node.js client via ewd-document-store, so once again, QEWD isn't an alternative to the Node.js client, but makes it a lot easier to use with IRIS or Cache.

You can't directly filter objects with IRIS NoSQL storage (aka Global Storage) - instead the trick it to maintain indices to optimise iteration - see Ward's follow-up comment., and study those slide decks I've created that describe how to use QEWD's abstraction of Global Storage.

@Fabian Pie one thing I forgot: you should also build an index from the Customer global, e.g. based on Robert's code snippet:

set id="" for {
  set id=$order(^Customer(id)) quit:id=""
  set city=$get(^Customer(id,"Address",1))
  set name=$get(^Customer(id,"Name"))
  if $length(city)&$length(name) set ^CustomerIndex(city,name,id)=""
}

Now you can easily filter on the selected city and loop over the (sorted) customer names by iterating over the ^CustomerIndex global using similar JavaScript code as above. By using globals, you get sorting for free! And you don't need to traverse the whole customer global anymore.