Expressions

Expressions can be used to access variables and calculate values dynamically.

The following attributes of BPMN elements require an expression:

Additionally, the following attributes of BPMN elements can define an expression optionally instead of a static value:

Expressions vs. Static Values

Some attributes of BPMN elements, like the timer definition of a timer catch event, can be defined either as a static value (e.g. PT2H) or as an expression (e.g. = remaingTime).

The value is identified as an expression if it starts with an equal sign = (i.e. the expression prefix). The text behind the equal sign is the actual expression. For example, = remaingTime defines the expression remaingTime that accesses a variable with the name remaingTime.

If the value doesn't have the prefix then it is used as 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.

Note that 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:

  • Side-effect free
  • 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)

Zeebe 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 the project's documentation.

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 then it is handled as a FEEL context. A property of the context (aka nested variable property) can be accessed by . (a dot) 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 equal 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 _ and _ same as (x >= _ and x <= _) 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

If a variable or a nested property can be null then 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

totalCount > 5
// false is totalCount is null

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 functions (e.g. contains, matches, etc.).

Temporal Expressions

The following operators can be applied on temporal values:

Temporal Type Examples Operators
date date("2020-04-06")
  • date + duration
  • date - date
  • date - duration
  • time time("15:30:00"),
    time("15:30:00+02:00"),
    time("15:30:00@Europe/Berlin")
  • time + duration
  • time - time
  • time - duration
  • 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")
  • date-time + duration
  • date-time - date-time
  • date-time - duration
  • duration duration("P12H"),
    duration("P4Y")
  • duration + duration
  • duration + date
  • duration + time
  • duration + date-time
  • duration - duration
  • date - duration
  • time - duration
  • date-time - duration
  • duration * number
  • duration / duration
  • duration / number
  • 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, it 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 then 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 then 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

    FEEL defines a set of built-in functions to convert values and to apply different operations on specific value types in addition to the operators.

    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
    

    Additional Resources

    References: