JSON Expressions
Contents
Introduction
JSON-structured expressions provide an opportunity to filter and query API resources through composable operations.
Expressions are defined by the user through a simple domain-specific language. An expression will be evaluated with an API resource type as its input and may cross-reference properties within the given resource to produce a result.
Expression Format
Expressions are structured as a JSON object/tree describing operators and their operands, with any level of nesting. The structure resembles the composition of function calls in a typical programming language.
An operation consists of a single-property JSON object with an operator name as the property-key and a scalar operand or array of operands as the property value:
{ <operator> : <operand(s)> } => evaluated
Examples:
Operations may include sub-operations within their operand(s). Each operand for an operation will be evaluated recursively before the operation itself is evaluated. Any JSON objects contained within an operand will be evaluated as nested operations.
Examples:
Ambiguity arises when a JSON object-literal or value containing object-literals is desired as an operand, without it being interpreted as a nested operation. For this purpose, a special value
operator is provided which returns its operand(s) completely unevaluated.
Examples:
Supported Operators
Logical Operators
not
The not
operator returns true if its operand evaluates to false. The operand must evaluate to a boolean.
Examples:
and
The and
operator returns true if all of the values within the given array of operands evaluate to true. All operands must evaluate to booleans.
Examples:
or
The or
operator returns true if any of the values within the given array of operands evaluate to true. All operands must evaluate to booleans.
Examples:
Relational Operators
equal
The equal
operator returns true if all of the values within the given array of operands are equal according to a deep-equality check. All operands must evaluate to equivalent or compatible types.
Examples:
notEqual
The notEqual
operator returns true if not all of the values within the given array of operands are equal according to a deep-equality check. All operands must evaluate to equivalent or compatible types.
Examples:
lessThan
The lessThan
operator returns true if each of the values within the given array is less than its subsequent value. If 2 operands are provided, and both evaluate to dates, the operands will be compared as dates; otherwise, all operands must evaluate to numeric types.
The before
operator is an alias for the lessThan
operator.
Examples:
lessThanEqual
The lessThanEqual
operator returns true if each of the values within the given array is less than or equal to its subsequent value. If 2 operands are provided, and both evaluate to dates, the operands will be compared as dates; otherwise, all operands must evaluate to numeric types.
Examples:
greaterThan
The greaterThan
operator returns true if each of the values within the given array is greater than its subsequent value. If 2 operands are provided, and both evaluate to date types, the operands will be compared as dates; otherwise, all operands must evaluate to numeric types.
The after
operator is an alias for the greaterThan
operator.
Examples:
greaterThanEqual
The greaterThanEqual
operator returns true if each of the values within the given array is greater than or equal to its subsequent value. If 2 operands are provided, and both evaluate to date types, the operands will be compared as dates; otherwise, all operands must evaluate to numeric types.
Examples:
String Operators
toLower
The toLower
operator returns its operand to lower-case. The operand must evaluate to a string.
Examples:
toUpper
The toUpper
operator returns its operand to upper-case. The operand must evaluate to a string.
Examples:
startsWith
The startsWith
operator returns true if its first operand starts with its second operand. Both operands must evaluate to strings.
Examples:
endsWith
The endsWith
operator returns true if its first operand ends with its second operand. Both operands must evaluate to strings.
Examples:
contains
(also see Collection Operators)
The contains
operator returns true if its first operand contains its second operand. If the first operand evaluates to an array, the array will be searched for the second operand. If the first operand evaluates to a string, the string will be searched for the second operand which must also evaluate to a string.
Examples:
split
The split
operator returns an array of strings, formed by splitting its first operand on its second operand. Both operands must evaluate to strings.
Examples:
format
The format
operator returns a formatted string. The first operand must evaluate to a format string and the second operand must evaluate to an array of parameters for the format string.
Example:
regexMatches
The regexMatches
operator returns true if its second operand matches the pattern given by its first operand. Both operands must evaluate to strings, and the regex pattern must be ECMAScript-compatible.
Example:
parseJson
The parseJson
operator returns its operand parsed into a JSON value. The operand must evaluate to a string.
Example:
Date Operators
date
The date
operator converts its operand to a date object. The operand must evaluate to a string.
Example:
before
The before
operator returns true if its first operand is before its second operand. Both operands must evaluate to date types. The date
operator will need to be used to convert each operand, if dates are not automatically converted.
The before
operator is an alias for the lessThan
operator.
Examples:
after
The after
operator returns true if its first operand is after its second operand. Both operands must evaluate to date types. The date
operator will need to be used to convert each operand, if dates are not automatically converted.
The after
operator is an alias for the greaterThan
operator.
Examples:
addSeconds
The addSeconds
operator returns a date-time value computed by adding its second operand as a number of seconds to its first operand. The first operand must evaluate to a date type. The date
operator will need to be used to convert the first operand, if dates are not automatically converted.
Examples:
addMinutes
The addMinutes
operator returns a date-time value computed by adding its second operand as a number of minutes to its first operand. The first operand must evaluate to a date type. The date
operator will need to be used to convert the first operand, if dates are not automatically converted.
Examples:
addHours
The addHours
operator returns a date-time value computed by adding its second operand as a number of hours to its first operand. The first operand must evaluate to a date type. The date
operator will need to be used to convert the first operand, if dates are not automatically converted.
Examples:
addDays
The addDays
operator returns a date-time value computed by adding its second operand as a number of days to its first operand. The first operand must evaluate to a date type. The date
operator will need to be used to convert the first operand, if dates are not automatically converted.
Examples:
Arithmetic Operators
sum
The sum
operator returns the sum of its operands, as a floating-point value. All operands must evaluate to or be convertible to numeric values.
The add
operator is an alias for the sum
operator.
Examples:
add
The add
operator returns the sum of its operands, as a floating-point value. All operands must evaluate to or be convertible to numeric values.
The add operator is an alias for the sum
operator.
Examples:
subtract
The subtract
operator returns its second operand subtracted from its first operand, as a floating-point value. Both operands must evaluate to or be convertible to numeric values.
Examples:
Collection Operators
value
The value
operator is used to "quote" object-literal values within a filter expression so they are not misinterpreted as operators during evaluation. The operand(s) will be returned unmodified and unevaluated.
Examples:
contains
The contains
operator returns true if its first operand contains its second operand. If the first operand evaluates to an array, the array will be searched for the second operand. If the first operand evaluates to a string, the string will be searched for the second operand which must also evaluate to a string.
Examples:
overlaps
The overlaps
operator returns true if its first operand overlaps/intersects with its second operand. Both operands must evaluate to arrays.
Examples:
groupBy
The groupBy
operator returns an object containing grouped arrays of objects, using a path string to lookup the grouping property for each item in a given array. The first operand must evaluate to a path string and the second operand must evaluate to an array of objects to apply the groupings over. The property values used for grouping will be converted to strings for their grouping keys within the returned object.
Example:
Conditional Operators
ifElse
The ifElse
operator returns its second operand if its first operand evaluates to true, otherwise its third operand is returned. The first operand must evaluate to a boolean. The ifElse
operator is equivalent to a ternary conditional.
Examples:
Contextual Operators
path
The path
operator returns a value within an implicit or given JSON object or array, using JSON-Path syntax for path lookups. If a single operand evaluating to a path string is provided, the path will be used to lookup a value within the resource associated with the current expression. If an array of operands is provided, the second operand should evaluate to an object or array which the first operand is a path into.
Examples:
with
The with
operator binds values to names within a new evaluation context, evaluates its final operand under the prepared context, then finally unbinds the temporary evaluation context and returns the evaluated result. An array of operands must be provided where all leading operands are object-literal values and the final operand is any expression.
The with
operator is special, in that its leading operands are received as object-literal values but are not interpreted as operations. Instead, the objects are interpreted as key-value bindings where each value is evaluated as an expression and assigned to its key/name within the current evaluation context. The final operand will be evaluated and returned.
Each with
operation creates a nested evaluation context which inherits the bindings of its parent context. This is conceptually similar to lexical/block scoping. Leading operands will be evaluated in order; each leading operand will add bindings to the context and may reference values bound by prior operands or bound within parent contexts.
If a binding object contains multiple binding properties, the bindings in the object may not reference each other, but may reference pre-existing bindings.
The ref
operator may be used to reference values which are bound within the current context or any parent context. The combination of the with
and ref
operators enables a sequential programming style and re-use of intermediate values. For complex expressions, this can be helpful for overall readability and terseness.
Examples:
ref
The ref
operator references a named value bound within the current evaluation context. The operand must be a string-literal: the name of the referenced binding. If the same name has been bound multiple times within the current evaluation context, the most-recently bound value will be returned.
The with
operator must be used to bind named values within an evaluation context before they may be referenced by the ref
operator.
Examples:
Last updated