-
-
Notifications
You must be signed in to change notification settings - Fork 15
Add pagination to FAQ #149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Following Level/abstract-level#124.
|
|
||
| There's no operation that can skip N amount of keys. You could manually skip keys by repeatedly calling `iterator.next()` or `iterator.nextv()` but that's expensive because those calls do fetch the data of every entry. Or you could seek to a key, but determining that key either requires an index (built by you) or for your keys to be monotonically increasing numbers. In other words, if you strictly need offset-based pagination, you will have to build it. | ||
|
|
||
| Alternatively, use the concept of cursors (also known as continuation tokens). Where the input is not an offset but instead the last key visited - i.e. `cursor`. To fetch a page of say 10 entries, create an iterator with `db.iterator({ gt: cursor, limit: 10 })`. Then use the last key of that iterator as the cursor of the next page. This approach better aligns with the characteristics of `abstract-level`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice summary, thanks. From an architectural perspective, the big challenge is: How and Where to store those cursors for each concurrent user?
And perhaps, if you could add links to other examples or libraries covering this issue, that would be great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming the application is served over HTTP, it'd be a URL query parameter. As an example, check GitHub notifications. The "Prev" and "Next" buttons in that UI have URLs like https://github.com/notifications?before=Y3Vyc29yOjI2 and /notifications?after=Y3Vyc29yOjUw respectively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the example @vweevers this is interesting, also challenging. How would you store cursor positions for each user session and where? Sounds some session-based short-term caching is required for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're not stored per se, in the same way you wouldn't store an offset and limit. Instead you embed them e.g. in the HTML served by your server (or similar). The process of generating such HTML is roughly:
// For the sake of brevity this:
// - Only implements going to a next page, not a previous
// - Ignores security, validation and escaping
// - Assumes the db uses utf8 key encoding
const requestUrl = '/foo?after=alice'
const queryParams = new URL(requestUrl).searchParams
const afterCursor = queryParams.get('after')
const limit = 10
let entries
if (afterCursor) {
// Grab N entries after the given key
entries = await db.iterator({ gt: afterCursor, limit }).all()
} else {
// Start from the beginning
entries = await db.iterator({ limit }).all()
}
// TODO: handle case where entries is empty
const lastKey = entries[entries.length - 1][0]
const html = `
<p>This page has ${entries.length} entries</p>
<p><a href="/foo?after=${lastKey}">Next page</a></p>
`There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. Thanks. This made "click" in my head.
Unfortunately, my app is SPA and doesn't render HTML pages on server side. In that case, I'd still have to pass them (like offset and limit) over API which will complicate things.
Perhaps time for me to migrate to React server-side rendering (SSR) ... 🤷
Following Level/abstract-level#124.