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.

set.fold()

Reduce the Set to a single, accumulated value by applying a provided function to each element. Iterates through elements from left to right. Uses a provided seed as the initial value.

Signature

fold(seed: B, reducer: (B, A) => B) => B

Description

Iterates through each element in a Set to perform a rolling operation. For example, you can use fold() to calculate a rolling sum, concatenate elements, or perform complex transformations.

fold() calls a reducer callback function on every element of the Set from left to right. The reducer function takes two arguments:

  • The accumulator that holds the running result from previous iterations. For the first iteration, a seed value serves as the initial accumulator.

  • The current element’s value from the Set.

The method returns the result of the last iteration. The calling Set isn’t changed.

Eager loading

This method uses eager loading and requires a read of each document in the calling Set. For large Sets, this may result in poor performance and high costs.

Performance hint: full_set_read

Queries that call this method on a document Set emit a performance hint, if enabled. For example, the following query:

// Use `fold()` to sum stock counts
let stockCounts = Product.all().map(doc => doc.stock)
stockCounts.fold(0, (a, b) => a + b)

Emits the following hint:

performance_hint: full_set_read - Using fold() causes the full set to be read. See https://docs.fauna.com/performance_hint/full_set_read.
at *query*:3:17
  |
3 | stockCounts.fold(0, (a, b) => a + b)
  |                 ^^^^^^^^^^^^^^^^^^^^
  |

To address the hint, use set.take() to explicitly limit the size of the calling Set to fewer than 100 documents:

// Limit the doc Set's size using `take()`
let stockCounts = Product.all().take(20).map(doc => doc.stock)
stockCounts.fold(0, (a, b) => a + b)

This applies even if the original, unbounded Set contains fewer than 100 documents.

Alternatively, you can rewrite the query to avoid calling the method.

Fold family methods

FQL supports several methods for folds, which iteratively reduce a Set to a single value. These methods include:

The methods are similar but have the following differences:

Parameters

Parameter Type Required Description

seed

Generic

Yes

Initial accumulator value provided to the reducer function.

reducer

Function

Yes

Anonymous FQL function to call on each element of the Set.

Reducer function arguments:

Parameter Type Required Description

accumulator

Generic

Yes

Value returned by the previous reducer function call. On the first call, seed is passed as the accumulator.

current

Generic

Yes

The current element’s value.

Return value

Type Description

Generic

Result of the last reducer function call. For an empty Set, the seed is returned.

Examples

Basic example

// `toSet()` converts an Array to a Set.
let set = [1, 2, 3].toSet()
set.fold(100, (value, elem) => value + elem)
106

Group by operation

FQL doesn’t provide a built-in GROUP BY operation. However, you use fold() in an anonymous FQL function or a user-defined function (UDF) to achieve the same result.

As an FQL function:

// Defines an anonymous `groupBy()` function.
// `groupBy()` two arguments:
// * `set`: Set or Array containing data to group
// * `key_fn`: Grouping key for each element
let groupBy = (set, key_fn) => {
  // Calls the `fold()` function on the `set`
  // Set or Array.
  set.fold(
    // Start with an empty object.
    {},
    (acc, val) => {
      // For each value, get a key using the `key_fn` arg.
      let key = key_fn(val)

      let existing_group = acc[key] ?? []

      // Append the current value to the Set or
      // Array for that key.
      let new_group = existing_group.append(val)

      let new_entry = Object.fromEntries([
        [key, new_group]
      ])

      // Return an object with grouped results.
      Object.assign(acc, new_entry)
    }
  )
}

// Call the `groupBy()` function.
// Groups `Product` documents by category name.
groupBy(Product.all(), .category!.name)

You can also define a groupBy() UDF. This lets you reuse the function across multiple queries.

You create and manage a UDF as an FSL function schema:

// Defines the `groupBy()` UDF.
// `groupBy()` two arguments:
// * `set`: Set or Array containing data to group
// * `key_fn`: Grouping key for each element
function groupBy (set, key_fn) {
  // Calls the `fold()` function on the `set`
  // Set or Array.
  set.fold(
    // Start with an empty object.
    {},
    (acc, val) => {
      // For each value, get a key using the `key_fn` arg.
      let key: String = key_fn(val)

      let existing_group = acc[key] ?? []

      // Append the current value to the Set or
      // Array for that key.
      let new_group = existing_group.append(val)

      let new_entry = Object.fromEntries([
        [key, new_group]
      ])

      // Return an object with grouped results.
      Object.assign(acc, new_entry)
    }
  )
}

You can create and manage schema using any of the following:

For additional examples using the groupBy() UDF, see Group By: Aggregate data in Fauna.

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!