在 Star Wars 宇宙中,一个 faction 有许多 ship。Relay 包含让操作一对多关联变得简单的函数,使用一个标准化的方式来表达这些一对多关联。这个标准的 连接 模型提供能过 连接 来处理 切片 和 分页 的方法。
让我们先选择 rebels,并查询它们的第一个 ship:
query RebelsShipsQuery { rebels { name, ships(first: 1) { edges { node { name } } } } }
yields
{ "rebels": { "name": "Alliance to Restore the Republic", "ships": { "edges": [ { "node": { "name": "X-Wing" } } ] } } }
使用 first
参数来把这对 ships
结果集切到只剩第一个。不过那如果我们想要用它做 分页 呢?在每一个边上,会有一个我们可以用来分页的 cursor。这次让我们查询前两个,并获取 cursor::
query MoreRebelShipsQuery { rebels { name, ships(first: 2) { edges { cursor node { name } } } } }
然后我们取回
{ "rebels": { "name": "Alliance to Restore the Republic", "ships": { "edges": [ { "cursor": "YXJyYXljb25uZWN0aW9uOjA=", "node": { "name": "X-Wing" } }, { "cursor": "YXJyYXljb25uZWN0aW9uOjE=", "node": { "name": "Y-Wing" } } ] } } }
注意,cursor 是一个 base64 字符串。这是一个前面见过的模式:服务器提醒我们这是一个明文的字符串。我们可以把这个字符串传回服务器作为 ships
字段的 after
参数,这将让我们在上一个结果的最后一个之后查询接下来的 ship:
query EndOfRebelShipsQuery { rebels { name, ships(first: 3 after: "YXJyYXljb25uZWN0aW9uOjE=") { edges { cursor, node { name } } } } }
给我们
{ "rebels": { "name": "Alliance to Restore the Republic", "ships": { "edges": [ { "cursor": "YXJyYXljb25uZWN0aW9uOjI=", "node": { "name": "A-Wing" } }, { "cursor": "YXJyYXljb25uZWN0aW9uOjM=", "node": { "name": "Millenium Falcon" } }, { "cursor": "YXJyYXljb25uZWN0aW9uOjQ=", "node": { "name": "Home One" } } ] } } }
太棒了!让我们继续并获取后四个!
query RebelsQuery { rebels { name, ships(first: 4 after: "YXJyYXljb25uZWN0aW9uOjQ=") { edges { cursor, node { name } } } } }
yields
{ "rebels": { "name": "Alliance to Restore the Republic", "ships": { "edges": [] } } }
嗯。没有 ship;猜测应该是在系统中 rebel 只有五个 ship。如果能知道我们已经碰到连接的末端的话很不错,就不需要再一次往返来验证这件事。连接模型用一个叫做 PageInfo
的 类来使用这个方法。因此让我们发送两个再一次的帮我们把 ships 取回,但这次要查询 hasNextPage
:
query EndOfRebelShipsQuery { rebels { name, originalShips: ships(first: 2) { edges { node { name } } pageInfo { hasNextPage } } moreShips: ships(first: 3 after: "YXJyYXljb25uZWN0aW9uOjE=") { edges { node { name } } pageInfo { hasNextPage } } } }
我们取回
{ "rebels": { "name": "Alliance to Restore the Republic", "originalShips": { "edges": [ { "node": { "name": "X-Wing" } }, { "node": { "name": "Y-Wing" } } ], "pageInfo": { "hasNextPage": true } }, "moreShips": { "edges": [ { "node": { "name": "A-Wing" } }, { "node": { "name": "Millenium Falcon" } }, { "node": { "name": "Home One" } } ], "pageInfo": { "hasNextPage": false } } } }
因此在第一次对 ships 的查询,GraphQL 告诉我们有下一页,但是在下一次,它告诉我们已经碰到连接的末端。
Relay 使用这所有的方法来构建连接相关的抽象,让这些能有效的运作而不需要在客户端上手动的管理 cursor。
有关服务器应如何运行的完整详细信息,请参见 GraphQL Cursor 连接 规范。