Skip to Content
v0.9.4: UI Improvements + Interactive Relationship Graph Download
DocumentationCEL Labels Guide

CEL Labels Guide

Labels in OCPQ currently use the Common Expression Language (CEL), to allow computing arbitrary labels (e.g., KPIs) based on the query result. Most notably, CEL is not turing complete and is designed to be simple, fast, and safe.

See https://cel.dev/ for general information on CEL, there is also a language definition with many more details. OCPQ currently uses the cel-rust software library.

Labels can be added using the plus icon + next to the Labels section of an OCPQ node.

CEL scripts can also be used as filters or constraints, as long as they return a boolean value.

Below, we first give a brief introduction to CEL in OCPQ, before going into more details on the available functionality.

Introduction / Cheat Sheet

When using CEL inside OCPQ, the CEL script is evaluated for each queried binding. CEL can thus be used to add labels, but also as filter or constraint predicates, returning a boolean.

The values of all event and object variables of the current binding are available as variable values. For instance, if an object variable o1 is present in the binding o1 can be used in the CEL script, using the OCPQ-specific functionality described below.

Next, we cover the basic primitives and functionality of CEL.

Types

Values can be int, double, bool, string, timestamp, duration, or a list or map of values.

Type Conversion

To construct or convert a value of a type, use the associated function with the same name.

  • int('4') == 4 or string(1) == '1',
  • duration('80s') <= duration('1m30s')
  • timestamp('2024-01-01T12:30:00+00:00') <= timestamp('2024-01-02T12:30:00+00:00')

Function Calling: For the type conversion functions, there is no difference between '4'.int() and int('4').

Lists

List literals are written as [1,2,3]. There are some common functions for lists:

  • [1,2,3].size() == 3, [1,2,3].contains(3), [1,2,3].max() == 3 and [1,2,3].min() = 1
  • [1,2,3].sum() == 6 and [1,2,3].avg() == 2
  • [1,2,3].filter(x,x>1) == [2,3]. Notice, that the first argument of the filter function defines the variable identifier running through the list.
  • Similiarly, [1,2,3].map(x,2*x) == [2,4,6].

Maps

Map literals are written as {'a': 1, 'b': 2}. To access one specific fields, [key] is used, e.g., {'a': 1, 'b': 2}['b'] == 2.

OCEL / OCPQ Specifics

There are number of helper functions implemented to make CEL more powerful when working with OCEL. Each bound variable (e.g., o1, o2, and e1) is available as a variable value and can be used to access further properties of the bound value. For instance, the timestamp of the event bound to e1 can be accessed using e1.time(), the object type of o1 as o1.type(). Event and object attributes can be accessed using the attr or attrs functions (and also attrAt for object variables). For example, the price of an order object bound to o1 might be accessed using o1.attr('price').

Advanced CEL Scripts

In addition to event and object variables, the Advanced CEL Script version can also access child bindings sets (e.g., A), as the list of child bindings that are returned by a subquery. The value of A is then simply a list of all child bindings, and each entry of that list is a map, assigning the name of the child variables (e.g., 'o4') to a concrete object or event value. Moreover, the key 'satisfied' is contained in the child binding map, yielding whether that child binding is satisfied or violated. Labels are always advanced CEL scripts, and can access all values of labels occuring before it (i.e., are on top of it).

As an example, assume A refers to a subquery for all order objects o2 for a customer. Then the total sum of all orders by the customer can be calculated using A.map(b,b['o2'].attr('price')).sum().

Values & Types

CEL supports the following value types:

  • int
  • double
  • bool
  • string
  • list
  • map
  • timestamp
  • duration

All of the types can also be used to parse or convert another value into value of this type (e.g., int("5") == 5). See the general functions below for examples

General Functions

string

string (type conversion) Converts a value into a string.

Parameters:

  • arg: value : The value to convert to string.

Examples:

  • string(4.0) == '4'
  • string(4.50) == '4.5'
  • string(e1.time()) == '2023-04-04T14:09:41+00:00'

double

double (type conversion)

Converts a value into a double (i.e., floating point number).

Parameters:

  • arg: value : The value to convert to double.

Examples:

  • double('4') == 4.0
  • double('4.50') == 4.5
  • double(4) == 4.0

int

int (type conversion)

Converts a value into an int (i.e., integer number). The argument is floored (i.e., rounded down towards zero), if necessary.

Parameters:

  • arg: value : The value to convert to int.

Examples:

  • int('4') == 4.0
  • int(4.50) == 4
  • int(1.8) == 1

duration

duration (type conversion)

Parses a duration from a given string. As units, combinations of h (hour), m (minute), s (seconds) are supported.

Parameters:

  • arg: string : The value to convert to a duration.

Examples:

  • duration('80s') <= duration('1m30s')
  • duration('12h45m30s') <= duration('13h')

timestamp

timestamp (type conversion)

Parses a timestamp from a given string. The string must be in RFC3339 format.

Parameters:

  • arg: string : The value to convert to a timestamp.

Examples:

  • timestamp('2024-01-01T12:30:00+00:00') <= timestamp('2024-01-02T12:30:00+00:00')
  • e1.time() >= timestamp('2024-01-01T15:30:00+00:00')

startsWith

