Written by

👨‍💻 InterSystems IRIS Specialist
Article Andrew Sklyarov · 4 hr ago 4m read

IS ObjectScript Tips & TricksContestant

Hey Community, 

Yeah, I know we have a lot of tips & tricks articles - we even have a special tag for it, right? But I can not share my own collection. In this collection, you can find snippets for beginners, as well as some everyday ObjectScript constructions. In fact, it's also a way to learn something new about my favorite programming language. So, if you guys have anything to add, you are welcome!

 

1) Returning an error using format text:

Return $$$ERROR($$$GeneralError, $$$FormatText("Failed to open message with ID = %1", id))

2) Error check when we use Embedded SQL (&sql()):

Return:SQLCODE<0 $$$ERROR($$$GeneralError, $$$FormatText("Operation failed. SQLCODE = %1: %2", SQLCODE, $SYSTEM.SQL.Functions.SQLCODE(SQLCODE)))

3) Emptiness check that covers any null-like values:

If $ASCII(object.field)>0 {
    // Not empty
}

Here is a need to explain, I believe. For example, external systems via SOAP protocols can return null as $CHAR(0). In Cache null, it is an empty string, but checking for an empty string will not work correctly. This expression is a single-line way to solve this problem.

4) Auto-populated record update timestamp in a table:

Property UpdatedAt As %TimeStamp [ SqlComputeCode = { Set {*} = $ZDATETIME($NOW(), 3) }, SqlComputed, SqlComputeOnChange = (%%INSERT, %%UPDATE) ];

5) Reading a value from LookupTable (named Settings):

Set value = $GET(^Ens.LookupTable("Settings", "my_key"))

6) Writing a value to LookupTable (named Settings):

Set tSC = ##class(Ens.Util.LookupTable).%UpdateValue("Settings", "my_key", value)

7) Importing Python packages:

XData %import [ MimeType = application/python ]
{
import iris
from confluent_kafka import Consumer, KafkaException, TopicPartition, KafkaError
}

8) Returning IRIS statuses from Python:

return iris.system.Status.OK()
return iris.system.Status.Error(5001, f'Request body is invalid: {e}')

9) Tracing in Python code:

Method Trace(message As %String)
{
    $$$TRACE(message)
}

Method PyImpl() [ Language = python ]
{
    self.Trace(f'Received message: {input.value}')
}

10) Converting JSON array (%DynamicArray) to a comma-separated string and back:

Set jsonArray = ["test1","test2","test3"]
Set str = $TRANSLATE(jsonArray.%ToJSON(), "[]")
Set jsonArray = [].%FromJSON("["_str_"]")

11) Formatting JSON:

Do ##class(%JSON.Formatter).%New().FormatToString({"test":"test"}, .formatted)

12) Converting %Status into exception and back for Try/Catch:

try {
    $$$ThrowOnError(object.%Save())
} catch ex {
    Return ex.AsStatus()
}

13) Writing a $$ERROR into the Application Error Log (when we need a "silent" exception):

Do ##class(%Exception.StatusException).CreateFromStatus(tSC).Log()

14) Normalizing an input string:

Set value = $ZSTRIP(value, "<>WC")

15) One way to iterate over a %DynamicArray:

Set it = array.%GetIterator()
While it.%GetNext(.key, .value) {
    Write !, key, ":", value
}

16) One way to execute a SQL query:

Set rs = ##class(%SQL.Statement).%ExecDirect(, "SELECT ID, Name FROM MyTable WHERE Status = ?", status)
Return:rs.%SQLCODE<0 $$$ERROR($$$GeneralError, $$$FormatText("Execute failed. SQLCODE = %1: %2", rs.%SQLCODE, rs.%Message))
While rs.%Next() {
    Write !, rs.ID_", "_rs.Name
}

17) Another way to execute a SQL query (using cursors):

&sql(DECLARE C1 CURSOR FOR SELECT ID, Name INTO :id, :name FROM MyTable WHERE Status = :status)
&sql(OPEN C1)
Return:SQLCODE $SYSTEM.Error.FromSQLCode(SQLCODE, %msg).Status
&sql(FETCH C1)
While SQLCODE = 0 {
    Write !, $$$FormatText("id = %1, name = %2", id, name)       
    &sql(FETCH C1)
}
&sql(CLOSE C1)

18) The third way to execute an SQL query (using SQL functions):

Query Test(status As %String) As %SQLQuery [ SqlProc ]
{
    SELECT ID, Name FROM MyTable WHERE Status = :status
}

ClassMethod TestRun()
{
    Set rs = ..TestFunc("Active")
    While rs.Next() {
        Write !, rs.ID_", "_rs.Name
    }
}

These are the main three, but there are other ways to execute a SQL query. I won't list them all here, let's move on.

19) Running code in a single instance in whichever process gets there first (locking):

Lock +^MyLock(id):0
Return:'$TEST $$$OK
Try {
    // Parallel execution is prohibited for this block
} Catch ex {
    Set tSC = ex.AsStatus()
}
Lock -^MyLock(id)

20) $CASE or $SELECT instead of an If/Else chain:

Set label = $CASE(status, "A":"Active", "I":"Inactive", "P":"Pending", :"Unknown")
Set label = $SELECT(status="A": "Active", status="I": "Inactive", status="P":"Pending", 1:"Unknown")

21) Temporary (process-private) global:

Set ^||Temp(1) = "Will be removed automatically when the process ends"

22) Adding leading zeros:

Return $REPLACE($JUSTIFY(docNumber,4)," ",0)

23) Setting a Numeric field in a JSON object:

Set json = {"id":(+value)}

24) Setting a Boolean field in a JSON object:

Do json.%Set("active", isActive, "boolean")

25) Alternative to AND (&&) in conditions:

If '$ISOBJECT(obj),obj.prop'="" {
    Write !, obj.prop
}

26) Iterating over an array (same for the Globals):

Set node = ""
For {
    Set node = $ORDER(array(node))
    Quit:node=""
    Set subnode = ""
    For {
        Set subnode = $ORDER(array(node,subnode))
        Quit:subnode=""
        Write !, "array(", "node, ",", subnode, ") = ", array(node,subnode)
    } 
}

27) Dynamic global name:

Set MyGlobal = "^Sample.Data"
Write !, $GET(@MyGlobal@(node))

28) Your turn! 🙂