n00b questions

Hello,

I have some beginner questions as I am working through the InterSystems Cache learning path:

- Where I work, we us Cache, but we often learning about and train on MUMPS.  No one really talks about or mentions MUMPS here, but my understanding is that ObjectScript is basically MUMPS plus whatever new things InterSystems put on top of it.  Is that a fair assessment?

- The training online often refers to IRIS.  We don't use IRIS, but we use globals to store data.  I suppose I'm just confused on the difference between Cache, Zen, IRIS and other products as they all seem to do similar things.  

- If I have a global available in a certain namespace, can I use InterSystems SQL to query those globals?  

- How do existing globals and creating classes work?  Like I have a Person global right now.  Can I turn that into a class and manipulate the data that way?

- I'm used to Java where you can write a class and then write a driver class to test your classes and methods. Or simply just test your newly created classes and methods in the main method (wherever that lies in your code).  Is there something similar in Studio?  I can write classes but then they are compiled and I have to go to the terminal and test them?  Is this where routines come into play in Studio?

Is anyone else in similar boat where you're using Cache but not all the other tools and products InterSystems has?  I'm struggling with learning my company's way and InterSystems' way.

Looking forward to any comments or feedback.

Best,

Mike

  • 0
  • 0
  • 296
  • 13
  • 3

Answers

- If I have a global available in a certain namespace, can I use InterSystems SQL to query those globals?  

- How do existing globals and creating classes work?  Like I have a Person global right now.  Can I turn that into a class and manipulate the data that way?

You'll need class mapping to query globals via SQL. Check article series  The Art of Mapping Globals to Classes by @Brendan Bannon.

- I'm used to Java where you can write a class and then write a driver class to test your classes and methods. Or simply just test your newly created classes and methods in the main method (wherever that lies in your code).  Is there something similar in Studio?  I can write classes but then they are compiled and I have to go to the terminal and test them?  Is this where routines come into play in Studio?

You can configure Studio or Atelier debugger to run any class method. There's no need to use routines for that.

Others can correct me if I am wrong, but everything Intersystems is moving to under the IRIS name.  Think of IRIS as a container with everything running under it.  Health Information Exchange (HIE), Cache Object Script, Globals, Ensemble, and etc run under what they call IRIS.

Zen is Intersystems way of creating Web Pages that talk directly with Cache.

You can use and call globals from any one of the products that you might be using. Globals works across the board, and is just another way of storing the data. In your above example think of the Person global as a variable you can call and retrieve the data

I have a global called OSUWMCLDAP that has various properties I use in some of my class files. I call this global from my class file ie like the following...

Here is examples I use in my class files to retrieve the data from the global.

#define LDAPServer $Get(^OSUMCLDAP("Server"))

Or

$Get(^OSUMCLDAP("Domain"))

Thanks, Scott.  

What you explained about IRIS was what someone told me about Cache, lol!  I'd be interested to know other perspectives on the grouping of products and what they are, but what you've said makes sense.

I might rephrase my problem with globals: let say I've been using Cache/MUMPS for many years and all my data is in globals.  With the advent of ObjectScript and studio, I would like to take a more object oriented approach to manipulating my data.  How would I create a class to do that?

^PERSON(1) = "MIKE|MALE|DEVELOPER"

^PERSON(2) = "SCOTT|MALE|MASTER PROGRAMMER"

How would I create a class to do the CRUD ops on these existing globals?

Thanks!

Mike

I guess I am not following. I have created class files in the past through Studio to call those globals I referenced before. I am not familiar with the CRUD method, but a simple SET ^PERSON(1) = "MIKE|MALE|Developer" should be able to set your global, and when you do a GET to pull the information out of the global you can use PIECE to split the string apart by the | . Someone out here in the development community might have a better answer for you.

Thanks!  I'm familiar with SET and $PIECE.

So I have my global that exists already:

^PERSON(1) = "Mike|Male|Dev"

