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.
Here is a summary of the other operations available in CQL:
|Concatenation||Joins two or more strings together.||&|
|Range||Generates a sequence of values.||..|
|Fallback||Provides a fallback value when something is missing.||?? or or else|
|Spread||Expands 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.
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>*/
Here is a practical example:
"CQL " & "💚" // CQL 💚
You can also combine more than two strings by using multiple & operators:
"C" & "Q" & "L" // CQL
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
For more information about how conversion to string works, see string conversion.
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.
Ranges are currently limited to a maximum length of 30, so any attempt to create a longer range will result in an error.
To create a range, use the .. operator between the start and end values:
/*<start>*/ .. /*<end>*/
For example, the following range generates a sequence of integers from 1 to 3:
1 .. 3 // 1, 2, 3
Because ranges have direction, you can also create a descending range by reversing the start and end values:
3 .. 1 // 3, 2, 1
You can also create ranges of characters, like this:
'A' .. 'C' // A, B, C
In fact, you can use any Unicode character, including emojis:
'😃' .. '😆' // 😃, 😄, 😅, 😆
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:
'2023-12-04' .. '2023-12-06' // 2023-12-04, 2023-12-05, 2023-12-06
For more examples of valid date formats, see date conversion.
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"
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"
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"
In this case, the expression checks the user's name first, then the nickname, and finally returns the fallback if both are missing.
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.
To combine two collections, place the ... where you want to insert the elements of the second collection:
["a", .../*<list>*/, "z"]
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]
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
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
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
The above example is invalid and results in an error because the element without a key comes after the element with a key.