-
Notifications
You must be signed in to change notification settings - Fork 220
How to apply pagination on big database ? #6
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
The builder alone can't answer to this issue. To be possible the logic of calculating the offset and length should be separate from the spliter it self. So you can use this parameter to get your results from db and then the builder could finish the work. |
After a lot of reading, i came with this answer to my problem:
This last one, as said in facebook/relay#540, cursor "pagination model is optimized for infinite scrolling" and data that are heavily update. Think about a list with lot of news comments, etc. like in Facebook. If we scroll, we want to be sure to not repeat the same news: cursors is made for that, to be sure to continue were the last request ended, ever if there is some inserted or delete entries. Its important to noticed that cursor pagination method may apply to an natural ordering and unique field (id, micro timestamp, etc.). Limit/offset pagination could be a solution when there is no such modifications on entries. It be acceptable if we get the same entry on the next page, because there is a new one during navigation. So for now, I just implemented the easier solution. Here is an example of an entry point: users:
type: "[User]"
args:
limit:
description: "Pagination limit."
type: "Int"
offset:
description: "Pagination limit offset."
type: "Int"
orderBy:
description: "Ordering infos"
type: "[String]"
filterExpression:
description: "Filter expression to apply to users"
type: "String"
resolve: "@=service('loyalty_user.user_manager').find(args)" This is a temporary solution, as i must resolve a way to get back the number of results to send to client. Finally, i have an idea in mind to apply cursor pagination, but it need some heavy tests for performances:
Maybe someone has a point of view or a better solution ? PS: I don't precise the way i made filters (filterExpression - https://github.com/K-Phoen/RulerZBundle) but i really opened to deal with that too. |
I have an idea on the subject but a must first POC to be sure it works. |
You can get a working example here mcg-web/graphql-symfony-doctrine-sandbox. In this file you can see how I create a pagination on the faction ships. This query can be a good start: query RebelsShipConnectionQuery {
fake {
name
first: ships(first: 5) {
...shipConnection
}
withLastAndAfter: ships(last: 3, after: "YXJyYXljb25uZWN0aW9uOjE=") {
...shipConnection
}
withLastAndBeforeAndAfter: ships(last: 3, before: "YXJyYXljb25uZWN0aW9uOjQ=", after: "YXJyYXljb25uZWN0aW9uOjE=") {
...shipConnection
}
originalShips: ships(first: 2) {
...shipConnection
}
moreShips: ships(first: 3, after: "YXJyYXljb25uZWN0aW9uOjE=") {
...shipConnection
}
}
}
fragment shipConnection on ShipConnection {
sliceSize
edges {
cursor
node {
name
}
}
pageInfo {
hasNextPage
hasPreviousPage
}
} |
Thanks for your POC. I came the same way in my side. It works while there is no order by on query nor deletion on ships. So |
The purpose of cursors is to drastically improve fetching performance on large paginated connections. Let's say we want to retrieve a list of events ordered by SELECT `id`, `date`, `type` FROM `events`
WHERE `user_id` = :viewer
ORDER BY `date` DESC
LIMIT :offset, :page_size If you want to read the 20th page, your DBAL needs to skip the 19th first pages which is really expensive, even with caching. In contrario, in Relay, you can specify a cursor with
The following request could be translated by your GraphQL implementation into this SQL query: SELECT `id`, `date`, `type` FROM `events`
WHERE `user_id` = :viewer
AND `id` > :after -- This is the key part
ORDER BY `date` DESC
LIMIT 0, :page_size -- Here it starts at 0 See the optimization? The database will only read |
I really see the optimization, but i think it could not apply to my use case. To work, cursor pagination must apply to data that dates is linked to id.
Dates does not follow id order. If i sort by date DESC, i get :
If i apply a pagination to show the 3 first items, i get:
As I understand, if we want the next set of items, we query for first: 3 after: 8. Cursor is based on last ID of our current set. SELECT `id`, `date`, `type` FROM `events`
WHERE `id` > 8
ORDER BY `date` DESC
LIMIT 0, 3 But WHERE
But the good result for the next page should be:
So id order need to follow sort order (here date). |
We could also used |
I'm closing this because not really a generic solution to this, feel free to reopen if needed :) |
If I want to apply pagination to some objects, the better way seems to be compatible with Relay.
I have read all specifications about relay, and seen that you have already implemented Connections and Nodes (this last one seems not to be mandatory, but hardly advise).
I think i must use
ConnectionBuilder::connectionFromArraySlice
directly, with the result of a query to my database (with doctrine) and the arguments (first, after, etc.). Must i pass something special like total number of entities ?I have read too a discussion about adding arguments for filtering/ordering. graphql/graphql-relay-js#20
Have you some advices for this ?
The text was updated successfully, but these errors were encountered: