You have too many Documents to display at once and want to build a traditional interface using offset pagination. You can use the Count
FQL function to determine the maximum number of pages, and the Drop
FQL function to offset the results.
Note: Offset pagination has significant performance costs which we outline below. In general, we recommend Fauna's built in cursor-based pagination, also known as "keyset" pagination.
Solution
The first thing to do is get the first page of data. You will need to know how many total records there are before making subsequent paginated queries.
Let(
{
// 1.
query: Documents(Collection("things")),
// 2.
page: Paginate(
Var("query"),
{
size: 10 // Page size
}
),
// 3.
count: Count(Var("query"))
},
// 4.
{
page: Var("page"),
count: Var("count")
} )
Documents(Collection("things"))
is the "Set" to be queried. You will need to use it more than once, so assign it to a variable in theLet
function.- Fetch the Documents with
Paginate
. Set thesize
argument to the number of Documents per Page - Use
Count
to compute the total size of the data Set. - Return an object containing both the count and the page, since both are required by the application.
Now that you have the first page of data and the total number of Documents in the Set, the application can "jump" to a specified page. Do this by fetching all Documents up to and including the page you require, then drop the documents from pages that you do not need.
Modify the previous query to fetch the Documents then drop what is not needed.
Let(
{
query: Documents(Collection("things")),
// modify to use `Drop`
page: Drop(
100, // Page size * skipped pages
Paginate(
Var("query"),
{
size: 110 // Page size + (Page size * skipped pages)
}
)
),
count: Count(Var("query"))
},
{
page: Var("page"),
count: Var("count")
} )
Limitations
Note that the Count
function is limited to iterating over 100000 items in a Set. If you have more than 100000 Documents in a Collection, the procedure outlined here will be insufficient. The count will need to be obtained through multiple separate requests.
The run time of
Count
is dependent on the number of elements in the underlying set or page — it’s linear, or O(n) . For very large sets or pages, executingCount
might result in a query timeout error, or “width” error.
Performance cost
The only way to jump to a precise number of Documents is to fetch a precise number and drop what you do not need, as we have outlined here. The more Documents there are, the more Documents you must fetch and drop to get just the Documents you need. As the number of Documents increases, the poorer these queries will perform.
This performance limitation is not unique to Fauna, it’s how an SQL-based database would make such a jump. The last FQL query can be compared to the equivalent SQL query:
SELECT * FROM things LIMIT 10 OFFSET 100
Alternative: Fauna’s native keyset pagination
Instead of the above procedure to fetch and drop unnecessary Documents, consider Fauna's native keyset pagination. Using cursors, you can navigate forward or backward through pages of data. You avoid the performance cost of traditional, offset pagination.
See our documentation for more information regarding pagination in Fauna:
Example
Simply use the Paginate
function. If there are more results than the size specified, Paginate returns an after
cursor to be used with subsequent queries.
Get the first page of Documents. The count is not required for subsequent queries
Paginate(
Documents(Collection("things")),
{
size: 10 // Page size
}
)
//result
{
// The "cursor"
after: [Ref(Collection("things"), "310623306674864192")],
data: data: [
Ref(Collection("things"), "307286552345234523"),
Ref(Collection("things"), "307286571965481024"),
Ref(Collection("things"), "307286583998939200"),
Ref(Collection("things"), "310623299765796928"),
Ref(Collection("things"), "310623301646942272"),
Ref(Collection("things"), "310623304138358848"),
Ref(Collection("things"), "310623304708784192"),
Ref(Collection("things"), "310623305260335168"),
Ref(Collection("things"), "310623305790914624"),
Ref(Collection("things"), "310623306285842496")
]
}
Get the next page of Documents. The count is not required for subsequent queries
Paginate(
Documents(Collection("things")),
{
after: [Ref(Collection("things"), "310623306674864192")], // cursor
size: 10 // Page size
}
)
//result
{
before: [Ref(Collection("things"), "310623306674864192")],
after: [Ref(Collection("things"), "310629118513250368")],
data: [
Ref(Collection("things"), "310623306674864192"),
Ref(Collection("things"), "310623307051302976"),
Ref(Collection("things"), "310623307488559168"),
Ref(Collection("things"), "310624660144263234"),
Ref(Collection("things"), "310624660144264258"),
Ref(Collection("things"), "310624660144265282"),
Ref(Collection("things"), "310624660144266306"),
Ref(Collection("things"), "310624772419486787"),
Ref(Collection("things"), "310624772419487811"),
Ref(Collection("things"), "310624772419488835")
]
}