Checks if a string starts with a given prefix.

Examples:

  • 'abc'.startsWith('a') == true
  • 'abc'.startsWith('ac') == false

endsWith

Checks if a string ends with a given postfix. Examples:

  • 'abc'.endsWith('c') == true
  • 'abc'.endsWith('cb') == false

size

Returns the size/length of a string or list.

Parameters:

  • arg: string|list : The string or list to measure the size of.

Examples:

  • size('456') == 3
  • size([]) == 0

matches

Test if a string matches a regular expression.

Parameters:

  • regex: string : The regex to test for matches.

Examples:

  • 'abcd'.matches('abc?') == true
  • '^abc$'.matches('abcd') == false

contains

Returns if a string, list or map contains the provided value as a substring/element/key.

Parameters:

  • arg: value : The value to look for.

Examples:

  • 'test123'.contains('test') == true
  • ['test123','test456','test789'].contains('test456') == true

has

Checks if the argument function can be resolved.

Parameters:

  • property: value : The property to check for.

Examples:

  • has(o1.attr) == true
  • has(o1.nonExistingFunc) == false

map

Maps entries of a list according to the provided expression, producing a new list.

Parameters:

  • variable: identifier : The variable identifier running through the list. For instance, x or i.
  • expr: expression : The expression on which to map this entry. For instance, 2 * x.

Examples:

  • [1,2,3].map(x, 2*x) == [2,4,6]

filter

Filters entries of a list according to the provided filter expression, producing a new list.

Parameters:

  • variable: identifier : The variable identifier running through the list. For instance, x or i.
  • expr: expression : The filter expression which determines if a list item is retained. For instance, x >= 3.

Examples:

  • [1,2,3,4].filter(x, x>=3) == [3,4]

all

Returns if all entries of the list satisfy the filter expression.

Parameters:

  • variable: identifier : The variable identifier running through the list. For instance, x or i.
  • expr: expression : The filter expression which determines if a list item is satisfied. For instance, x >= 3.

Examples:

  • [3,4,5].all(x, x>=3) == true

exists

Returns if at least one entry of the list satisfies the filter expression.

Parameters:

  • variable: identifier : The variable identifier running through the list. For instance, x or i.
  • expr: expression : The filter expression which determines if a list item is satisfied. For instance, x >= 3.

Examples:

  • [3,4,5].exists(x, x>=5) == true

max

Returns the maximum value of either all provided arguments or, if the first argument is a list, the maximum value in this list.

Parameters:

  • arg1: value|list : Either a single value or a list of values.
  • ...args: value : If arg1 is a single value, args are the other passed values.

Examples:

  • max([3,4,5]) == 5
  • max(3,4,5) == 5

min

Returns the minimal value of either all provided arguments or, if the first argument is a list, the minimal value in this list.

Parameters:

  • arg1: value|list : Either a single value or a list of values.
  • ...args: value : If arg1 is a single value, args are the other passed values.

Examples:

  • min([3,4,5]) == 3
  • min(3,4,5) == 3

sum

Sums up all entries of a list. Integer and floats are considered while other values are handled as 0.

Examples:

  • [3,4,5].sum() == 12

avg

Computes the average of all entries in a list. Integer and floats are considered while other values are handled as 0.

Examples:

  • [3,4,5].avg() == 3

OCPQ Specific Functions

time

Retrieve the timestamp of an event.

Example:

  • e2.time() - e1.time() <= duration('24h')

type

Retrieve the type of an object/event.

Example:

  • o1.type() == 'orders'

id

Retrieve the id of an object/event.

Example:

  • o1.id() == 'order-91230'

attr

Retrieve an object/event attribute value.

Parameters:

  • attr_name: string : The name of the attribute.

Example:

  • o1.attr('price') >= 100

For objects, the first encountered attribute value is picked, regardless of the associated timestamp. Use attrAt to retrieve the attribute value of an object at a specific timestamp.


attrs

Retrieves all attributes of an event or object. Returns a list containing all attributes represented as lists of size 3: name, value, timestamp. The timestamp is only present for object attributes, otherwise it is set to null.

Examples:

  • e1.attrs() == [['resource',1000.0,null]]
  • o1.attrs().filter(x,x[0] == 'price').all(x,x[1] >= 100)

attrAt

Retrieve an object attribute value.

Parameters:

  • attr_name: string : The name of the attribute.
  • at_time: timestamp : The timestamp on which to retrieve the current attribute value.

Examples:

  • o1.attr('price',e1.time()) >= 100
  • o1.attr('price',timestamp('2024-01-01T12:30:00+00:00')) >= 50.0

The latest recorded attribute value before or at the specified timestamp is selected. See attr for also retrieving attributes of events.


numEvents

Retrieves the total number of events in the loaded OCEL.

Example:

  • numEvents() >= 1000

numObjects

Retrieves the total number of objects in the loaded OCEL.

Example:

  • numObjects() >= 1000

events

Retrieves a list of all events in the loaded OCEL.

Example:

  • events().all(e,e.time() >= timestamp('2020-01-01T00:00:00+00:00'))

objects

Retrieves a list of all objects in the loaded OCEL.

Example:

  • objects().all(e,e.attr('price') >= 100)
Last updated on