gpt4 book ai didi

sql - 基于 Graphql 嵌套游标的分页、解析器和 SQL 查询

转载 作者:行者123 更新时间:2023-12-05 05:43:26 26 4
gpt4 key购买 nike

有没有办法实现graphql cursor based pagination以高效的方式使用嵌套分页查询?

假设我们有 3 种伪 graphql 类型:

type User {
id: ID!
books: [Book!]!
}

type Book {
isbn: ID!
pages: [Page!]!
}

type Page {
num: ID!
}

为简单起见,假设用户可以阅读数千本书,每本书可以有数百页。

用户数据库表:

id
1
2
3
...etc

book 数据库表:

isbn
1
2
3
...etc

页面数据库表:

num
1
2
3
...etc

user_book 数据库表:

user_id | book_isbn
1 | 2
1 | 3
2 | 1
...etc

book_page 数据库表:

book_isbn | page_num
1 | 1
1 | 2
2 | 3
...etc

我们无法加载百万用户、他们阅读的数千本书和数百页,因此我们进行了分页。假设我们要加载 100 个用户,他们每个人阅读的前 50 本书和每本书的前 10 页:

{
users(first: 100, after: "9") {
edges {
node {
id
books(first: 50) {
edges {
node {
id
pages(first: 10) {
node {
id
}
}
}
}
}
}
}
}

我们可以加载用户 10..110,然后为每个用户 books 解析器使用父用户 id 加载 50 本书,并且每个book pages 解析器加载 10 页:

// pseudo code
const resolvers = {
// get users from 10 to 110
users: (_, args) => `SELECT * FROM user WHERE id > args.after LIMIT 100`,
User: {
books: (root) => `SELECT * FROM book JOIN user_book ub WHERE ub.user_id = root.id LIMIT 50`
},
Book: {
pages: (root) => `SELECT * FROM page JOIN book_page bp WHERE bp.book_isbn = root.isbn LIMIT 10`
}
};

问题1 SQL

我们执行 1 个数据库请求以获取 100 个用户,然后执行 100 个请求以获取每个用户的书籍,最后执行 500 个请求以获取每本书的页面(100 个用户 * 50 本书)。一次查询的总 601 数据库请求:(

如果我们没有分页,我们可以使用数据加载器将用户 ID 批处理到书籍解析器中的数组中,并在页面解析器中将书籍 ID 批处理为仅执行 3 个数据库请求。但是,如果拥有用户 ID 数组 10...100,我们如何才能为每个用户查询 50 本书,同时为页面查询同样的内容呢?

问题2 Graphql

我们不能对嵌套的分页查询(书籍和页面)使用游标,因为我们不知道它们,即使我们知道我们也无法为每个组单独传递它们:

{
users(first: 100, after: "9") {
edges {
node {
id
// this will not work because for each user books group we need it's own cursor
books(first: 50, after: "99") {
edges {
node {
id
}
}
}
}
}
}

我解决这个问题的唯一想法是只允许分页作为顶级查询,永远不要将其用作类型字段。

最佳答案

您的第一个问题主要取决于您的数据库及其可用查询。您基本上想要类似“为每个用户选择前 n 本书” 的东西。这可以在 SQL 中完成。请参阅:Using LIMIT within GROUP BY to get N results per group?

第二个问题在我看来有点做作:在初始页面加载时,您获取并显示 100 个用户,每个用户 50 本书,每本书 10 页。

现在从 UI 的角度考虑:每个显示的用户都会有一个按钮来为该用户获取接下来的 50 本书。虽然不需要嵌套分页。您可以简单地使用 books 查询端点。

我没有看到您想要“为每个显示的用户获取接下来的 50 本书” 的常见现实场景。

graphql 模式看起来像这样(我将省略 pages,因为它本质上与 books 相同):

Query {
users(first: Int, after: UserCursor): UserConnection
books(first: Int, after: BookCursor): BookConnection
}

type UserConnection {
edges: [UserEdge]
}

type UserEdge {
cursor: UserCursor
node: User
}

type User {
id: ID
books(first: Int): BookConnection # no `after` argument here
}

type BookConnection {
edges: [BookEdge]
}

type BookEdge {
cursor: BookCursor # here comes the interesting part
node: Book
}

type Book {
id: ID
}

具有挑战性的部分是:“当您将此游标传递给 Query.books 时,BookEdge.cursor 必须是什么样子才能使嵌套分页工作?”

简单的回答:光标必须另外包含 User.id,因为您的服务器代码必须能够为给定的用户 id选择接下来的 50 本书 在光标之后,例如(简化)SELECT * FROM book WHERE user = 42 LIMIT 50 OFFSET 50

如您所见,此 GraphQL 模式没有任何嵌套分页。它只有嵌套游标。这些游标不关心它们是来自架构的顶层(如 Query.users)还是来自某个嵌套级别(如 Query.users.edges.node.books)。

唯一重要的部分是游标必须包含服务器代码获取正确数据所需的所有信息。在这种情况下,它可能类似于 cursor={Book.id, User.id}。需要 Book.id 才能获取此 id 之后的下 50 本书”User.id 需要获取“仅来自该特定用户的书籍”

关于sql - 基于 Graphql 嵌套游标的分页、解析器和 SQL 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71833652/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com