Functions

MQL has several built-in functions for capabilities that don't neatly fit into concise syntax. Functions can operate on a variable amount of parameters and always evaluate to a value. The basic syntax for functions is <name>(<arg1>, <arg2>, ... <argN>).

📘

Request a function!

Don't see a function you want? Let us know via email or Slack!

Top level functions

length (strings)

length(input: string) -> integer

Use length to retrieve the number of Unicode code points used to encode a string in UTF-8. Note that this is not equivalent to the number of bytes or glyphs.

length("sublimesecurity.com") -> 19
length("📧") -> 1
length("émaîl") -> 5

length (arrays)

length(input: [T]) -> integer
Use length to retrieve the length of an array. If the input array is null, the function returns null. If the input array is non-null but empty, the function returns 0.

length(attachments) -> 3
length(filter(attachments, .file_extension == "xls")) -> 2

📘

Unicode and strings

Characters in strings are a complicated matter. Within MQL, all strings are always valid unicode and internally encoded as UTF-8.

Functions within MQL typically operate on unicode strings, and a "character" means a single UTF-8 code point, which can span more than one byte and may be an incomplete glyph.

Array looping functions

Arrays could come from various places: on the MDM, such as recipients.to or attachments; inline with [...] syntax, such as [body.html.raw, body.plain.raw]; or returned from functions, such as beta.binexplode.

To process an array and loop over elements, MQL uses a few builtin functions. Array functions loop over an input array with an expression and return another array or some other value. Array functions typically take two arguments: the source array, and an expression to evaluate over the elements.

Within the looping expression, use the prefix . to access the loop item. To access an attribute of the looping item, use .<attribute>. To access fields on the root level of the MDM, drop the prefix. You can access a loop item from a parent scope with an additional ., such as ...

For example, to check if an array of strings contains the string secret:

any(["super", "secret", "text"], . == "secret")

To check the body.links array that the attribute .mismatched is always false:

all(body.links, .mismatched == false)

To check if either body.plain.raw or body.html.raw to contain any of the strings "urgent", "promptly", or "asap", use an array to avoid repeating logic:

any([body.plain.raw, body.html.raw], ilike(., "*urgent*", "*promptly*", "*asap*"))
{
  "attachments": [
    {
        "file_extension": "xls",
        ...
    },
    {
        "file_extension": "docx",
        ...
    },
    {
        "file_extension": "xls",
        ...
    }
  ],
  ...
}

all

all(input: [T], expression: bool) -> bool
all returns true if all of the values in the original array match an expression, which is provided by the second argument. If the array is empty, then all is vacuously `true.

Example usage

all(attachments, .file_extension == "xls") -> false
all(attachments, .file_extension in ("xls", "docx")) -> true

Tip: Use all if you want to perform an and across the array, and use any to perform an or.

any

any(input: [T], expression: bool) -> bool
any is the counterpart of all. Instead of checking for the expression to evaluate true for all items, any will check if at least one value returns true.

any(attachments, .file_extension == "xls") -> true
any(attachments, .file_extension in ("xls", "docx")) -> true

Tip: Use all if you want to perform an and across the array, and use any to perform an or.

filter

filter(input: [T], expression: bool) -> [T]
filter is used to generate a new filtered array, only returning items that cause the loop expression to evaluate true.

filter(attachments, .file_extension != "xls") -> [{"file_extension": "docx"}]

map

any(input: [T], expression: U) -> [U]
map is used to generate a new array, by evaluating an expression for each item in the array.

map(attachments, length(.file_extension)) -> [3, 4, 3]

map is particularly useful when querying an MDM:

map(attachments, .file_name) -> ["unpaid invoice.xlsx", "unpaid invoice.rar"]