FQL v4 will be decommissioned on June 30, 2025. Ensure that you complete your migration from FQL v4 to FQL v10 by that date. For more details, review the migration guide. Contact support@fauna.com with any questions. |
Query composition
The FQL is a functional language that is highly composable. This section describes these aspects of the language and how to take advantage of them.
Functional programming
Functional programming refers to the construction of programs by applying and composing functions.
A function is a list of instructions that perform a specific task, packaged as a single unit. The function can be used wherever the set of instructions needs to be executed.
Most built-in FQL functions are pure functions:
-
Given a specific set of inputs, the same result is returned, no matter how often the function is called. This is the same property of many mathematical functions. For example, adding 2 to 1 always results in 3.
-
Pure functions have no side effects, so there are no changes to any of a function’s input values, or to the database.
Functions that write to the database cause side effects, so the built-in write functions are not pure functions.
Composability
Composition in programming languages refers to the ability to create complex behavior by aggregating simpler behavior.
For Unix-like systems (e.g. Linux, macOS), one example of composability is terminal’s implementation of pipes. A pipe allows the output of one program to be fed into another program.
For example, to show the contents of a text file, you could run:
cat myfile.txt
1
2
3
4
5
To show just the first two lines of the text file, you could run:
cat myfile.txt | head -2
1
2
The cat
program knows how to read a file and show its content. The
head
program knows how to just display a specific number of lines of
text, regardless of where they came from. We combined the two programs
with a "pipe", which is the vertical bar character |
.
In a composed command, the pipe says "take the output from the program on the left and pass it to the program on the right". This feature enables any two programs that can operate on common data to be combined to achieve a goal that neither could be used to accomplish on their own.
For functional languages, like FQL, composability means that the result of a function can be used in place of a specified value so long as the function returns the same type of value.
FQL queries
Every Fauna query is made up of a single expression. An expression can be one of FQL’s data types, or a function call that is itself an expression and can involve subordinate expressions. At any point in a query, where a value can be used, an FQL expression that returns the same type of value can be used in place of the value.
"Data type" expressions
Here are some examples of simple expressions:
-
A numeric expression:
12345
-
A string expression:
FQL is a functional language!
Function expressions
Calling an FQL function executes that function, and passes any provided arguments as parameters to the function. When the function execution is completed, there is a return value called the result, or an error occurs that terminates the query containing the function call.
For example:
3
The Add
function is called with the arguments 1
and 2
, the
function is executed, and its return value (or result) is 3.
The result of evaluating an expression is always one of Fauna’s
Data types. That means that expressions can
be used as parameters to other functions. In the following example, the
result of calling Add
is used in a separate Add
call:
7
Add
should only work with numeric parameters (arithmetic addition
doesn’t work, for example, with Strings). FQL evaluates the inner
Add
expression when it encounters it, before it can complete
evaluation of the outer Add
expression, so by the time the outer
Add
is evaluated, it is working with numeric parameters.
Combining expressions
You can combine expressions, even if they are unrelated, in two ways:
-
The
Do
function takes a list of expressions, evaluates them in order, and returns the result from the last expression:42
-
An Array can be used to provide a list of expressions, which are evaluated in order, and the resulting array contains the results from each expression:
[ 'The answer is', 42 ]
Conditional logic
FQL provides several functions that allow your queries to perform conditional logic:
-
The
If
function tests a condition and either executes thetrue
orfalse
expression depending on the result of evaluating the condition:The condition evaluates as true
-
In order to build conditional expressions, FQL provides the following functions:
And
Returns true if all values are true.
ContainsField
Returns true when a specific field is found in a document.
ContainsPath
Returns true when a document contains a value at the specified path.
ContainsValue
Returns true when a specific value is found in a document.
Equals
Returns true if all values are equivalent.
Exists
Returns true if a document has an event at a specific time.
GT
Returns true if each value is greater than all following values.
GTE
Returns true if each value is greater than, or equal to, all following values.
LT
Returns true if each value is less than all following values.
LTE
Returns true if each value is less than, or equal to, all following values.
Not
Returns the opposite of a boolean expression.
Or
Returns true if any value is true.
A very common use case is to implement an "upsert", where a document should be created if it does not exist or updated when it does:
{
ref: Ref(Collection("Letters"), "101"),
ts: 1649427736990000,
data: { letter: 'A', extra: 'First' }
}
Use Let
to gather intermediate results
When composing FQL queries, you may encounter the need to use the result
of a particular expression multiple times. The Let
function
allows you to give names to the results of expressions, and then you can
use those named values while composing new named values or within the
result expression.
For example, we can rewrite the "upsert" query provided
above using Let
to reduce the repetitive
elements, which makes the query more concise and easier to reason about:
{
ref: Ref(Collection("Letters"), "101"),
ts: 1649427716790000,
data: { letter: 'A', extra: 'First' }
}
The Let
function allows you to gather intermediate results in a
complex expression, and then access those results by name where you need
them.
Composing queries in an application
Each FQL query is sent to the Fauna service and evaluated only when
the client.query
driver function is executed in your client program.
Until that happens, you can assign FQL expressions to variables and then
use those variables in your query.
For example:
7
Here is a more sophisticated example that combines host language functions and a host language mathematical constant with FQL functions, used both inside and outside of the host language functions:
The total area is 90.54
This facility means that your application logic can compose complex queries by establishing simple expressions and combining them to compose more complex expressions. Those expressions can serve as queries themselves, or be used as subordinate expressions in even more complex queries. There are limits (see below) that constrain the maximum complexity. Within those limits, you can make your queries as complex as you need.
Caveats
-
Fauna queries are limited to 16 MB in size.
-
For Fauna "system schemas", including databases, collections, and indexes, you cannot both create a system schema and use it in a single transaction. However, you can create multiple system schemas in a single transaction, and the next query can use any/all of the now-existing system schemas.
-
The JavaScript driver supports syntactic sugar for
Lambda
functions:// standard FQL q.Lambda(["x", "y"], q.Add(q.Var("x"), q.Var("y"))) // syntactic sugar (x, y) => { q.Add(x, y) }
Is this article helpful?
Tell Fauna how the article can be improved:
Visit Fauna's forums
or email docs@fauna.com
Thank you for your feedback!