Skip to content
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

Relay-style connections/pagination? #46

Open
jmcneese opened this issue May 15, 2020 · 6 comments
Open

Relay-style connections/pagination? #46

jmcneese opened this issue May 15, 2020 · 6 comments
Labels
enhancement New feature or request
Milestone

Comments

@jmcneese
Copy link

Sqlmancer looks great, but one missing functionality that would prevent me from adopting it is the lack of relay-compatible connection definition and pagination. Unless I am missing something in the documentation or code, I don't see any way to do this. Is this something that might be directly supported in the future, or made possible via a plugin?

@danielrearden danielrearden added this to the Possible features milestone May 15, 2020
@danielrearden danielrearden added the enhancement New feature or request label May 15, 2020
@danielrearden
Copy link
Owner

Good question. The library does not currently support relay-style pagination and connections,
although it might in the future. Currently, Sqlmancer supports simple pagination as well as returning "Page" types that include results, aggregate, hasMore fields. Implementing relay-style pagination is doable, but would take considerable effort and it's not at the top of the backlog at the moment. I appreciate you opening this issue though -- if there's additional demand for this feature, I'll definitely circle back to it sooner rather than later.

@zth
Copy link

zth commented Jun 12, 2020

I'm also interested in this feature and would like to help out if possible. @danielrearden what are your initial thoughts on the difficulties involved in implementing connections in Sqlmancer? What do you see as the big hurdles?

@danielrearden
Copy link
Owner

I think it would make sense to create a connection model method that would work roughly like the existing paginate method works now.

The biggest challenge is that we currently compose pretty much the entire JSON object necessary to resolve a root field inside the database itself -- with a few small exceptions, we don't modify what's returned by the database and we don't change any resolver logic. Ideally, we wouldn't deviate from that way of doing things just to implement this feature. But that also means constructing some pretty complex JSON structures via just SQL. There's also a number of questions to answer:

  • Is it necessary to support both forward and backward pagination when we already include an orderBy field?
  • How do the first/last arguments interact with the orderBy argument. Presumably using last would effectively invert the direction specified by orderBy.
  • How do we handle generating opaque cursors? Can this be done inside the database? How do we generate not only the cursors for each node, but also the startCursor and endCursor inside PageInfo efficiently?

@danielrearden
Copy link
Owner

@zth I'd really like to see this feature added, but it will definitely take some planning. I think the first question we need to answer is "what's the least amount of changes we'd need to make to be Relay-compliant". For example, if we can drop backward pagination, that may simplify things a bit.

I also want to be conscientious of the fact that Sqlmancer's API may be undergoing some pretty drastic changes in the near future, so it might be more efficient to implement any major new features like this against the new API. I'll have a better idea of the direction the library will be moving in after this week.

@tsiege
Copy link
Contributor

tsiege commented Jun 16, 2020

Seconding this!

@jmcneese
Copy link
Author

  • Is it necessary to support both forward and backward pagination when we already include an orderBy field?

yes, otherwise the implementation would not be Relay-compliant. also, the sql order doesn't necessarily have anything to do with whether you are pagination forward or backward... in fact the relay connection spec says that ordering should not change when changing pagination direction.

  • How do the first/last arguments interact with the orderBy argument. Presumably using last would effectively invert the direction specified by orderBy.

they aren't necessarily tied to each other, depending on the db. with mysql, it's necessary to change the overall orderBy direction for backwards paginating connections (when last arg is given), since that's the only way to get a limited result set since there's not negative offsets with mysql (afaik). then, when constructing the connection.edges array, the rows from the mysql resultset need to be reversed, so as to match the spec.

consider this (not relay-specific, but generic cursor-pagination) diagram:
1*IbwvqAOLhq7BHjQ_wJTGlQ

imagine this is a resultset that is order by createdDate DESC. forward pagination (using first and after args) is moving left to right (newest to oldest) along this stream. backward pagination (using last and before args) is moving right to left (oldest to newest). at no point would it make sense to the client querying this data set if the items ordering somehow changed. if we need to change actual db query ordering to deal with it as mentioned above, the client shouldn't be aware of that.

  • How do we handle generating opaque cursors? Can this be done inside the database?

I guess it could be done in the db, but then there'd be boilerplate or schema changes the developer would have to do to get this all to work. it would be better that the library would autocreate any cursors, depending on the primary table being queried, and the primary column being sorted on (so that this cursor information can later be interpreted when using after or before args). I have typically generated cursors like so: base64(${tableName}:${sortKey}:${record[sortKey]}). later, when constructing the query, it's just a matter of saying where ${cursorTableName}.${cursorSortKey} > ${cursorRecordValue} when forward paginating, or where ${cursorTableName}.${cursorSortKey} < ${cursorRecordValue}

How do we generate not only the cursors for each node, but also the startCursor and endCursor inside PageInfo efficiently?

const generateCursor = (tableName, sortKey, record) => Buffer.from(`${tableName}:${sortKey}:${record[sortKey]}`).toString('base64');
const startCursor = generateCursor(tableName, sortKey, records[0]);
const endCursor = generateCursor(tableName, sortKey, records[records.length - 1]);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants