Custom Expression Engine Function
Functions available as part of the expression engine can be used in various layers in the C3 Agentic AI Platform including stored calculated fields, read calculated fields, canonical transform expressions, and metric expressions. You can also add a user-defined function when one is not available in the expression engine library.
Define a custom expression engine function
To define a custom expression engine function, you need to:
Create a custom function Type.
Declare a custom function.
Implement the logic within the custom function.
All expression engine functions used in stored calc and calc fields must be pure functions (see Pure function). When used in stored calc or calc fields, expression engine functions are computed in worker thread context as the worker system user, which means they cannot access user-specific config or preferences.
If you plan to use a custom expression engine function in stored calc or calc fields, do not define functions that:
- Access user preferences or configs (e.g.,
UiSdlUserPreference.inst().getConfig().configValue('preferredLocale')) - Depend on user context or session data
- Have side effects or rely on external state
Using such functions in stored calc or calc fields will result in incorrect or missing data, as configs and user preferences won't contain information specific to the user executing the action.
Create a custom function type
Create a Type that mixes ExpressionEngineFunction. For example, create the Type CustomFunction in the file CustomFunction.c3typ and mixin the ExpressionEngineFunction Type:
type CustomFunction mixes ExpressionEngineFunctionThe CustomFunction Type can now use functions from the ExpressionEngineFunction Type, such as, trim, isInfinity, or exponentialDecay.
Declare the custom functions
Declare the custom function under the Type that mixes ExpressionEngineFunction. For example modify the CustomFunction.c3typ Type as follows if the function is implemented in JavaScript:
type CustomFunction mixes ExpressionEngineFunction {
customFunc: function(a: int, b: double) : Timeseries js-server
}Modify the CustomFunction Type to also indicate customFunc is implemented in Python:
type CustomFunction mixes ExpressionEngineFunction {
customFunc: function(a: int, b: double) : Timeseries py-server
}Inside the body of the CustomFunction Type where the method customFunc is defined it is worth noting the difference in the properties. The python implementation uses py-server and the JavaScript version uses js-server.
Implement the logic for the custom function
Implement the logic for the custom function defined in the CustomFunction Type.
A JavaScript and Python implementation of the custom function is below.
function customFunc(a, b) {
var result = SourceType.evalMetric({
id: "foo",
start: "2010-01-01",
end: "2011-01-01",
interval: "DAY",
expression: "exponentialDecay(randomTimeseries(), randomTimeseries() > 0.9, " + a + ", " + b + ")"
});
return result.data();
}Call a custom expression engine function
Below are several examples of how the custom expression function above, customFunc, can be called.
In a stored calculated field:
entity type SourceType mixes MetricEvaluatable {
x: int
y: double
z: Timeseries stored calc "customFunc(x, y)"
...
}In a read calculated field:
entity type SourceType {
x: int
y: double
z: Timeseries calc "customFunc(x, y)"
...
}In a canonical transform expression:
type TransformCanonicalSourceTypeToTargetType mixes TargetType transforms CanonicalSourceType {
z: ~ expression "customFunc(x, y)"
...
}Where:
type CanonicalSourceType {
x: int
y: double
...
}And:
entity type TargetType {
z: Timeseries
...
}You do not need to create a custom JavaScript transform anymore. Prefer using a custom expression engine function.
In a metric expression:
{
"id": "CustomMetric_SourceType",
"name": "CustomMetric",
"srcType": "SourceType",
"expression": "window('AVG', customFunc(x, y), -10, 10)"
}Alternatively you can mixin the MetricFunctionLibrary Type to create custom expression engine functions. However, those functions can only be available in metric expressions and take the Timeseries Type as input (and optional other parameters) to return a Timeseries Type as output.
Debug a custom expression engine function
A custom expression engine function is declared by a method so all the debugging tips related to methods also apply.
Because custom expression engine functions are implemented by custom methods (in Python or JavaScript), it is not optimized and it might run much slower than functions available in the expression engine library.