graphql / graphql/graphql-spec
client cursor - select by list of random keys - client side query batching
- Dominant language
- JavaScript
- Stars
- 14.6k
- Forks
- 1.2k
- PR merge metrics
- No merged PRs in 30d
Description
in SQL we can use the [SQL IN operator](https://www.w3schools.com/sql/sql_in.asp)
```
SELECT * FROM table
WHERE table.key IN UNNEST (@keylist)
```
where keylist is an array of keys like `[1, 1000, 50, 10000]` [[source](https://cloud.google.com/spanner/docs/sql-best-practices#write_efficient_queries_for_range_key_lookup)]
[ the UNNEST operator converts from array to vertical vector ]
the keys are random, not adjacent, not predictable
similar to a server-side pagination cursor, so we can say "client cursor"
use case in graphQL?
parallel pagination. broad first search / breadth-first search.
and: dealing with "read only" servers = we cannot change the server schema
workaround?
using a list of N keys, we can repeat the same selector [sub-query] for N times
and append the key index to all variable names
code sample in javascript
with the compound key `{ repoOwner, repoName }`
```js
const url = 'https://api.github.com/graphql'
// basic authentication - only for testing
const username = 'your_github_username'
const password = 'your_github_password'
// query object
const qObj = {
variables: {
repoOwner_0: "graphql",
repoName_0: "graphql-spec",
repoOwner_1: "graphql",
repoName_1: "graphql-js",
},
query: `query
(
$repoOwner_0: String!,
$repoName_0: String!,
$repoOwner_1: String!,
$repoName_1: String!,
){
r0: repository
(
owner: $repoOwner_0,
name: $repoName_0,
){
nameWithOwner
pushedAt
createdAt
stargazers { totalCount }
}
r1: repository
(
owner: $repoOwner_1,
name: $repoName_1,
){
nameWithOwner
pushedAt
createdAt
stargazers { totalCount }
}
}`
}
// send query
const headers = new Headers({
'Authorization': 'Basic '+btoa(username+":"+password),
'Content-Type': 'application/json',
'Accept': 'application/json',
})
fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(qObj),
})
.then(r => r.json())
.then(response => {
// print response
console.log('response = '+JSON.stringify(response, null, ' '))
})
```
result:
```txt
response = {
"data": {
"r0": {
"nameWithOwner": "graphql/graphql-spec",
"pushedAt": "2019-12-19T01:02:54Z",
"createdAt": "2015-07-01T01:26:56Z",
"stargazers": {
"totalCount": 12274
}
},
"r1": {
"nameWithOwner": "graphql/graphql-js",
"pushedAt": "2019-12-26T02:31:06Z",
"createdAt": "2015-06-30T12:16:50Z",
"stargazers": {
"totalCount": 15304
}
}
}
}
```
this selector
```
repository
(
owner: [$repoOwner_0, $repoOwner_1],
name: [$repoName_0, $repoName_1],
)
```
is not supported by the server:
> List dimension mismatch on variable $var_name and argument name ([String!]! / String!)
the fields [repository, repositry] must be renamed to [r0, r1], otherwise i get
> Field 'field_name' has an argument conflict
[on stackoverflow.com](https://stackoverflow.com/questions/59482349/graphql-client-side-query-batching-repeat-one-query-for-a-list-of-keys) i posted some more code, to generate such "concat queries"
inspired by [Batching queries with GraphQL & Python, by Julien Danjou](https://medium.com/mergify/batching-queries-with-graphql-94983b648c3c)
questions:
1. am i re-inventing the wheel here? and just too blind to see a better solution?
2. are there plans to implement such a feature? i could not even find a similar request
implementation sketch:
add a `@repeat` directive / pseudo-object / block / container
```js
const qObj = {
variables: {
repoOwner: ["graphql", "graphql" ],
repoName: ["graphql-spec", "graphql-js"],
constVar: 1234,
},
query: `query
(
$repoOwner: String!,
$repoName: String!,
$constVar: Int!,
){
# repeat this block for all keys
# = for the longest array, fill shorter arrays
@repeat {
repository
(
owner: $repoOwner,
name: $repoName,
)
{
pushedAt
stargazers { totalCount }
sampleobject(arg: $constVar)
}
}
}`
}
```
Contributor guide
Assessment
This issue has not been assessed yet.