Operators

This section describes the FQL operators. See Operator precedence for the operator precedence and associativity table.

Assignment

Operator Syntax Description

=

variable [:type] = value

Simple assignment operator assigns the value to the declared variable.

The optional :type notation constrains a value to the given type, where type is one of the supported Types. For example,
let x:String = "5"
is a valid statement, whereas
let x:String = 5
results in an invalid_query error.

You can use an if …​ else statement to conditionally assign a variable value. For an example, see Conditional assignment.

Arithmetic

The arithmetic operators perform arithmetic operations on numeric operands.

Operator Syntax Description

+

operand1 + operand2

Addition, sums the operands.

-

operand1 - operand2

Subtraction, subtracts operand2 from operand1.

*

operand1 * operand2

Multiplication, multiplies the operands.

/

operand1 / operand2

Division, divides operand1 by operand2.

%

operand1 % operand2

Modulo, returns the remainder of operand1 divided by operand2 and takes the sign of the dividend.

**

operand1 ** operand2

Exponentiation, returns the result of raising operand1 to the power of operand2.

Concatenation

The plus operator performs concatenation on sting operands.

Operator Syntax Description

+

operand1 + operand2

For String operands, concatenates the operands, left-to-right.

Comparison

Comparison operators return a Boolean value.

For the <, >, , and >= operators, comparison across types always returns false and comparing non-comparable objects always returns false.

Operator Syntax Description

==

expression1 == expression2

Equal to. Returns true if expression1 and expression2 have the same value. Otherwise, returns false.

  • When comparing documents, only the document id metadata fields are compared and all other fields are ignored.

  • Sets are equal if they have the same elements and the elements are in the same order.

!=

expression1 != expression2

Not equal to. Returns true if expression1 and expression2 do not have the same value. Otherwise, returns false.

>

expression1 > expression2

Greater than. Returns true if expression1 is greater than expression2.

  • Comparison across types returns false.

  • Comparison of non-comparable objects, such as anonymous functions and Set cursors, returns false.

  • Comparing Sets isn’t supported but a Set can be converted to an Array and then compared. See set.toArray().

>=

expression1 >= expression2

Greater than or equal to. Returns true if expression1 is greater than or equal to expression2.

  • Comparison across types returns false.

  • Comparison of non-comparable objects, such as anonymous functions and Set cursors, returns false.

  • Comparing Sets isn’t supported but a Set can be converted to an Array and then compared. See set.toArray().

<

expression1 < expression2

Less than. Returns true if expression1 is less than expression2.

  • Comparison across types returns false.

  • Comparison of non-comparable objects, such as anonymous functions and Set cursors, returns false.

  • Comparing Sets isn’t supported but a Set can be converted to an Array and then compared. See set.toArray().

<=

expression1 <= expression2

Less than or equal to. Returns true if expression1 is less than or equal to expression2.

  • Comparison across types returns false.

  • Comparison of non-comparable objects, such as anonymous functions and Set cursors, returns false.

  • Comparing Sets isn’t supported but a Set can be converted to an Array and then compared. See set.toArray().

isa

expression1 isa <type>

Evaluate if expression1 is of type <type>, which can be any of the supported runtime Types. The <type> must be a module object.
Example: "1" isa String returns true

Check a value’s type with isa

Use the isa operator to check if a value is of a specific FQL type. For example:

"foo" isa String  // true

123 isa String    // false

123 isa Int       // true

0.123 isa Double  // true

123 isa Number    // true

0.123 isa Number  // true

{ a: "foo", b: "bar" } isa Object // true

[ 1, 2, 3 ] isa Array   // true

Product.all() isa Set   // true

// For documents, the type is the name
// of the collection. For example, `Product`
//collection documents have a type of `Product.`
Product.byName('limes').first() isa Product // true

// Document references also resolve to the
// document type.
Product.byId('111') isa Product // true

