Check out v4 of the Fauna CLI
v4 of the Fauna CLI is now in beta. The new version introduces enhancements to the developer experience, including an improved authentication workflow. To get started, check out the CLI v4 quick start. |
Expressions and statements
A query is a series of zero or more statements and expressions separated by newlines or semicolons. Multiple statements and expressions in a block must end with an expression.
Statements and expressions can span multiple lines, and there is no line continuation character.
at
Get the result of an expression at a given time.
Description
The at expression gets the result of an expression at a specified timestamp, provided as a Time or TransactionTime.
You can use an at
expression to run a query on the
historical snapshot of one or more documents. This
is called a temporal query.
Minimum viable timestamp (MVT)
The minimum viable timestamp (MVT) is the earliest point in time that you can
query a collection’s document history. The MVT is calculated as the query
timestamp minus the collection’s
history_days
setting:
MVT = query timestamp - collection's `history_days`
Temporal queries using an at
expression can’t access document snapshots that are older than the MVT. Any
query that attempts to access a document snapshot before the MVT returns an
error with the invalid_request
error
code and a 400 HTTP status code:
{
"code": "invalid_request",
"message": "Requested timestamp 2099-01-09T00:36:53.334372Z less than minimum allowed timestamp 2099-01-10T00:21:53.334372Z."
}
For example, if a collection has history_days
set to 3
and you run a query
that attempts to access a document in the collection from 4 days ago, the query
returns an error.
Examples
The following example gets the current document and a snapshot of the document from yesterday.
-
After running this query yesterday to create a document,
Product.create({ id: "9780547928227", name: "lemon", state: "yesterday state" })
{ id: "9780547928227", coll: Product, ts: Time("2099-04-10T16:22:32.420Z"), name: "lemon", state: "yesterday state" }
-
And running this query today to update the document,
Product.byId("9780547928227")?.updateData({ state: "today state" })
{ id: "9780547928227", coll: Product, ts: Time("2099-04-10T16:23:03.520Z"), name: "lemon", state: "today state" }
-
The following query returns the current data,
Product.byId("9780547928227")?.state
"today state"
-
And the following query returns the data from yesterday,
let yesterday = Time.now().subtract(1, "day") at (yesterday) { Product.byId("9780547928227")?.state }
"yesterday state"
The following examples show that when comparing documents using the ==
operator, only the document id
field is compared, ignoring all other fields.
Compare the state
fields of different versions of the same document,
which differ because of the state change between yesterday and today:
let yesterday = Time.now().subtract(1, "day")
let product1 = at(yesterday) {
Product.byId('9780547928227')
}
let product2 = Product.byId('9780547928227')
product1?.state == product2?.state
false
Compare versions of the full document. The documents are identical
because the document id
fields are the same:
let yesterday = Time.now().subtract(1, "day")
let product1 = at(yesterday) {
Product.byId('9780547928227')
}
let product2 = Product.byId('9780547928227')
product1 == product2
true
if … else
Use conditional branching for execution control flow.
Description
The if
and if … else
expressions conditionally execute a block
depending on the Boolean value of expression1. If
expression1 evaluates to true
, expression2 executes.
Otherwise, expression3 executes.
The last expression in the if
or else
block that satisfies the
condition is the value returned for the block.
The result of the if
expression is a value that can be assigned to a
variable. If the expression evaluates to false
and else
isn’t included, the expression returns null
.
Examples
Change the query result when the if
condition evaluates to true
:
if (5 > 3) "higher"
"higher"
Use an else
block if the condition evaluates to false
:
if (5 > 6) {
"higher"
} else {
"lower"
}
"lower"
Assign the result of the if
expression to a variable:
let state = if (5 > 6) {
"higher"
} else {
"lower"
}
state
"lower"
Ternary operator
FQL doesn’t have a ternary (conditional) operator. You can get the same result
using an if … else
statement. For
example, to perform an upsert:
// Customer email to look up
let email = "alice.appleseed@example.com"
// Customer data to upsert
let data = {
name: "Alice Appleseed",
email: "alice.appleseed@example.com",
address: {
street: "87856 Mendota Court",
city: "Washington",
state: "DC",
postalCode: "20220",
country: "US"
}
}
// Try to find the existing customer by email.
// If the customer doesn't exist, returns `null`.
let customer = Customer.byEmail(email).first()
// Create or update the customer
// based on existence.
if (customer == null) {
Customer.create(data)
} else {
customer!.update(data)
}
let
Assign a value to a variable.
Description
A let
statement declares a variable with a name identifier and assigns a
value to the variable. Optionally, you can type the variable as any of the
supported types.
The name can be any valid identifier but can’t be a single underscore (_) character.
The variable is scoped to the block in which it is declared. A variable can be referenced in a nested block, but a variable declared in a nested block can’t be referenced outside of the block.
Assigning a value to a variable can be done only using the let
statement.
You can’t assign a value to name directly:
let x = 100 // valid assignment
x = 1000 // invalid assignment
A variable of the same name can be declared again in the same block and assigned a new value. It is a new variable declaration, and the previously declared variable can no longer be referenced:
let x = 100 // (A) valid assignment
let x = 1000 // (B) valid assignment and (A) can no longer be referenced
x // returns 1000
Examples
let xInt = 5
let yString = "hello"
let zArray = [xInt, yString]
[xInt, yString, zArray]
[
5,
"hello",
[
5,
"hello"
]
]
Variable scoping example where the declared x
variable has global scope,
while the scope of variables a
and b
is limited to the block:
let x = {
// a and b are only accessible in this block
let a = 1
let b = 2
a + b
}
x
3
Assign a new value to the variable:
let x = "foo"
let fn = () => x
let x = "bar"
let y = fn()
[x, y]
[
"bar",
"foo"
]
Use a previous definition of the variable when redefining the variable:
let x = 1
let x = x + 1
x
2
Type a variable:
let x: Int = 2
x
2
Incorrectly typing a variable results in an error:
let x: Int = "2"
x
invalid_query: The query failed 1 validation check
error: Type `"2"` is not a subtype of `Int`
at *query*:1:5
|
1 | let x: Int = "2"
| ^
|
cause: Type `String` is not a subtype of `Int`
|
1 | let x: Int = "2"
| ^^^
|
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!