-
Notifications
You must be signed in to change notification settings - Fork 179
Make connection adapter for array slices #31
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
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The existing `connectionFromArray` requires an array representing all
the records in a connection. Actually materializing such an array may be
prohibitively expensive when dealing with large connections (containing
thousands of items, or more). This largely defeats the purpose of
pagination, which is supposed to free us from having to deal with total
data sets when subsets suffice.
At the moment, it is possible to do some ghastly workarounds to trick
`connectionFromArray` into thinking it has a fully materialized array.
For example:
```
const paddedItems = new Array(offset).concat(...items);
if (count > offset + args.first) {
paddedResults.length++;
}
const connection = connectionFromArray(paddedItems, args);
```
In other words, we create an array with, say, 1000 empty slots, then
insert the 10 items actually retrieved, then increment the `length`
property on the array to trick `connectionFromArray` into thinking that
`hasNextPage` should be true.
This commit seeks to address this common use case by implementing
`connectionFromArraySlice`. This effectively allows you to fetch the
data for a subset of a connection, and pass that in with some metadata
indicating where the subsequence starts and how long the total array
would be if materialized, and get a connection object back.
We also add `connectionFromPromisedArraySlice`, for parity with
`connectionFromPromisedArray`, and we reimplement the existing
`connectionFromArray` to call through to the new function.
All specs pass, except one, which I actually think is a bug in the old
implementation, so I changed it. The test case uses `first: 0` with no
`before` or `after`. Previously, the code returned `hasNextPage: false`,
but according to the [spec](), it seems it should actually return
`true`.
[HasNextPage]() is specified as follows:
1. If first was not set, return false.
2. Let edges be the result of calling ApplyCursorsToEdges(allEdges,
before, after).
3. If edges contains more than first elements, return true.
4. Return false.
Given that there are no `before` or `after` calls, and
[ApplyCursorsToEdges]() specifies that `allEdges` should be returned in
this case, it seems we should be returning `true` as per step 3 above.
One final thing to note: I've exported `cursorToOffset` as it can be
useful to schema authors when they prepare array slices suitable for
passing in to `connectionFromArraySlice`.
[ApplyCursorsToEdges]: https://facebook.github.io/relay/graphql/connections.htm#ApplyCursorsToEdges()
[HasNextPage]: https://facebook.github.io/relay/graphql/connections.htm#HasNextPage()
[spec]: https://facebook.github.io/relay/graphql/connections.htm
I kept these separate from the parent commit for clarity: - Prefer trailing commas everywhere. - Sort exported types alphabetically within groups. - Use object short notation. - Prefer idiomatic `== null` check over explicit `=== null` and `=== undefined. - (Subjective) use a ternary in one place where it doesn't harm readability.
Because Travis hates those. https://travis-ci.org/graphql/graphql-relay-js/builds/84378975
Contributor
Author
|
Adding some Relay peeps: @steveluscher, @yungsters, @josephsavona. I'll add a separate test for |
Add some separate, light tests for `connectionFromArraySlice` and `connectionFromPromisedArraySlice`, rather than relying on them being tested indirectly (via `connectionFromArray` and `connectionFromPromisedArray`). Additionally, consolidated all the `arrayconnection.js` tests in a single file. [1]: At least, I think this will appease them.
- No need to capitalize initial letter of description. - Jettison implied "Correctly" qualifier. - Reduce unnecessary nesting of `describe` blocks (ie. siblingless nested `describe` blocks). - Suffix function names with `()`.
Fix things Travis was complaining about, one of which (an early `return` I'd left behind) was actually masking a real bug.
In building a schema using `connectionFromArraySlice`, I've found it
useful to have access to functionality like `getOffset`. This commit
exposes it as an export, after changing the name to be more descriptive,
do further distinguish it from the existing `cursorToOffset`.
Here's an example of the function in use:
resolve: async (_, args) => {
const offset = getOffsetWithDefault(args.after, -1) + 1;
const [articles, totalCount] = await articleLoader.readIndex(
args.first, offset
);
return connectionFromPromisedArraySlice(
articleLoader.loadMany(articles),
args,
{
sliceStart: offset,
arrayLength: totalCount,
},
);
},
Without the function, I end up reimplementing equivalent logic, such as:
const after = args.after && cursorToOffset(args.after);
const offset = isNaN(after) ? 0 : after + 1;
Contributor
|
👍 |
Explicitly test the case where the slice is oversized/undersized on the left/right.
wincent
added a commit
that referenced
this pull request
Oct 12, 2015
Make connection adapter for array slices
wincent
added a commit
to wincent/masochist
that referenced
this pull request
Nov 3, 2015
This is currently a pending PR upstream (graphql/graphql-relay-js#31). I naughtily just copied it into `node_modules`, so if, for whatever reason, it can't be merged, I'll need to switch to a fork or find an alternative.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The existing
connectionFromArrayrequires an array representing all the records in a connection. Actually materializing such an array may be prohibitively expensive when dealing with large connections (containing thousands of items, or more). This largely defeats the purpose of pagination, which is supposed to free us from having to deal with total data sets when subsets suffice.At the moment, it is possible to do some ghastly workarounds to trick
connectionFromArrayinto thinking it has a fully materialized array. For example:In other words, we create an array with, say, 1000 empty slots, then insert the 10 items actually retrieved, then increment the
lengthproperty on the array to trickconnectionFromArrayinto thinking thathasNextPageshould be true.This commit seeks to address this common use case by implementing
connectionFromArraySlice. This effectively allows you to fetch the data for a subset of a connection, and pass that in with some metadata indicating where the subsequence starts and how long the total array would be if materialized, and get a connection object back.We also add
connectionFromPromisedArraySlice, for parity withconnectionFromPromisedArray, and we reimplement the existingconnectionFromArrayto call through to the new function.All specs pass, except one, which I actually think is a bug in the old implementation, so I changed it. The test case uses
first: 0with nobeforeorafter. Previously, the code returnedhasNextPage: false, but according to the spec, it seems it should actually returntrue.HasNextPage is specified as follows:
Given that there are no
beforeoraftercalls, and ApplyCursorsToEdges specifies thatallEdgesshould be returned in this case, it seems we should be returningtrueas per step 3 above.One final thing to note: I've exported
cursorToOffsetas it can be useful to schema authors when they prepare array slices suitable for passing in toconnectionFromArraySlice.