Just in addition to what Robert wrote, Mumps has the concept of multithreading for over 50 years! Long before C++ and Java was born.
justmy2cents
- Log in to post comments
Just in addition to what Robert wrote, Mumps has the concept of multithreading for over 50 years! Long before C++ and Java was born.
justmy2cents
Instead of inserting debug_macros, try Intersystems TRACE utility.
write $$DIR^TRACE("c:\Temp\") ; to set an output directory
write $$ON^TRACE(jobnr) ; the pid of the process you want to trace
; zn "appNamespace"
; do ^yourProgram
; zn "%SYS"
write $$OFF^TRACE(jobnr) ; to stopp the trace
do ^TRACE ; to display the trace resultTRACE displays the function-/method-calls with arguments.
I said nothing about a documentation. I don't do it even ![]()
By the way, what's the definition of an empty set?
Answer:
4(%@3%4@')6%.@"9@).4%23%#4)/.@/&@-5-03@02/'2!-%23@!.$@4(%)2@$/#5-%.4!4)/.
@Evgeny Shvarov
Thank you, I've made a note.
A internet search engine can be of great help and, of course, some luck, ![]()
Carefully reading the DC is also a good source of information, ![]()
![]()
See this articles
https://community.intersystems.com/post/yet-another-use-case-translate-…
https://community.intersystems.com/post/convert-numeric-expression20201…; (last reply)
After seeing several solutions I got the idea to make a comparison.
The bottom line is, it's advisable to check how an algorithm (or function or method etc.) performs over another.
So try the below program snippet... you will be surprised!
Test //
s date="20201121090000"
s new=""
s t0=$zh
f i=1:1:1E6 s new=$e(date,1,4)_"-"_$e(date,5,6)_"-"_$e(date,7,8)_" "_$e(date,9,10)_":"_$e(date,11,12)_":"_$e(date,13,14)
s t1=$zh
f i=1:1:1E6 s new=$tr("abcd-ef-gh ij:kl:mn","abcdefghijklmn",date)
s t2=$zh
f i=1:1:1E6 s new=$zd($zdh($e(date,1,8),8),3)_" "_$e(date,9,10)_":"_$e(date,11,12)_":"_$e(date,13,14)
s t3=$zh
f i=1:1:1E6 s new=$system.SQL.TOTIMESTAMP(date, "YYYYMMDDHHMISS")
s t4=$zh
f i=1:1:1E6 &SQL(SELECT TO_TIMESTAMP(:date,'YYYYMMDDHHMISS') INTO :new)
s t5=$zh
w "$e() only",?12,t1-t0,!
w "$tr()",?12,t2-t1,!
w "$e()+$zd()",?12,t3-t2,!
w "SQL/class",?12,t4-t3,!
w "SQL/static",?12,t5-t4,!
q
Of course, the results will depend on hardware, on Cache/IRIS version and on utilisation of your system
Use $tr() in backward mode
set date=" 20201121090000"
write $tr("abcd-ef-gh ij:kl:mn","abcdefghijklmn",date) --> 2020-11-21 09:00:00Just to see things clearer, you do this test directly on Cache server (i.e. local)?
And you have logged in with your local (and not, for example with an domain\kevin) account? Cache runs also under the same local "kevin" account?
Hello Kevin,
in most of the cases (but not always) the reason for this is the exhaustion of TCP ports, see
https://docs.microsoft.com/en-us/windows/client-management/troubleshoot…
too. Grab a windows command prompt and start with
netsh int ipv4 show dynamicport tcpthis shows you how many ports you have.
netstat -ano | find "TCP"shows you all the TCP ports in use (including the process numbers) and
netstat -an | find "TCP" | find "CLOSE"shows you all the bad guys.
If this is your problem then the solution is:
- increase the number of ports (if it's possible)
- reduce the cases, where a new port is needed and immediate closing of unneeded ports
If your number is an integer
write $extract(1E10 + 12345, 2, 11) // if N is a fixed value, here N = 10or
write $extract("1E"_N + 12345, 2, N+1) // if N is variableThe game ends if your number has more than 18 (decimal) digits!
A quick and dirty way:
set ^|"%SYS"|%SYS("SystemMode")="TEST" // or "LIVE" or "DEVELOPMENT" or "FAILOVER"Sorry, I don't quite get you.
One used to say, if I don't understand something, then this something is either too complicated or very simple. So where belongs your case?
I'm not sure about what you mean with "unlimited amount of indexes"...
first, there is a limit of 255 for the parameters, this means you can have
do ..setValue(key1,key2,...key254,value)but you can't have
do ..setValue(key1,key2,...key254,key255,value)second, if you meant to have many-many values (in sense of, we have to much data)
do ..setValue(key1,value1)
do ..setValue(key2,value2)
// etc.then, at some point you will reach the and of the available RAM (incl. swap/paging space. Usually swap/paging space is much less then the space for the real data).
thirdly, if you meant variable number of indexes (in sense of variable number of parameters) then I can just refine Robert's solution to
ClassMethod setValue(params...) As %Status
{
set base=$name(%session.Data)
for i=1:1:params-1 { set base=$name(@base@(params(i)))
set @base=params(params)
quit 1
}In case, you have both, variable number of indices and too much data then you could use a mix of the above with data placed in a temp file.
Somewhere, at the program start, let you point %sessionData to a temp file
set %sessionData=$nam(^||SessionTemp)and then consider this in the setValue method
ClassMethod setValue(params...) As %Status
{
set bas=%sessionData
...
}Of course, in your application, you have to change only those %session.Data(...) to @%sessionData@(...) which uses the "variable-big-data" part of %session.Data(). All other (the standard ones) stay unchanged.
Oh yes, DATEADD(ms, ....) works, but
select 'Arrival Time' ...
won't work. This gives the string constant of "Arrival Time"
Sorry, I didn't noticed the space char... maybe I need new glasses ;-)
But yes, if you have some unusual property names, then you need double-quotes ($char(34), he used $char(39))
I'm pretty sure, you intended to write
select COUNT (Arrival Time) FROM dbo.table where Arrival Time < DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00')i.e. do not place propertynames under quote
By the way, I don't know, how many records you have in your table, but if you have thousands or millions of records, consider to compute constant things just one time!
DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00')is a constant value, it's not neccessary to compute it for each record! Also, in this particular case, CONVERT() and LEFT() are also not needless, 1603173432000 is the value, and it is an integer.
As Robert said, XSLT... but as he indicated too, in this case XSLT would be an overkill.
But there is also a "forgotten" function, $locate() too for such cases! This function is almost never used in DC examples. Two or three lines of code, and you have the perfect solution... but for "simple" cases only. Beware of nested tags!
SetFixedValue(str,tag,value) Public
{
set i=0, t="(?i)<"_tag_">[^<]+", s=$l(tag)+2
while $locate(str,t,i,j,v) { set $e(str,j+s-$l(v),j-1)=value, i=j+s-$l(v)+$l(value) }
quit str
}
It works like a charm...
set a="<Name>..."
write $$SetFixedValue^test(a,"rollno","***") --> <Name>ABC</Name><RollNo>***</RollNo><Name>XYZ</Name><RollNo>***</RollNo><Name>xyz</Name><RollNo>***</RollNo>During the installation process a stripped-down version of the apache web server will be installed too (to serve the management portal and for test purposes). This is the CSP-System (CSP means CacheServerPages). Enter a password, you will need it rarly or almost never.
To say it simply, you did this:
set x=123
set x=456and just see 456 but not 123!
What you (probably) want to do is:
set x(1)=123
set x(2)=456I added one more "patient item" to your JSON and a simple test class
Class DC.PR1 Extends %RegisteredObject
{
ClassMethod GetDataFromTestStream() [ PublicList = (myGuid, myId, myForename, mySurname, myDOB, nokName, nokRelation, nokTel, nokEmail) ]
{
set xData=##class(%Dictionary.CompiledXData).%OpenId(..%ClassName(1)_"||Test")
try { set obj={}.%FromJSON(xData.Data) }
catch e { set obj="",err=e.Name }
if 'obj {
write "Error: ",err,!
quit ""
}
// Put all properties in the corresponding SUBSCRIPTED variable
//
for i=0:1:obj.%Size()-1 { // loop over patients (top array)
set patient=obj.%Get(i).patient
set myGuid(i)=patient.guid
set myId(i)=patient.id
set myForename(i)=patient.forename
set mySurname(i)=patient.surname
set myDOB(i)=patient.dateOfBirth
for j=0:1:patient.NOK.%Size()-1 { // loop over NOKs of the i-th patient
set nok=patient.NOK.%Get(j)
set nokName(i,j)=nok.NOKname
set nokRelation(i,j)=nok.NOKrelationship
set nokTel(i,j)=nok.telephone
set nokEmail(i,j)=nok.email
// instead of storing each property, you could
// process them hier...
}
}
}
XData Test [ MimeType = application/json ]
{
[
{
"patient":
{
"guid":"12345",
"id":12345,
"forename":"Joe",
"surname":"Bloggs",
"dateOfBirth":"2002-12-10T00:00:00Z",
"NOK":[
{
"NOKname":"Alison Bloggs",
"NOKrelationship":"Wife",
"telephone":"02081234567",
"email":"alison@bloggs.com"
},
{
"NOKname":"Peter Bloggs",
"NOKrelationship":"Father",
"telephone":"02081234567",
"email":"Peter@bloggs.com"
}
]
}
},
{
"patient":
{
"guid":"212345",
"id":212345,
"forename":"John",
"surname":"Blue",
"dateOfBirth":"2003-12-10T00:00:00Z",
"NOK":[
{
"NOKname":"Aline Blue",
"NOKrelationship":"Wife",
"telephone":"032081234567",
"email":"aline@blue.com"
},
{
"NOKname":"Peter Blue",
"NOKrelationship":"Father",
"telephone":"032081234567",
"email":"Peter@blue.com"
}
]
}
}
]
}
}
Try it in a terminal session:
do ##class(DC.PR1).GetDataFromTestStream()
zwrite
The output should be:
myDOB(0)="2002-12-10T00:00:00Z"
myDOB(1)="2003-12-10T00:00:00Z"
myForename(0)="Joe"
myForename(1)="John"
myGuid(0)=12345
myGuid(1)=212345
myId(0)=12345
myId(1)=212345
mySurname(0)="Bloggs"
mySurname(1)="Blue"
nokEmail(0,0)="alison@bloggs.com"
nokEmail(0,1)="Peter@bloggs.com"
nokEmail(1,0)="aline@blue.com"
nokEmail(1,1)="Peter@blue.com"
nokName(0,0)="Alison Bloggs"
nokName(0,1)="Peter Bloggs"
nokName(1,0)="Aline Blue"
nokName(1,1)="Peter Blue"
nokRelation(0,0)="Wife"
nokRelation(0,1)="Father"
nokRelation(1,0)="Wife"
nokRelation(1,1)="Father"
nokTel(0,0)="02081234567"
nokTel(0,1)="02081234567"
nokTel(1,0)="032081234567"
nokTel(1,1)="032081234567"
And don't forget, you may have more then one "patient" item in your JSON, so either consume the data in the inner loop oder place them in a two-dimensional array.
HTH
$SYSTEM returns the (local) name of a machine (where Cache/IRIS is installed), a colon, and the instance name.
Sorry, my mistake.
FQDN is the fully qualified domain name for a specific computer, or host, on the internet.
$SYSTEM returns the (local) name of a machine (where Cache/IRIS is installed), a colon, and the actual namespace. Usually, Cache and IRIS are not exposed to internet.
So the question is, what you need, what you want to do?
Unfortunately no, because I have nothing to do with HealthShare, HL7 and all those healthy things. If you have any ObjectScript or Class problem, I am happy to help you.
Thanks for the adjustment, I looked at the very first line (odbc format, which is 3).
Anyway, the $tr() construct is called the $tr()-backwards format, I found this on the internet more then ten years ago... so the credit goes to a unknown inventor.
$tr(targetPattern, sourcePattern, sourceValue) --> targetValueit's easy, without AM/PM
write $tr("abcd-ef-gh ij:kl:00","abcdefghijkl",202010011000),! // ODBC-Format
write $tr("gh.ef.abcd ij:kl:00","abcdefghijkl",202010011000),! // German-Format
write $tr("ef/gh/abcd ij:kl:00","abcdefghijkl",202010011000),! // US-Formatset datetime=202010011000
set tmp=$tr("abcdefgh ij:kl:00","abcdefghijkl",datetime) // or using $e(...)
set tmp=$e(datetime,1,8)_" "_$e(datetime,9,10)_":"_$e(datetime,11,12)_":00" write $zdt($zdth(tmp,3),1,3)
To make your (and others) life easier... here is a simple class to display a dynamic object (or dynamic array).
/// A general class for various helper functions
///
Class %zapi.utils Extends %RegisteredObject
{ /// Show an dynamic array or object
///
/// obj : the dynamic object (or array) you want to display
/// deep: max path depth, 0 (default) = display all
/// find: if given, (a part of) a property name
ClassMethod ShowObj(obj, deep = 0, find = "")
{
new %seen
do ..shObj(obj,deep-1,find,"<obj>")
}
ClassMethod shObj(obj, deep, find, path) [ Internal, Private ]
{
set path=path_"."
set itr=obj.%GetIterator()
while itr.%GetNext(.prop,.val) {
set:obj.%IsA("%DynamicArray") prop="%Get("_prop_")"
set prop=path_prop
if $isobject(val) {
if $data(%seen(+val)) { set val="same as --> "_%seen(+val)
} else { set %seen(+val)=prop }
}
if $isobject(val),deep { do ..shObj(val,deep-1,find,prop)
} else { write:prop[find prop,": ",val,! }
}
}
}
With our old pData, use it as follows:
write pData
[{"patient":{"guid":"12345","id":12345,"forename":"Joe","surname":"Bloggs","dateOfBirth":"2002-12-10T00:00:00Z"},"visit":[{"guid":45678,"date":"2020-01-10","reason":"other"},{"guid":45679,"date":"2020-01-11","reason":"routine"}],"
documentAttachments":[{"guid":"23432","id":152,"catergory":"notes"},{"guid":"23433","id":153,"catergory":"summary"}]}]
set obj={}.%FromJSON(pData)
do ##class(%zapi.utils).ShowObj(obj)If you don't want to write each time a novel, you can create your own "show object" command too.
Insert the following line into %ZLANGC00 routine (if %ZLANG doesn't exists, create it):
ZSHO(obj, deep=0, find="") Public { do ##class(%zapi.utils).ShowObj(obj,deep,find) }and Voila!, you got a short command to display dynamic objects
zsho obj // display the whole object
zsho obj:2 // display up to second nesting level
zsho obj::"visit" // display all occurrences of property visit
zsho obj::"guid" // display all occurrences of property guidThe outputs of the above commands are
USER>zsho obj
<obj>.%Get(0).patient.guid: 12345
<obj>.%Get(0).patient.id: 12345
<obj>.%Get(0).patient.forename: Joe
<obj>.%Get(0).patient.surname: Bloggs
<obj>.%Get(0).patient.dateOfBirth: 2002-12-10T00:00:00Z
<obj>.%Get(0).visit.%Get(0).guid: 45678
<obj>.%Get(0).visit.%Get(0).date: 2020-01-10
<obj>.%Get(0).visit.%Get(0).reason: other
<obj>.%Get(0).visit.%Get(1).guid: 45679
<obj>.%Get(0).visit.%Get(1).date: 2020-01-11
<obj>.%Get(0).visit.%Get(1).reason: routine
<obj>.%Get(0).documentAttachments.%Get(0).guid: 23432
<obj>.%Get(0).documentAttachments.%Get(0).id: 152
<obj>.%Get(0).documentAttachments.%Get(0).catergory: notes
<obj>.%Get(0).documentAttachments.%Get(1).guid: 23433
<obj>.%Get(0).documentAttachments.%Get(1).id: 153
<obj>.%Get(0).documentAttachments.%Get(1).catergory: summary
USER>zsho obj:2
<obj>.%Get(0).patient: 11@%Library.DynamicObject
<obj>.%Get(0).visit: 2@%Library.DynamicArray
<obj>.%Get(0).documentAttachments: 21@%Library.DynamicArray
USER>zsho obj::"visit"
<obj>.%Get(0).visit.%Get(0).guid: 45678
<obj>.%Get(0).visit.%Get(0).date: 2020-01-10
<obj>.%Get(0).visit.%Get(0).reason: other
<obj>.%Get(0).visit.%Get(1).guid: 45679
<obj>.%Get(0).visit.%Get(1).date: 2020-01-11
<obj>.%Get(0).visit.%Get(1).reason: routine
USER>zsho obj::"guid"
<obj>.%Get(0).patient.guid: 12345
<obj>.%Get(0).visit.%Get(0).guid: 45678
<obj>.%Get(0).visit.%Get(1).guid: 45679
<obj>.%Get(0).documentAttachments.%Get(0).guid: 23432
<obj>.%Get(0).documentAttachments.%Get(1).guid: 23433 Have a nice day...
It's simple and easy... just follow the picture (I meant, the JSON string):
[
{
"patient":
{
"guid":"12345",
"id":12345,
"forename":"Joe",
"surname":"Bloggs",
"dateOfBirth":"2002-12-10T00:00:00Z",
"NOK":[
{
"NOKname":"Alison Bloggs",
"NOKrelationship":"Wife",
"telephone":"02081234567",
"email":"alison@bloggs.com"
}
]
}
}
]
Now we create a dynamic Object:
set obj = {}.%FromJSON(pData)Your pData is a JSON-Array, where the (array)elements/items are objects
(we have just one elemment), so we can say:
set item = obj.%Get(0)This item object has a patient property, which is an object, so we go one step deeper
set item = obj.%Get(0)
set patient = item.patient
set patient = obj.%Get(0).patient // same as aboveOur patient object has properties like guid, id, forename, surname, dateOfBirth and NOK.
The property NOK itself is, again, an array where the array elements/items are objects.
set nok = patient.NOK
set nok = obj.%Get(0).patient.NOK // same as aboveNow, we know, our property NOK is an array, so we have to take of those elements. This nok elements are objects, so we can take the properties (NOKname, ...email):
set nokItem = nok.%Get(0)
write nokItem.NOKname," ",nokItem.email,!or, all in one line
write obj.%Get(0).patient.NOK.%Get(0).NOKname,!Now all together, a small routine to print out all NOKxxxx properties:
set obj = {}.%FromJSON(pData)
for i=0:1:obj.%Size()-1 { // loop over pData items grabbing patient props
set patient = obj.%Get(i).patient
for j=0:1:patient.NOK.%Size()-1 { // now we loop over all NOK items
set nok = patient.NOK.%Get(j)
write nok.NOKname,!
write nok.NOKrelationship,!
}
}So I hope, you see the light at the end of the tunnel...
According to above sample data, your JSON-Data (pData) is :
- an array of one or more objects
- the object(s) have "patient", "visit" and "documentAttachments" as properties
- the "patient" property is an object and has "guid", "id", "forename", "surname", and "dateOfBirth" as properties
So, to get the "forename" property, you have to do something like:
set object = {}.%FromJSON(pData) // create a JSON-Object from JSON string
write object.%Get(0).patient.forename // or step by step
set firstArrayItem = object.%Get(0) // first array item is 0
set patient = firstArrayItem.patient // this is the patioent object
write patient.forename // now you have the name
write patient.surname // and other propertiesBy the way, the "visit" property is also an array! To list all the "reason" properties, you have to write a loop:
set visit = firstArrayItem.visit // the first visit
for i=0:1:visit.%Size()-1 {
write visit.%Get(i).reason,!
}Your output will be:
other
routineI hope, things are now more clear?
The problem is obvious, your set object = ##class(%DynamicAbstractObject).%FromJSON(pData) won't work, because pData contains an invalid JSON!
1) "visit":[{"guid":45678" <-- surplus QUOTE-char, maybe a typo
2) reason":"other"} {"guid":45679 missing comma between } and {
3) "catergory":"notes"} {"guid":"23433" missing comma between } and {
You can use https://jsonlint.com/ to check your JSON string.
The above should work, so the question is, what is the content of pData. Do you can share a sample with us?
Also, you can write the above somewhat shorter too and you can access all the properties either via propertyname or via the %Get() method.
I assume, you have a string like this:
set pData="{""Name"":""Joe"", ""Age"":50}"the you can do this
set object={}.%FromJSON(pData)
write object.Name // Joe
write object.%Get("Name") // Joe
write pData // {"Name":"Joe", "Age":50}