// Dangling references, which point to documents
// that don't exist, still resolve to the
// document type. For example, the following
// document doesn't exist.
Product.byId('999') isa Product // true

isa doesn’t support parameterized generic types, such as Ref<A> or Array<String>. Queries that attempt to use isa with such types return an invalid_query error:

// NOT SUPPORTED:
[ 1, 2, 3 ] isa Array<Number>

// NOT SUPPORTED:
Product.all() isa Set<Product>

// NOT SUPPORTED:
Product.byId('111') isa Ref<Product>

Logical

Logical operators return a Boolean value.

Operator Syntax Description

||

operand1 || operand2

Logical OR. Returns true if one of the operands is true. Otherwise, returns false.

&&

operand1 && operand2

Logical AND. Returns true if the operands are true. Otherwise, returns false.

Bitwise operators

Operator Syntax Description

|

expression1 | expression2

Bitwise inclusive OR:

expression1 expression2 expression1 | expression2

0

0

0

0

1

1

1

0

1

1

1

1

^

expression1 ^ expression2

Bitwise exclusive OR (XOR):

expression1 expression2 expression1 ^ expression2

0

0

0

0

1

1

1

0

1

1

1

0

&

expression1 & expression2

Bitwise AND:

expression1 expression2 expression1 & expression2

0

0

0

0

1

0

1

0

0

1

1

1

Unary

Operator Syntax Description

-

-operand
- operand

Unary negation. Negates the operand it precedes.

!

!operand

Logical negation (NOT). Inverts the value of the Boolean operand and returns a Boolean value.

Examples:

Unary negation variations:

-5 // -5. A negative literal, not unary negation.

- 5 // -5. Unary negation applied to a literal.

let num = 5
-num // -5. Unary negation of a variable.

Insert a space between - and operand if operand is a literal Number.

Logical NOT:

!false // true

Optional chaining

Operator Syntax Description

?.

object.value?.field

object.method?.(args)

Optional chaining. When accessing a field or invoking a method, if the left side of the expression evaluates to null, return null.

If chained to other methods, the `null` value is passed to the method.
For example, `{a: null}.a?.toString()` passes `null` to
xref:reference:fql-api/string/tostring.adoc[], which returns `"null"`,
a type:string[] representation of type:null[].

Examples:

let customer = {
  name: "Alice Appleseed",
  address: {
    state: "DC"
  }
}

customer.address?.state

The optional chaining operator can also be used with methods:

let customer = {
  name: "Alice Appleseed",
  address: {
    state: "DC"
  }
}

customer.name?.toLowerCase()

Null coalescing

Operator Syntax Description

??

expression1 ?? expression2

Null coalescing. If expression1 evaluates to null, return the expression2 result. Otherwise, return the expression1 result.

Example:

// Gets a `Customer` collection document.
let customer = Customer.byEmail("carol.clark@example.com").first()

// This customer's `cart` field is `null`.
customer?.cart ?? "Not found" // returns "Not found"

Non-null assertion postfix

Operator Syntax Description

!

expression!

Non-null assertion postfix. Runtime validation: if expression evaluates to null, return an error. Otherwise, return the result.

Example:

let customer = {
  name: "Alice Appleseed",
  address: {
    state: "DC"
  }
}

customer.date! // Returns an error.

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 is null, create a new customer.
// Otherwise, update the existing one.
if (customer == null) {
  Customer.create(data)
} else {
  customer!.update(data)
}

Null checking

If you’re checking for a null value, you can use the null coalescing (??) operator to return a default value when an expression is null. For example:

// Try to find the existing customer by email.
// If the customer doesn't exist, return `null`.
let customer = Customer.byEmail("carol.clark@example.com")?.first()


// Use the null coalescing (??) operator to return the customer's
// cart. If the customer or their cart is `null`, return `"Not found"`.
// In this case, the customer's cart is `null`.
customer?.cart ?? "Not found" // returns "Not found"
\