连接

在 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 连接 规范。