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.

Syntax

at (expression1) expression2

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.

  1. 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"
    }
  2. 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"
    }
  3. The following query returns the current data,

    Product.byId("9780547928227")?.state
    "today state"
  4. 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.

Syntax

if (expression1) expression2 [else expression3]

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.

Syntax

let name [: type] = value

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

Declare and assign Int, String, and Array values to variables:

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!