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 expression2 at the expression1
time snapshot of the data. The expression1 operand is a
Time or
TransactionTime.
The at
expression returns null
if the requested document doesn’t exist.
This changes the time when
collection.byId()
is
evaluated. Fauna stores document history up to the limit defined by
history_days
.
The default number of days stored is zero so history_days
must be increased
for at
to be meaningful. See Document history.
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!