Custom Expressions
Overview
Custom Expressions in the MetaRouter platform are powered by Lua. Lua is a general purpose scripting language, designed to be securely embedded in high performance environments. Lua is user-friendly and highly customizable, allowing MetaRouter to introduce new and useful features, primarily in the form of helper functions, for processing your event data efficiently and effectively.
In addition to this guide, Lua offers official documentation, which can be found here.
Limitations
- We do not support Coroutines, the
os
library or theio
library. - You will not have access to the filesystem, network or process environment in which Lua runs.
- You Lua script must finish running within 10 seconds.
Basic Syntax
Identifiers
Identifiers in Lua can be any string of letters, digits and underscores, not beginning with a digit. An identifier is the name used to identify components of your program: specifically variables and functions.
Single line comments:
-- Start with two dashes (useful for commenting out a single line of code)
Multiline comments:
--[[
Are created with double brackets.
(useful for commenting out longer runs of code)
--]]
Reserved words
The following words are reserved; meaning we cannot use them as identifiers:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until
while
Lua is case-sensitive. and
is a reserved word, but And
and AND
are different identifiers.
JSON Values in Lua
JSON and Lua have similar type systems for representing values.
JSON has six data types: boolean, string, number, array, object and null.
Lua provides direct equivalents for each of these types:
Type | JSON | Lua | Notes |
---|---|---|---|
boolean | boolean | boolean | Used by both type systems to represent the logical values true and false. |
number | number | number | Used by both type systems to represent real numbers. Neither type system has an integer type. |
string | string | string | Used by both type systems to represent text. Both systems support Unicode characters. |
object | object | table | JSON objects are represented as tables in Lua. Tables are generalized collections capable of representing both dictionaries (aka associative arrays) and lists. |
array | array | table | JSON arrays are represented as tables in Lua as well. |
null | null | null | Used to represent "no value" in both systems |
boolean
The boolean type has two values: true
and false
bool1 = true -- declares a global variable named `bool1`, set to true
bool2 = false -- declares a global variable named `bool2`, set to false
number
The number type represents real numbers. Lua, like JSON, does not have an integer type. Both systems use double-precision floating point number systems to represent all numerical values.
-- declare and initialize a global variable named `number1`,
-- set to the real number value 4
number1 = 4
-- declare and initialize a global variable named `number2`,
-- set to the real number value 4.5
number2 = 4.5
string
A string is a sequence of unicode characters, typically used to represent text. Lua and JSON are both capable of representing Unicode characters.
-- declare and initialize a global variable named `string1`
-- to the value enclosed by single quotes:
string1 = 'a single quoted string'
-- declare and initialize a global variable named `string2`
-- to the value enclosed by double quotes:
string2 = "a double quoted string"
-- declare and initialize a global variable named `string3`
-- to the value enclosed by double brackets:
string3 = [[
Double brackets start and
end multiline strings.
]]
-- Lua provides automatic conversions between numbers and strings.
-- declare and initalize a global variable named `number3`
-- to the result of adding a string and a number:
number3 = "10" + 1 -- `number3` will be 11
-- Lua will raise an error when there's not an obvious numeric conversion:
number4 = "hello" + 1 -- will fail `ERROR (cannot convert "hello")`
tables
The table type is used to represent both arrays and objects in JSON. More generally, tables are associative arrays, where values can be indexed by numbers, like a traditional array or strings, like an object in JSON.
Used to represent an array:
-- declare and initialize a global variable named `t1`
-- referencing a table that contains two strings:
t1 = {"hello", "world"}
-- The JSON equivalent of this table is
-- ["hello", "world"]
Used to represent an object:
-- declare and initialize a global variable named `t2`
-- referencing a table that contains two key, value pairs:
t2 = {}
t2["one"] = 1
t2["two"] = 2
-- The JSON equivalent of table t2 is:
-- {"one": 1, "two": 2}
-- declare and initialize a global variable named `t3`
-- referencing a table that contains two key, value pairs:
t3 = {
["one"] = 1,
["two"] = 2
}
-- Table t3 is equivalent to t2, using an alternative syntax for
-- declaring and initializing a table.
functions
Functions are first-class values in Lua. They can be stored in variables and passed into other functions as arguments and returned as results, just like other values (nil, boolean, number, string and table).
Conditionals
if then else
An if statement tests a condition and executes its then-part or else-part accordingly. The else-part is is optional.
-- conditional logic that determines an event happened on your home page.
if input.context.page.path == '/' then
return 'Y'
else
return 'N'
end
Predefined Variables, Functions and Libraries
global variables
input
null
global functions
Please reach out to the MetaRouter team for assistance with global functions, which we make available to you for ease-of-use. The Global functions that MetaRouter offers include:
- FLATTEN
- JOIN
- LOWER
- MAP
- MILLISECONDS_SINCE_EPOCH_FROM_RFC3999
- MILLISECONDS_SINCE_EPOCH
- PLUCK
- REPLACE
- SECONDS_SINCE_EPOCH_FROM_RFC3999
- SECONDS_SINCE_EPOCH
- SPLIT
- SUBSTRING
- SUM
- TO_BOOL
- TO_FLOAT
- TO_MD5_HASH
- TO_SHA256_HASH
- TO_STRING
- TO_ROUNDED_INT
- TO_TRUNCATED_FLOAT
- TO_TRUNCATED_INT
- TRIM
- UPPER
- LEFT
- RIGHT
math library
string library
The string
library provides common utility functions for working with text. String indexes start with 1 in Lau, unlike JavaScript and C. Negative index numbers are interpreted as working your way backwards from the end of a string.
local s = "my string"
local f = s[1] -- f refers to the string value "m"
local e = s[-1] -- e refers to the string value "g"
table library
Using Lua
You can access Lua functions within any Integration in the MetaRouter UI by adding an Expression mapping. Lua works with all Config options, including Global, Event and Default Configs. Additionally, you can map multiple parameters within a single Lua expression, as shown with the Facebook e-commerce starter kit:
Expression Types & Examples
Single-Key Expressions
Single Key Expressions are data transforms that create a value of a single output key (eg. a cart_total
parameter). This expression has access to the entire incoming event payload, and can calculate a single outgoing key via custom logic from one or more incoming keys.
Example 1 — Adobe Experience Platform products
array
products
arrayA common example for a Single Key Lua Expression is to merge multiple incoming keys into a single outgoing key, with custom concatenation logic. In this case, Adobe Experience Platform and Adobe Analytics would like to have the products from an Order Completion event delivered as a string with a single Key, called products
. Below is the logic for how a Lua Expression can calculate that single string.
UI Context
JSON Input
{
"writeKey": "example",
"userId": "5d56f1a5b800f125e94c1c62",
"type": "page",
"properties": {
"products": [
{
"categoryCode": [
"27",
"21"
],
"categoryDescription": [
"Kitchen",
"Garden & Patio"
],
"name": "Blue Rhino Stainless Steel Griddle",
"numberOfUnits": 1,
"salePrice": 77.58,
"upc": "0007690308630",
"ebtEligible": false,
"deliveryEligible": false,
"pickupEligible": false,
"shipEligible": true,
"productModalitySelected": "ship",
"marketplaceItem": false,
"orderRevenue": 77.58,
"yellowTag": false,
"shipMethod": "standard shipping"
}
]
}
}
Lua Expression
function evar89For(inputProduct)
local fields = {
TO_STRING(inputProduct.deliveryEligible),
TO_STRING(inputProduct.pickupEligible),
TO_STRING(inputProduct.shipEligible)
}
return JOIN(fields, '_')
end
function toAdobeProductMap(inputProduct)
local events = {
JOIN({'event136', inputProduct.salePrice}, '='),
JOIN({'event137', inputProduct.numberOfUnits}, '='),
JOIN({'event210', inputProduct.salePrice}, '='),
}
local eVars = {
JOIN({'evar49', LOWER(inputProduct.name)}, '='),
JOIN({'evar50', LOWER(JOIN(inputProduct.categoryDescription, '_'))}, '='),
JOIN({'evar38', LOWER(inputProduct.ebtEligible)}, '='),
JOIN({'evar88', LOWER(inputProduct.productModalitySelected)}, '='),
JOIN({'evar89', evar89For(inputProduct)}, '='),
JOIN({'evar108', TO_BOOL(inputProduct.marketplaceItem or false)}, '='),
JOIN({'evar129', TO_BOOL(inputProduct.yellowTag or false)}, '='),
}
return {
productName = inputProduct.upc,
quantity = inputProduct.numberOfUnits,
price = inputProduct.salePrice,
events = events,
eVars = eVars,
}
end
function toAdobeProductString(adobeProduct)
local fields = {
adobeProduct.productName,
adobeProduct.quantity,
adobeProduct.price,
JOIN(adobeProduct.events, '|'),
JOIN(adobeProduct.eVars, '|'),
}
return JOIN(fields, ";")
end
local adobeProducts = MAP(input.properties.products, toAdobeProductMap)
local adobeProductStrings = MAP(adobeProducts, toAdobeProductString)
return ";" .. JOIN(adobeProductStrings, ",")
JSON Output
{
"products": ";0007690308630;1;77.58;event136=77.58|event137=1|event210=77.58;evar49=blue rhino stainless steel griddle|evar50=kitchen_garden \u0026 patio|evar38=false|evar88=ship|evar89=false_false_true|evar108=false|evar129=false"
}
Multi-Key Expressions
Multi Key Expressions are data transforms that create multiple output key values (eg. product0price
, product1price
, product2price
parameters). This expression has access to the entire incoming event payload, and can calculate an indeterminate number of outgoing key via custom logic from one or more incoming keys.
Example 1 — Flattening a Product Array
In the case below, the user is tracking the products purchased in an Order Completed event as an Array of Objects, according to Analytics.js syntax. The partner, however, would like product information as a flat object of iterated keys:
{
"0br": "Brand",
"0id": "Product ID",
"0nm": "Name",
"0pr": Price,
"0qt": Quantity,
"1br": "Brand",
"1id": "Product ID",
"1nm": "Name",
"1pr": Price,
"1qt": Quantity,
}
Therefore, the example below outlines a Multi-Key Lua Expression calculating this set of iterated keys.
UI Context
JSON input
{
"appId": "test",
"writeKey": "test",
"anonymousId": "b1ca62d6-22dd-4d0c-acad-a5f225e841a8",
"userId": "90cf8995-f8f5-4d50-bbb7-5e75954f94e2",
"context": {
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",
"page": {
"path": "/dash",
"referrer": "http://dev-mr.bluecode.co/dash",
"search": "",
"title": "MetaRouter",
"url": "http://dev-mr.bluecode.co"
},
"traits": {
"name": "MR dev",
"email": "[email protected]"
}
},
"type": "track",
"event": "Order Completed",
"properties": {
"checkout_id": "fksdjfsdjfisjf9sdfjsd9f",
"order_id": "50314b8e9bcf000000000000",
"affiliation": "Google Store",
"total": 27.5,
"revenue": 25,
"shipping": 3,
"tax": 2,
"discount": 2.5,
"coupon": "hasbros",
"currency": "USD",
"products": [
{
"product_id": "507f1f77bcf86cd799439011",
"sku": "45790-32",
"name": "Monopoly: 3rd Edition",
"price": 19,
"quantity": 1,
"category": "Games",
"brand": "B",
"url": "https://www.example.com/product/path",
"image_url": "https:///www.example.com/product/path.jpg"
},
{
"product_id": "505bd76785ebb509fc183733",
"sku": "46493-32",
"name": "Uno Card Game",
"price": 3,
"quantity": 2,
"category": "Games"
}
]
}
}
Lua Expression
local keyMappings = { -- create a lookup table that maps
product_id = 'id', -- incoming product property names
name = 'nm', -- to the output property suffix
brand = 'br',
price = 'pr',
quantity = 'qt',
}
local results = {} -- create an empty table to store our results
for inputIndex, product in pairs(input.properties.products) do
for inputKey, productVal in pairs(product) do
local outputKey = keyMappings[inputKey]
if outputKey then
local outputIndex = inputIndex - 1
local resultKey = outputIndex .. outputKey
results[resultKey] = productVal
end
end
end
return results
JSON output
{
"0br": "B",
"0id": "507f1f77bcf86cd799439011",
"0nm": "Monopoly: 3rd Edition",
"0pr": 19,
"0qt": 1,
"1id": "505bd76785ebb509fc183733",
"1nm": "Uno Card Game",
"1pr": 3,
"1qt": 2
}
Math Expressions
Math Expressions are data transforms that create output value(s) (eg. a cart_total
parameter) based on a calculation from incoming keys. This expression has access to the entire incoming event payload, and can calculate an indeterminate number of outgoing key via custom logic from one or more incoming keys.
Example 1: Calculating a "Cart Total"
A common use-case for Math Expressions using Lua is to calculate a total value of the cart during an Order Completed event. The following example showcases Lua iterating through the product array, and for each product, multiplying the quantity
value by the price
value, and then summing those values together to specify a cartTotal
output parameter.
UI Context
JSON Input
{
"appId": "test",
"writeKey": "test",
"anonymousId": "b1ca62d6-22dd-4d0c-acad-a5f225e841a8",
"userId": "90cf8995-f8f5-4d50-bbb7-5e75954f94e2",
"context": {
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",
"page": {
"path": "/dash",
"referrer": "http://dev-mr.bluecode.co/dash",
"search": "",
"title": "MetaRouter",
"url": "http://dev-mr.bluecode.co"
},
"traits": {
"name": "MR dev",
"email": "[email protected]"
}
},
"type": "track",
"event": "Order Completed",
"properties": {
"checkout_id": "fksdjfsdjfisjf9sdfjsd9f",
"order_id": "50314b8e9bcf000000000000",
"affiliation": "Google Store",
"total": 27.5,
"revenue": 25,
"shipping": 3,
"tax": 2,
"discount": 2.5,
"coupon": "hasbros",
"currency": "USD",
"products": [
{
"product_id": "507f1f77bcf86cd799439011",
"sku": "45790-32",
"name": "Monopoly: 3rd Edition",
"price": 19,
"quantity": 1,
"category": "Games",
"url": "https://www.example.com/product/path",
"image_url": "https:///www.example.com/product/path.jpg"
},
{
"product_id": "505bd76785ebb509fc183733",
"sku": "46493-32",
"name": "Uno Card Game",
"price": 3,
"quantity": 2,
"category": "Games"
}
]
}
}
Lua Expression
local products = input.properties.products
return SUM(MAP(products, function (p) return p.quantity * p.price end))
JSON Output
{
"cartTotal": 25
...
}
Conditional Logic Expressions
Conditional Logic Expressions are data transforms that create different output key values (eg. homepage
vs page
parameters) based on the exact nature of the incoming event. This expression has access to the entire incoming event payload, and can leverage a set of rules to determine the output value(s).
Example 1 — Indicating HomePage for Page Events
In this case, the partner would like to know, when it receives a Page event, whether that page was the Home Page, or any other page on the site. This is important for understanding the user's journey. In the below example, a homepage
parameter will either have a "Y"
or "N"
value based on whether or not the input.context.page.path
indicates the root of the site.
UI Context
JSON Input
{
"appId": "test",
"writeKey": "test",
"anonymousId": "b1ca62d6-22dd-4d0c-acad-a5f225e841a8",
"userId": "90cf8995-f8f5-4d50-bbb7-5e75954f94e2",
"context": {
"ip": "127.0.0.1",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",
"page": {
"path": "/dash",
"referrer": "http://dev-mr.bluecode.co/dash",
"search": "",
"title": "MetaRouter",
"url": "http://dev-mr.bluecode.co"
},
"traits": {
"name": "MR dev",
"email": "[email protected]"
}
},
"type": "track",
"event": "Order Completed",
"properties": {
"checkout_id": "fksdjfsdjfisjf9sdfjsd9f",
"order_id": "50314b8e9bcf000000000000",
"affiliation": "Google Store",
"total": 27.5,
"revenue": 25,
"shipping": 3,
"tax": 2,
"discount": 2.5,
"coupon": "hasbros",
"currency": "USD",
"products": [
{
"product_id": "507f1f77bcf86cd799439011",
"sku": "45790-32",
"name": "Monopoly: 3rd Edition",
"price": 19,
"quantity": 1,
"category": "Games",
"url": "https://www.example.com/product/path",
"image_url": "https:///www.example.com/product/path.jpg"
},
{
"product_id": "505bd76785ebb509fc183733",
"sku": "46493-32",
"name": "Uno Card Game",
"price": 3,
"quantity": 2,
"category": "Games"
}
]
}
}
Lua Expression
if input.context.page.path == '/' then
return 'Y'
else
return 'N'
end
JSON Output
{
"homepage": "N"
}
Object Manipulation Expressions
Example 1 — Creating a New Object From Scratch
UI Context
JSON Input
{
"appId": "test",
"writeKey": "test",
"anonymousId": "b1ca62d6-22dd-4d0c-acad-a5f225e841a8",
"userId": "90cf8995-f8f5-4d50-bbb7-5e75954f94e2",
"context": {
"ip": "127.0.0.1",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",
"page": {
"path": "/dash",
"referrer": "http://dev-mr.bluecode.co/dash",
"search": "",
"title": "MetaRouter",
"url": "http://dev-mr.bluecode.co"
},
"traits": {
"name": "MR dev",
"email": "[email protected]"
}
},
"type": "track",
"event": "Order Completed",
"properties": {
"checkout_id": "fksdjfsdjfisjf9sdfjsd9f",
"order_id": "50314b8e9bcf000000000000",
"affiliation": "Google Store",
"total": 27.5,
"revenue": 25,
"shipping": 3,
"tax": 2,
"discount": 2.5,
"coupon": "hasbros",
"currency": "USD",
"products": [
{
"product_id": "507f1f77bcf86cd799439011",
"sku": "45790-32",
"name": "Monopoly: 3rd Edition",
"price": 19,
"quantity": 1,
"category": "Games",
"url": "https://www.example.com/product/path",
"image_url": "https:///www.example.com/product/path.jpg"
},
{
"product_id": "505bd76785ebb509fc183733",
"sku": "46493-32",
"name": "Uno Card Game",
"price": 3,
"quantity": 2,
"category": "Games"
}
]
}
}
Lua Expression
return {
client_ip_address = input.context.ip,
client_user_agent = input.context.userAgent,
external_id = TO_SHA256_HASH(input.anonymousId),
}
JSON Output
{
"client_ip_address": "127.0.0.1",
"client_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",
"external_id": "fcda8a2f72aab4ae0504bd60af085ec3b324f4404208d4bb1c9c7672b21261b5"
}
Array Manipulation Expressions
Example 1 — Creating a Simplified Object from an Array
UI Context
JSON Input
{
"appId": "test",
"writeKey": "test",
"anonymousId": "b1ca62d6-22dd-4d0c-acad-a5f225e841a8",
"userId": "90cf8995-f8f5-4d50-bbb7-5e75954f94e2",
"context": {
"ip": "127.0.0.1",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",
"page": {
"path": "/dash",
"referrer": "http://dev-mr.bluecode.co/dash",
"search": "",
"title": "MetaRouter",
"url": "http://dev-mr.bluecode.co"
},
"traits": {
"name": "MR dev",
"email": "[email protected]"
}
},
"type": "track",
"event": "Order Completed",
"properties": {
"checkout_id": "fksdjfsdjfisjf9sdfjsd9f",
"order_id": "50314b8e9bcf000000000000",
"affiliation": "Google Store",
"total": 27.5,
"revenue": 25,
"shipping": 3,
"tax": 2,
"discount": 2.5,
"coupon": "hasbros",
"currency": "USD",
"products": [
{
"product_id": "507f1f77bcf86cd799439011",
"sku": "45790-32",
"name": "Monopoly: 3rd Edition",
"price": 19,
"quantity": 1,
"category": "Games",
"url": "https://www.example.com/product/path",
"image_url": "https:///www.example.com/product/path.jpg"
},
{
"product_id": "505bd76785ebb509fc183733",
"sku": "46493-32",
"name": "Uno Card Game",
"price": 3,
"quantity": 2,
"category": "Games"
}
]
}
}
Lua Expression
return PLUCK(input.properties.products, 'product_id')
JSON Output
{
"content_ids": [
"507f1f77bcf86cd799439011",
"505bd76785ebb509fc183733"
]
}
Updated about 1 year ago