collection.compute

Define a computed field.

A computed field dynamically derives the value of a field using a user-defined function. These fields aren’t persisted but are derived when the field is read.

The function is an anonymous function that is evaluated when a document is read. The anonymous function takes a source Document as input, operates on the document values, and returns a computed value. The following is an example of a computed function definition:

collection User {
  compute fullName = (.firstName + " " + .lastName)
}

The example function is invoked using the following query and returns Joe Bob:

User.create({ firstName: "Joe", lastName: "Bob" }).fullName

An optional type can be included as part of the function signature.

Computed fields can be indexed to search for documents using a computed value. For example:

collection Account {
  compute status = (
    doc => (
      if (oc.points > 100){
        "gold"
      }
      else {
        "standard"
      }
    )
  )
}

index byStatus {terms [.status]}

Syntax

compute <fieldName>[: <type>] <functionBody>

Properties

Parameter Type Required Description

fieldName

String

Yes

Computed field name. The fieldName can be the same as a persisted field name. An optional type can be declared for the function.

functionBody

Function

Yes

The function body is an anonymous function that is evaluated when a document is read. An optional type can also be provided as part of the function signature.

The anonymous function takes a source Document as input, operates on the document values, and returns a computed value.

Computed fields with defined anonymous functions can transform existing fields, provide convenient access to complex relations, and index over derived data.

The anonymous function return value is the value of the computed field.

Discussion

The computed_fields field defines document functions that evaluate a field when the document is read using the provided anonymous function. The anonymous function can:

  • read

  • cause other computed fields to be evaluated

  • call user-defined functions (UDFs).

A computed field anonymous function has the restriction that it can’t write to a document. Trying to write to a computed_fields field returns an error.

Computed fields take precedence over persisted fields, and persisted fields can be referenced as children of the .data field.

Computed fields and indexes

Computed fields can be indexed and when indexed, the value that is indexed is the value of the computed field computed at the time the index is built. Because of this, indexed computed fields have more restrictions to prevent changes to the value of the field that are hard to detect. These restrictions are enforced by the type system when changes are made to computed fields or index definitions, preventing such changes. They are also enforced at runtime when they cause evaluation of a computed field to fail during the index build, which results in a null term or value.

The following actions cause an index to be rebuilt:

  • Changes to the definition of an indexed computed field.

  • Removing the computed field. The new index treats the computed field like a persisted field.

When a new index entry is added or an existing entry is updated, the anonymous function computes the value at that time instead of every time the value is needed.

Computed fields can be indexed and used as unique constraints subject to the following restrictions, which cause the computed field evaluation to fail at index time because of an invalid index definition:

  • Indexed computed fields can’t use computed fields in the body.

  • Indexed computed fields can’t call UDFs.

  • Indexed computed fields can’t read.

Failing evaluation causes the document to emit a null term or value for the computed field.

The following table summarizes computed field index behavior:

Problem Does the entry exist?

One or more values error, but terms are correct. Termless indexes count are always correct.

Yes

At least one term doesn’t error.

Yes
Terms that error are null.

All terms error.

No

If an indexed computed field depends on another computed field, changing the definition of the first computed field silently changes the definition of the second.

Adding, updating, renaming, or removing non-indexed computed fields is safe. Changing an indexed computed field definition triggers a rebuild of its covering indexes.

Removing an indexed computed field is not allowed in protected mode.

Computed functions and types

Given the following schema, types are inferred on the index functions for the collection. For example:

collection User {
  index byFullName {
    terms [.fullName]
  }
  compute fullName: String = doc => "#{doc.firstName} #{doc.lastName}"
}

In a query, the type in the index function is inferred, and the following query results in a Type error: 3 is not a subtype of String error:

User.byFullName(3) // Type error: `3` is not a subtype of `String`

Examples

Define a computed field that validates a range of values

compute isAdult = (
  doc => (doc.age > 18 && doc.age <= 120)
)

Assign a type to a computed field

Assign a String type to a computed field:

collection User {
  compute status: String = doc => {
    if (doc.points > 100) {
      "gold"
    } else {
      "standard"
    }
  }
}

If the optional type is not provided, the type checker assigns the enumeration of the possible computed values, which are gold and standard.

Use a computed field as an index term

Convert a user name to uppercase and get the first letter of the name to use as a computed field for indexing on the first letter of user names:

collection User {
  compute volume = (
    doc => { doc.name.toUpperCase().slice(0, 1) }
  )
}

Define a check constraint on a computed field

Validate that the result of the computed field is gold or standard:

collection Account {
  compute status = doc => {
    if (oc.points > 100) {
      "gold"
    } else {
      "standard"
    }
  }

  check validStatus (.status == "gold" || .status == "standard")
}

Limitations

Runtime errors in computed fields that are indexed aren’t reported. Be careful not to index on a computed field that does a read, such as using a field that references another document in the computed function. Computed fields that result in runtime errors, including divide by zero, return null. If indexed, the index entry is null. Using type checking should prevent these kind of indexes from being defined.

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!