Expressions
Expressions can be used to access variables and calculate values dynamically.
This is particularly useful when automating a process using BPMN and orchestrating human tasks.
The following attributes of BPMN elements require an expression:
- Sequence flow on an exclusive gateway: condition
- Message catch event/receive task: correlation key
- Multi-instance activity: input collection, output element
- Input/output variable mappings: source
Additionally, the following attributes of BPMN elements can define an expression optionally, instead of a static value:
- Timer catch event: timer definition
- Message catch event/receive task: message name
- Service task/business rule task/script task/send task: job type, job retries
- User task: assignee, candidateGroups
- Call activity: process id
Expressions vs. static values​
Some attributes of BPMN elements—like the timer definition of a timer catch event—can be defined in one of two ways:
- As an expression (e.g.
= remainingTime
) - As a static value (e.g.
PT2H
)
Expressions always start with an equals sign (=). For example, = order.amount > 100
. The text following the equal sign is the actual expression. For example, order.amount > 100
checks if the amount of the order is greater than 100.
If the element does not start with the prefix, it is used as a static value. A static value is used either as a string (e.g. job type) or as a number (e.g. job retries). A string value must not be enclosed in quotes.
An expression can also define a static value by using literals (e.g. = "foo"
, = 21
, = true
, = [1,2,3]
, = {x: 22}
, etc.)
The expression language​
An expression is written in FEEL (Friendly Enough Expression Language). FEEL is part of the OMG's DMN (Decision Model and Notation) specification. It is designed to have the following properties:
- Free of side effects
- Simple data model with JSON-like object types: numbers, dates, strings, lists, and contexts
- Simple syntax designed for business professionals and developers
- Three-valued logic (true, false, null)
Camunda 8 integrates the FEEL Scala engine to evaluate FEEL expressions. The following sections cover common use cases in Zeebe. A complete list of supported expressions can be found in FEEL expressions.
Access variables​
A variable can be accessed by its name:
owner
// "Paul"
totalPrice
// 21.2
items
// ["item-1", "item-2", "item-3"]
If a variable is a JSON document/object, it is handled as a FEEL context. A property of the context (e.g. nested variable property) can be accessed by a period (.
) and the property name:
order.id
// "order-123"
order.customer.name
// "Paul"
Boolean expressions​
Values can be compared using the following operators:
Operator | Description | Example |
---|---|---|
= (only one equals sign) | equal to | owner = "Paul" |
!= | not equal to | owner != "Paul" |
< | less than | totalPrice < 25 |
<= | less than or equal to | totalPrice <= 25 |
> | greater than | totalPrice > 25 |
>= | greater than or equal to | totalPrice >= 25 |
between [X] and [Y] | same as (v >= [X] and v <= [Y]]) | totalPrice between 10 and 25 |
Multiple boolean values can be combined as disjunction (and
) or conjunction (or
):
orderCount >= 5 and orderCount < 15
orderCount > 15 or totalPrice > 50
Null checks​
If a variable or nested property can be null
, it can be compared to the null
value. Comparing null
to a value different from null
results in false
.
order = null
// true - if "order" is null or doesn't exist
order.id = null
// true - if "order" is null, "order" doesn't exist,
// "id" is null, or "order" has no property "id"
In addition to the comparison with null
, the built-in function is defined()
can be used to differentiate between a value that is null
and a value that doesn’t exist.
is defined(order)
// true - if "order" has any value or is null
is defined(order.id)
// false - if "order" doesn't exist or it has no property "id"
String expressions​
A string value must be enclosed in double quotes. Multiple string values can be concatenated using the +
operator.
"foo" + "bar"
// "foobar"
Any value can be transformed into a string value using the string()
function.
"order-" + string(orderId)
// "order-123"
More functions for string values are available as built-in string functions (e.g. contains, matches, etc.)
Temporal expressions​
The current date and date-time can be accessed using the built-in functions today()
and now()
. To store the current date or date-time in a variable, convert it to a string using the built-in function string()
.
now()
// date and time("2020-04-06T15:30:00@UTC")
today()
// date("2020-04-06")
string(today())
// "2020-04-06"
The following operators can be applied on temporal values:
Temporal Type | Examples | Operators |
---|---|---|
date | date("2020-04-06") | |
time | time("15:30:00"), time("15:30:00+02:00"), time("15:30:00@Europe/Berlin") | |
date-time | date and time("2020-04-06T15:30:00"), date and time("2020-04-06T15:30:00+02:00"), date and time("2020-04-06T15:30:00@UTC") | |
duration | duration("PT12H"), duration("P4Y") | |
cycle | cycle(3, duration("PT1H")), cycle(duration("P7D")) |
A temporal value can be compared in a boolean expression with another temporal value of the same type.
The cycle
type is different from the other temporal types because it is not supported in the FEEL type system.
Instead, the cycle
type is defined as a function that returns the definition of the cycle as a string in the ISO 8601 format of a recurring time interval.
The function expects two arguments: the number of repetitions, and the recurring interval as duration. If the first argument is null
or not passed in, the interval is unbounded (i.e. infinitely repeated).
cycle(3, duration("PT1H"))
// "R3/PT1H"
cycle(duration("P7D"))
// "R/P7D"
List expressions​
An element of a list can be accessed by its index. The index starts at 1
with the first element (not at 0
).
A negative index starts at the end by -1
. If the index is out of the range of the list,null
is returned instead:
["a","b","c"][1]
// "a"
["a","b","c"][2]
// "b"
["a","b","c"][-1]
// "c"
A list value can be filtered using a boolean expression; the result is a list of elements that fulfill the condition.
The current element in the condition is assigned to the variable item
:
[1,2,3,4][item > 2]
// [3,4]
The operators every
and some
can be used to test if all elements or at least one element of a list fulfill a given condition:
every x in [1,2,3] satisfies x >= 2
// false
some x in [1,2,3] satisfies x > 2
// true
Invoke functions​
A function can be invoked by its name followed by the arguments. The arguments can be assigned to the function parameters either by their position or by defining the parameter names:
floor(1.5)
// 1
count(["a","b","c"])
// 3
append(["a","b"], "c")
// ["a","b","c"]
contains(string: "foobar", match: "foo")
// true
FEEL defines several built-in functions:
- Conversion functions
- Boolean functions
- String functions
- Numeric functions
- List functions
- Context functions
- Temporal functions