Can I turn that into an object? 

PERSON.1 = "Mike D"

Let me read below as to what Chris has said as well and see if that helps.

MD

Mike,

Let me try to clarify some things here. You currently have a ^Person global with data in it. What others are referring to are objects that would be similar to that global. The best way (IMO) to do this would be to convert the data into objects. First, create and compile a class:
Class User.TestPopulate Extends %Persistent
{
Property Name As %String;
Property Gender As %String;
Property Role As %String;

}

In my example, I populated a global:

%SYS>s ^Person(1) = "Pete|MALE|Senior Support Specialist"
%SYS>s ^Person(2) = "Mike|MALE|Developer"

The default global for data storage for your new class will just be <PACKAGE>.<CLASS>D and the index global will be I instead of D. So, for the data, you're looking at, in my case:

%SYS>zw ^User.TestPopulateD

--> This is empty, because I haven't populated the object at all. Now, I wrote a simple routine to iterate through the ^Person global (no matter how large it is, this will work). Note this has minimal error checking:

TestPopulate
= ""
{
= $o(^Person(x),1,y)
q:x=""
personOBJ = ##class(User.TestPopulate).%New()
personOBJ.Name = $p(y,"|",1)
personOBJ.Gender = $p(y,"|",2)
personOBJ.Role = $p(y,"|",3)
sc = personOBJ.%Save()
if (sc'=1) {
$system.OBJ.DisplayError(sc)
}
!,y
}

Note this also just prints out the value of each person as it goes. Now, I run that routine, and then take a look at the 'D' global again:

%SYS>d ^TestPopulate
 
Pete|MALE|Senior Support Specialist
Mike|MALE|Developer
%SYS>zw ^User.TestPopulateD
^User.TestPopulateD=2
^User.TestPopulateD(1)=$lb("","Pete","MALE","Senior Support Specialist")
^User.TestPopulateD(2)=$lb("","Mike","MALE","Developer")

Now, if I want to access  an individual record, I can via objects:

%SYS>s me = ##class(User.TestPopulate).%OpenId(1)
%SYS>w me.Name
Pete

Finally, if you want to access it with SQL, you can, now that it's populated in an object (note I had to use SQLUser instead of User because I chose 'User' as my package name):

%SYS>d $system.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
 
The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
%SYS>>select * from SQLUser.TestPopulate
1.      select * from SQLUser.TestPopulate
 
ID      Gender  Name    Role
1       MALE    Pete    Senior Support Specialist
2       MALE    Mike    Developer
 
2 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0819s/50437/298608/17ms
          execute time(s)/globals/lines/disk: 0.0005s/4/854/1ms
                          cached query class: %sqlcq.pSYS.cls6
---------------------------------------------------------------------------

Hope this helps

Thanks!  You and Chris Thompson have both helped me greatly here!

MD

Class User.Person Extends %Persistent {

Property Name as %String;

Property Gender as %String;

Property JobTitle as %String;

}

set Person = ##class(User.Person).%New()

set Person.Name = "Mike"

set Person.Gender = "Male"

set Person.JobTitle = "Developer"

set status = Person.%Save()

This creates a new record in the Person table, but it's also in the User.PersonD global. Assuming this is the first record, you'll have an %Id() of 1

if you wanted to get the Person

set Person = ##class(User.Person).%OpenId(1)

if you wanted to delete:

set status = ##class(User.Person).%DeleteId(1)

Hope that helps.

P.S. - I wrote this in only a couple of minutes so there may be errors. Intersystems documentation can tell you more and I think there are examples in the Samples namespace.

Right, I've been working with the documentation and training, but I came here because I don't think it's always very clear.

In your example here you've use .%Save() to save to the Person table.  Is that the same thing as the ^PERSON global? 

Based on what I've tried, I would say it is not.  So you see I'm trying to use ObjecScript and object to update my ^PERSON global.

