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! 🙂