Set.paginate()

Get a page of paginated results using an after cursor.

Signature

Set.paginate((cursor: String, size: Number) => { data: Array<Any>, after: String | Null })

Description

Gets a page of paginated results using an after cursor.

The default page size of 16 can be changed using the set.paginate() or set.pageSize() method, in the range 1 to 16000 (inclusive).

The cursor is stable in the sense that pagination through a Set is done for a fixed snapshot time, giving you a view of your data as it existed across the whole Set at the instant you started paginating. For example, given Set [a, b, c] when you start paginating, one item at a time, even if you delete item c after you started reading the Set, item c is returned.

The exception is if the history is no longer available for the deleted item because history_days is set to the default value of 0 or is less than the minimum valid time needed. In that case, the deleted item is not returned with the paginated results and an error is returned:
Requested timestamp <time> less than minimum allowed timestamp..

A cursor is valid for history_days plus 15 minutes.

Invalid cursor

If you pass an invalid or expired after cursor to Set.paginate(), the Fauna Core HTTP API’s Query endpoint returns a query runtime error with an invalid_cursor error code and a 400 HTTP status code:

{
  "error": {
    "code": "invalid_cursor",
    "message": "Cursor cursor is invalid or expired."
  },
  ...
}

Fauna’s client drivers include classes for query runtime errors:

Parameters

Parameter Type Required Description

cursor

String

Yes

Encoded string representing a pagination cursor, a Base64-encoded object representing the state of a Set during pagination.

size

Number

Maximum number of Set elements to return in a page.

Return value

Type Description

An object with:

Field Type Description

data

Array representing a page of results. The number of elements is limited by the size parameter.

after

Cursor for the next page of results. The cursor is valid for history_days plus 15 minutes.

If no additional pages exist, after is Null.

Examples

Basic example

  1. The page includes a pagination cursor:

    Product.all().pageSize(2) { name }
    {
      data: [
        {
          name: "cups"
        },
        {
          name: "donkey pinata"
        }
      ],
      after: "hdWC1Y..."
    }
  2. Use the cursor to access the next page:

    Set.paginate("hdWC1Y...")
    {
      data: [
        {
          name: "pizza"
        },
        {
          name: "avocados"
        }
      ],
      after: "hdWC1Y..."
    }

Paginate in reverse

Paginated queries don’t include a before cursor. Instead, you can use a range search and document IDs or other unique field values to paginate in reverse. For example:

  1. Run an initial paginated query:

    Product.all().pageSize(2)
    {
      data: [
        {
          id: "111",
          coll: Product,
          ts: Time("2099-08-16T14:00:59.075Z"),
          name: "cups",
          description: "Translucent 9 Oz, 100 ct",
          price: 698,
          stock: 100,
          category: Category("123")
        },
        {
          id: "222",
          coll: Product,
          ts: Time("2099-08-16T14:00:59.075Z"),
          name: "donkey pinata",
          description: "Original Classic Donkey Pinata",
          price: 2499,
          stock: 50,
          category: Category("123")
        }
      ],
      after: "hdW..."
    }
  2. Page forward until you find the document you want to start reversing from:

    Set.paginate("hdW...")

    Copy the ID of the document:

    {
      data: [
        {
          id: "333",
          coll: Product,
          ts: Time("2099-08-16T14:00:59.075Z"),
          name: "pizza",
          description: "Frozen Cheese",
          price: 499,
          stock: 100,
          category: Category("456")
        },
        {
          // Begin reverse pagination from this doc ID.
          id: "444",
          coll: Product,
          ts: Time("2099-08-16T14:00:59.075Z"),
          name: "avocados",
          description: "Conventional Hass, 4ct bag",
          price: 399,
          stock: 1000,
          category: Category("789")
        }
      ],
      after: "hdW..."
    }
  3. To reverse paginate, run the original query with:

    // "444" is the ID of the document to reverse from.
    Product.all({ to: "444" }).reverse().pageSize(2)
    {
      data: [
        {
          // The results of the previous query are reversed.
          id: "444",
          coll: Product,
          ts: Time("2099-08-16T14:00:59.075Z"),
          name: "avocados",
          description: "Conventional Hass, 4ct bag",
          price: 399,
          stock: 1000,
          category: Category("789")
        },
        {
          id: "333",
          coll: Product,
          ts: Time("2099-08-16T14:00:59.075Z"),
          name: "pizza",
          description: "Frozen Cheese",
          price: 499,
          stock: 100,
          category: Category("456")
        }
      ],
      after: "hdW..."
    }

    To get historical snapshots of documents at the time of the original query, use an at expression:

    // Time of the original query.
    let originalQueryTime = Time.fromString("2099-08-16T14:30:00.000Z")
    at (originalQueryTime) {
      // "444" is the ID of the document to reverse from.
      Product.all({ to: "444" }).reverse().pageSize(2)
    }
  4. Repeat the previous step to continue paginating in reverse:

    Product.all({ to: "333" }).reverse().pageSize(2)

Traverse Set references

Sets are not persistable. You can’t store a Set as a field value or create a field definition that accepts a Set.

Instead, you can use a computed field to define a read-only function that dynamically fetches a Set:

collection Customer {
  ...
  // Computed field definition for the `orders` field.
  // `orders` contains a reference to a Set of `Order` collection documents.
  // The value is computed using the `Order` collection's
  // `byCustomer()` index to get the customer's orders.
  compute orders: Set<Order> = ( customer => Order.byCustomer(customer))
  ...
}

If the field isn’t projected, it contains an after pagination cursor that references the Set:

// Get a `Customer` document.
Customer.byEmail("alice.appleseed@example.com").first()
{
  id: "111",
  coll: Customer,
  ts: Time("2099-10-22T21:56:31.260Z"),
  cart: Order("412483941752112205"),
  // `orders` contains an `after` cursor that
  // references the Set of `Order` documents.
  orders: "hdW...",
  name: "Alice Appleseed",
  email: "alice.appleseed@example.com",
  address: {
    street: "87856 Mendota Court",
    city: "Washington",
    state: "DC",
    postalCode: "20220",
    country: "US"
  }
}

To materialize the Set, project the computed field:

let customer = Customer
                .where(.email == "alice.appleseed@example.com")
                .first()

// Project the `name`, `email`, and `orders` fields.
customer {
  name,
  email,
  orders
}
{
  name: "Alice Appleseed",
  email: "alice.appleseed@example.com",
  orders: {
    data: [
      {
        id: "412483941752112205",
        coll: Order,
        ts: Time("2099-10-22T21:56:31.260Z"),
        items: "hdW...",
        total: 5392,
        status: "cart",
        customer: Customer("111"),
        createdAt: Time("2099-10-22T21:56:31.104083Z"),
        payment: {}
      },
      ...
    ]
  }
}

Alternatively, you can pass the after cursor to Set.paginate():

Set.paginate("hdW...", 2)
{
  // Returns a materialized Set of `Order` documents.
  data: [
    {
      id: "412483941752112205",
      coll: Order,
      ts: Time("2099-10-22T21:56:31.260Z"),
      items: "hdW...",
      total: 5392,
      status: "cart",
      customer: Customer("111"),
      createdAt: Time("2099-10-22T21:56:31.104083Z"),
      payment: {}
    },
    ...
  ]
}

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!