Script Variables¶
Script variables are named holders for arbitrary Java objects that are bound by name into the channel's JavaScript scripts. They are bound the same way the source and message objects are bound, so a script variable named patientLookup is referenced in JavaScript as the bare identifier patientLookup, with no .getValue() or wrapper call.
Which scripts see a given variable depends on its scope:
| Script type | Channel-scoped variables | Message-scoped variables |
|---|---|---|
| Mapping, condition, destination | Yes | Yes |
| Scheduled | Yes | No, because scheduled scripts run outside the context of a single message |
| Preprocessor | No | No |
Each script variable has a name and a scope. The variable definition (name, scope, description) is persisted with the channel; the value is in-memory only and is always null at the start of channel processing until a script assigns to it.
Scope¶
| Scope | Lifetime | Intended use |
|---|---|---|
| Channel | One holder shared across all messages processed by the channel. Lives from channel start until the channel stops. | Static or almost-static reference data (code crosswalks, lookup tables, parsed configuration) loaded once and shared by every message. |
| Message (default) | One holder per message; persists across every node the message visits, including condition and destination nodes downstream of the node that set it. | Per-message scratchpad, typically a Message Model so downstream nodes can read fields directly without re-parsing a cached string. |
Note
Channel-scoped variables are always null after a channel restart because the value is not persisted. Initialize channel-scoped variables in a scheduled script with a Channel Start trigger so they are populated before any message is processed.
Why script variables and not messageCache?¶
messageCache stores string values. When a Message Model is stored in messageCache, it is serialized to a string and must be re-parsed on every read to access fields:
// Node A — store the query result as a string in messageCache
var csvMessageModel = qie.doQuery("lookupDB",
"SELECT first_name, last_name, dob FROM patient WHERE mrn = ?",
[message.getNode("PID-3")]);
messageCache.setValue("patientLookup", csvMessageModel.toString());
// Node B — re-parse the cached string before each read
var patientLookup = qie.parseCSVString(
messageCache.getValue("patientLookup"), true, '"', ',', true);
message.setNode("PID-5.1", patientLookup.getNode("last_name[1]"));
A script variable holds the live Message Model itself, so no re-parsing is required:
// Mapping Node A — populate a Message-scoped script variable named
// patientLookup (configured on the channel's Source tab)
patientLookup = qie.doQuery("lookupDB",
"SELECT first_name, last_name, dob FROM patient WHERE mrn = ?",
[message.getNode("PID-3")]);
// Mapping Node B (downstream of Node A on the same message) —
// read nodes directly off the held Message Model
message.setNode("PID-5.1", patientLookup.getNode("last_name[1]"));
message.setNode("PID-5.2", patientLookup.getNode("first_name[1]"));
message.setNode("PID-7", patientLookup.getNode("dob[1]"));
Channel-scoped example¶
Channel-scoped variables are intended for reference data that all messages share. Initialize them in a scheduled script with a Channel Start trigger so the value exists before the first message is processed, and so concurrent message threads do not race to populate it:
// Scheduled script (trigger: Channel Start)
// Channel-scoped script variable: codeCrosswalk
codeCrosswalk = qie.doQuery("lookupDB",
"SELECT external_code, internal_code FROM code_crosswalk");
Every mapping node in the channel can then read from the shared Message Model without an additional database round trip:
// Mapping node — read from the channel-scoped crosswalk Message Model
var externalCode = message.getNode("OBX-3.1");
// ...locate the row whose external_code matches externalCode and read internal_code...
Fan-out behavior¶
When a message fans out to multiple branches, each branch gets its own holder for the script variable, but the underlying Java object the holder points to is shared by reference. The practical consequences:
| Operation on branch B1 | Effect on branch B2 |
|---|---|
myVar = somethingNew (reassignment) |
None. B2 still sees the value that was set before the fan-out. |
myVar.setNode(...), myMap.put(...), myList.add(...) (mutation of the held object) |
B2 sees the mutation, because both branches reference the same Java object. |
In practice, treat the held value as read-only after the fan-out point. If a downstream branch needs a different value, reassign the variable (e.g. parse a fresh Message Model and assign it) instead of mutating the existing object.
Immutable values (Strings, numbers, or Message Models you only read from via .getNode(...)) are unaffected by this, the fan-out concern applies only when scripts mutate the held object.
When to use Script Variable vs. other channel state¶
| Mechanism | Holds | Scope | Persisted? | Thread-safe? | Best for |
|---|---|---|---|---|---|
| Script Variable (Channel) | Any Java object | Channel | Definition only, value is in-memory | No | Read-only reference data initialized once at channel start. |
| Script Variable (Message) | Any Java object | Single message, across all nodes | No | N/A (single message thread) | Holding a Message Model so downstream nodes can call .getNode(...) without re-parsing. |
| Channel Cache | String key → value | Channel | Yes | No | Slowly-changing configuration that must survive restarts. |
| Message Cache | String key → string value | Single message | Discarded after processing (unless Save message cache for each message is enabled) | N/A | Simple per-message scratchpad for string values. |
| Shared Cache | Any object | Channel, zone, or global | Optional (via persist parameter) |
Yes | Cross-channel state, cached lookups with a TTL, mutex locks. |
| System Variable | Typed value (String, Number, Date, Map, etc.) | Zone or global | Yes | N/A | User-managed reference values like credentials, lookup tables, environment-specific settings. |
