Question
· Sep 27, 2022

Strings Longer than "Long Strings"...

Hello!

Am after some advice on how to approach an issue I am currently having with one of our interfaces within HealthShare HealthConnect.

We send documents to a third party system via their API. 

The API contains a string property, which you use to pass in the document metadata and the Base64 encoded document itself.

Property rsXML As %String;

However, a few times a day we are getting errors as some documents exceed the ‘long string’ length within HealthShare for Strings.

Is anyone able to please advise how I should be passing this through?  The supplier has confirmed they can take larger documents, so am trying to work out how I pass these through?  Am playing around with changing the Request object property to a Stream but not having much joy?

Product version: HealthShare 2018.1
Discussion (18)2
Log in or sign up to continue

I have nothing in common with HealtShare... but a property (Property rsXML As %String), like you mentioned, can hold up to 3.6E6 chars. If your data are longer then 3641144 characters then you definitely need to use streams (and if you intend to Base64 encode the data, then your data will be 33% longer)! You need to change the above property to something like

Property rsXML As %GlobalBinaryStream;

BUT this means, you have to adapt all your programs, methods, etc. where this property is in use!

That won't be a joy rather a pain

The %Library.DynamicArray and %Library.DynamicObject class objects are stored only in process memory and are not affected by the 3641144 character string length of ObjectScript strings.  But they are affected by the amount of virtual memory that the operating system will grant to a process so avoid having many such large things active.  These Dynamic Objects generally hold JSON format data but they can also hold ObjectScript values.  The %ToJSON(output) method can change the contents of Dynamic Object into JSON text and it will send that text to 'output', which can be a file, a device or a %Stream.  If you need to persist a Dynamic Object in a database then you can use %ToJSON(globalstream) to persist the JSON text in a %Stream.GlobalCharacter or %Stream.GlobalBinary class object.  There is a %FromJSON(input) which can build a new Dynamic Object from 'input' which is JSON text from a string, a file, a device or a %Stream.  In recent versions of IRIS, the %Get(key,default,type) and %Set(key,value,type) methods can let you choose 'type' as some form of %Stream so you can examine and modify elements of a Dynamic Object when that element needs to be larger than 3614411 characters.  The default %Stream-s generated by the %Get method use the %Stream.DynamicBinary and %String.DynamicCharacter classes, which also store data in process memory so you should avoid having many such large streams active at the same time.  They can be copied out to other streams which can be stored a database or into a file or a device.

Yes, it would

kill
set $p(data,"x",2E6+1)=""
set dynArr=[].%Push(data).%Push(data).%Push(data)
set stream=##class(%Stream.GlobalCharacter).%New()
do dynArr.%ToJSON(stream)
try { set x=dynArr.%ToJSON() } catch e { write e.Name,! }
write stream.Size,!
write $length(data),!

The output is

<MAXSTRING>
6000010
2000000

As Steven Hobbs  wrote, the limit is only set by the size of the (virtual) memory.

Does not work with dynamic object class properties unfortunately:

Class User.DO Extends %Persistent
{

Property MyProp As %DynamicObject;

/// do ##class(User.DO).Test()
ClassMethod Test()
{
    do ..%KillExtent()
    // 11 = $length({"prop":""}.%ToJSON())
    for len = 100, $$$MaxStringLength - 11, $$$MaxStringLength - 11 + 1 {
        set sc = ..Create(len)
        write "len: ", len, " result: ", $case($$$ISOK(sc), $$$YES: "OK", : "ERROR: " _ $system.Status.GetErrorText(sc)), !
        quit:$$$ISERR(sc)
    }
}

ClassMethod Create(len) As %Status
{
    set obj = ..%New()
    do obj.MyProp.%Set("prop", ..GetStream(len), "stream")
    set sc = obj.%Save()
    quit sc
}

ClassMethod GetStream(len) As %Stream.TmpCharacter
{
    set chunk = 1000000
    set stream = ##class(%Stream.TmpCharacter).%New()
    for i=1:chunk:len-chunk {
        do stream.Write($tr($j("", chunk)," ", 0))		
    }
    do stream.Write($tr($j("", len#chunk)," ", 0))
    quit stream
}

}

Results in:

len: 100 result: OK
len: 3641133 result: OK
len: 3641134 result: ERROR: ERROR #5002: ObjectScript error: <MAXSTRING>%GetSerial+1^%Library.DynamicAbstractObject.1 [%GetSerial+1^%Library.DynamicAbstractObject.1:XF]

Same issue if there are several short properties in dynamic object with total length > 3641144 characters.

We need something like:

Property MyProp As %DynamicObject(STORAGE="stream");