Other operations

Explore the other operations available in CQL.

In addition to logical, comparison, and arithmetic operations, CQL provides a variety of operations for everyday tasks, such as string concatenation, range generation, and null checking.

In this reference, we explore the syntax and semantics of these operations, along with practical examples of how to use them in your queries.

Operations

Here is a summary of the other operations available in CQL:

OperationDescriptionOperator
ConcatenationJoins two or more strings together.&
RangeGenerates a sequence of values...
FallbackProvides a fallback value when something is missing.?? or or else
SpreadExpands a list into a sequence of values....

In the following sections, we provide more detail on each of these operations, including their syntax, semantics, and practical examples.

Concatenation

The concatenation operation combines two strings into a single string. This is useful when you need to format strings, such as when you want to create a full name by combining a first and last name.

To concatenate two strings, place the & operator between them:

/*<prefix>*/ & /*<suffix>*/
Try in Playground

Here is a practical example:

"CQL " & "💚" // CQL 💚
Try in Playground

You can also combine more than two strings by using multiple & operators:

"C" & "Q" & "L" // CQL
Try in Playground

When you concatenate a string with a non-string value, the non-string value is converted to a string before concatenation:

"1 + 1 = " & 2 // 1 + 1 = 2
Try in Playground

For more information about how conversion to string works, see string conversion.

Range

The range operation creates a sequence of values between two endpoints, much like a for-loop in programming languages.

Ranges have the following properties:

  • They have no gaps, so each value in the sequence is one increment away from the next value.
  • They are inclusive, which means that both the start and end values are included as part of the sequence, and the sequence cannot be empty.
  • They can be ascending or descending, depending on the order of the start and end values.

You can create ranges of integers, characters, and local dates. Ranges of other types, such as floating-point numbers and multi-character strings, are not supported.

To create a range, use the .. operator between the start and end values:

/*<start>*/ .. /*<end>*/
Try in Playground

For example, the following range generates a sequence of integers from 1 to 3:

1 .. 3 // 1, 2, 3
Try in Playground

Because ranges have direction, you can also create a descending range by reversing the start and end values:

3 .. 1 // 3, 2, 1
Try in Playground

You can also create ranges of characters, like this:

'A' .. 'C' // A, B, C
Try in Playground

In fact, you can use any Unicode character, including emojis:

'😃' .. '😆' // 😃, 😄, 😅, 😆
Try in Playground

The sequence of characters in a range is determined by their Unicode code points. Refer to the Unicode character list for more information.

Finally, you can create a range of local dates by specifying the start and end dates in the ISO 8601 format:

'2024-07-27' .. '2024-07-29' // 2024-07-27, 2024-07-28, 2024-07-29
Try in Playground

For more examples of valid date formats, see date conversion.

Fallback

The fallback operation provides a default value when something is missing. It's handy when dealing with potentially failing expressions or missing information, like optional attributes or parameters

To specify a default value, place the ?? operator between the expression you want to check and the fallback value:

user's name ?? "Anonymous"
Try in Playground

In the above example, the expression returns either the user's name or "Anonymous" if the user's name is missing.

For a more natural language feel, you can write:

user's name or else "Anonymous"
Try in Playground

You can also chain multiple fallback values together to return the first non-absent value:

user's name or else user's nickname or else "Anonymous"
Try in Playground

In this case, the expression checks the user's name first, then the nickname, and finally returns the fallback if both are missing.

Spread

The spread operation expands a collection into a sequence of values. It is useful when you need to combine collections or when you need to pass a list of values as arguments to a function.

Spreading collections

To combine two collections, place the ... where you want to insert the elements of the second collection:

["a", .../*<list>*/, "z"]
Try in Playground

In the above example, the list is expanded between two elements. You could also expand it at the beginning to prepend the elements or at the end to append the elements — it all depends on where you place the ... operator.

When spreading maps, be aware that the keys of the second map take precedence over the keys of the first map. This means that if both maps have the same key, the value from the second map will overwrite the value from the first map. Here is an example to illustrate this:

["a": 1, "b": 2, ...["b": 3, "c": 4]] // ["a": 1, "b": 3, "c": 4]
Try in Playground

Spreading arguments

Another use case for spreading is when you need to pass a list of values as arguments to a function.

Consider the following example:

let power = (a, b) => a ** b;
let numbers = [2, 3];
power(...numbers) // 8
Try in Playground

In this example, because the numbers variable is a list, the spread operator maps each element to the corresponding argument based on the order in which they appear in the list.

Because CQL supports named arguments, you can also spread a map of arguments:

let power = (a, b) => a ** b;
let numbers = ["b": 3, "a": 2];
power(...numbers) // 8
Try in Playground

Note that the order of the arguments in the map does not matter because each argument is mapped to the corresponding parameter based on the parameter name.

For collections that mix elements with and without keys, the same rule applies as for named and positional arguments: elements without keys must come first.

let power = (a, b) => a ** b;
let numbers = ["b": 3, 2];
power(...numbers) // 🚨 Invalid
Try in Playground

The above example is invalid and results in an error because the element without a key comes after the element with a key.