Does that make sense?

MD

SQL tables are just projections (this is an over simplification) of the globals. 

So your table User.Person is storing all of the data in ^User.PersonD. If you have indices, they are stored in ^User.PersonI.

if you compile that class, and in terminal run zw ^User.PersonD you'd see the structure of the global.

Ah yes, I see this now!  Thank you!

I can see the ^(Package).PersonD global created in our global exploration tool, however it won't let me read it.  When I load and run the routine in the terminal and do a ZW I get data that looks like this:

person1=<OBJECT REFERENCE>[1@Test.MXD.Person]

The status indicator for the %Save shows success (1).  

Now how do I do it the other way around?  Let's say a global ^PEOPLE already exists?   How do I create a class do deal with that?  If I create a class User.People, the global ^User.PeopleD will be created when I want it to use ^PEOPLE.

MD

Yes, you are seeing that because it sounds like you are loading the object and then doing a zw on the object. 

You can create your class and then change the storage definition in your class to use the ^Person global. I suggest you get a good idea of how the storage definitions work before you start messing around with it though. 

Thanks, but we don't use IRIS so I'm not sure how helpful they'll be.

MD

If you want to use pure MUMPS to transfer your data from global ^Person to persistent class- let's say  ^MyClass.Person, 

 so you can manipulate them like Object Scripts and using SQL you have to:

^PERSON(1) = "MIKE|MALE|DEVELOPER"

^PERSON(2) = "SCOTT|MALE|MASTER PROGRAMMER"

1.Create persistent class  ^MyClass.Person

Class MyClass.Person Extends %Persistent
{
Property Name As %String;
Property Gender As %String;
Property Role As %String;

}

2. Use routine something like this:

c1 n=""
1 n=$o(^Person(n)) q:n=""
a=^Person(n) i=1:1:3 a(i)=$p(a,"|",i)
^MyClass.PersonD(n)=$LB("",a(1),a(2),a(3))
1

 or if you prefer a little be more clear

c2 n=""
2 n=$o(^Person(n)) q:n=""
a=^Person(n) Name=$p(a,"|",1),Gender=$p(a,"|",2),Role=$p(a,"|",3)
^MyClass.PersonD(n)=$LB("",Name,Gender,Role)
2

3.Of course If you have indices like properties in the persistent class, the things will be a little more complicated.

for example:

Property Name As %String;

Index NameIndex On Name;

you have to create and global  ^MyClass.PersonI  with structure like:

 ^MyClass.PersonI ("NameIndex"," "_Name,n)=""

c3 n=""
3 n=$o(^Person(n)) q:n=""
a=^Person(n) Name=$p(a,"|",1),Gender=$p(a,"|",2),Role=$p(a,"|",3)
^MyClass.PersonD(n)=$LB("",Name,Gender,Role)
^MyClass.PersonI("NameIndex"," "_Name,n)=""
3

Welcome to the the InterSystems Developer Community!!

1) ObjectScript is a (very, very large) superset of the M language (previously known as MUMPS)

2) InterSystems IRIS uses much of the core technology that was in Caché, but it is a break from the past in many ways and allows next-gen development tools, architectures, etc.  InterSystems IRIS is an upgrade path which current Caché based applications should seriously consider.  Zen is a legacy web development technology which runs on top of Caché (it ships with Caché and InterSystems IRIS)

3) The answer to this depends on whether you are taking straight globals, or persistent objects.  Any data persisted via InterSystems Object or Relational structures can automatically be accessed via SQL.  If you are just writing straight globals (e.g. set ^Patient(123)=foobar) then you can't query unless you set up an SQLStorage mapping to your globals

4) You can map your existing global structures to object definitions for OO/SQL access using SQLStorage (https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls...).  It looks like there isn't a lot in the docs on that so you should contact Support for more details on how to set it up to match your existing data to definitions which you can hit with traditional SQL.

Hope that helps!