【问题标题】:GraphQL: How to implement pagination with graphQL-java?GraphQL:如何使用 graphQL-java 实现分页?
【发布时间】:2017-11-18 09:18:44
【问题描述】:

目前,我发现graphql-java 库中没有对分页的现有支持。它确实有一些基本的中继支持,我们可以在其中创建一个connection,这是 Facebook 推荐的实现分页的方式。
This 是有助于实现这一目标的方法。但是,由于没有文档,我很难理解这个函数是如何工作的。如果他们已经有一个现有模型允许使用 graphql-java 库进行Adddeletefetch 等基本查询,那么有人可以分解他们为添加分页支持而采取的步骤吗?

【问题讨论】:

    标签: java pagination graphql graphql-java


    【解决方案1】:

    您甚至不需要中继连接来支持分页。您的查询可以简单地接受页码和大小(或限制/偏移)作为参数并返回一个列表 - 完成。 但是,如果您想要中继连接,例如Book 类型,您会执行以下操作:

    Relay relay = new Relay();
    GraphQLOutputType book = ...; //build your normal Book object type
    GraphQLObjectType bookEdge = relay.edgeType(book.getName(), book, null, Collections.emptyList());
    GraphQLObjectType bookConnection = relay.connectionType(book.getName(), bookEdge, Collections.emptyList());
    

    因此,您将拥有一个符合 Relay connection specBookConnection 类型。

    对于基本 GraphQL 的示例,您有一个简单的 Web 应用程序 here

    连接规范自然适合支持基于光标的分页的数据存储,但在与不同的分页样式一起使用时需要一些创造力。

    1) 如果您希望使用简单的基于偏移量的分页,您可以决定将after 视为偏移量(意味着将传递一个数字),并将first 视为限制:

    SELECT * FROM ORDER BY timestamp OFFSET $after LIMIT $first
    

    beforelast 相同,只是方向不同。

    2) 另一种方法是将after/before 视为排序列的最后看到的值(因此将传递实际(混淆)值):

    SELECT * FROM ORDER BY timestamp WHERE timestamp > $after LIMIT $first
    

    我还建议您看看我的项目 graphql-spqran example app,这使得开发 GraphQL API 变得非常简单。

    例如,您将创建这样的分页结果:

    public class BookService {
        @GraphQLQuery(name = "books")
        //make sure the argument names and types match the Relay spec
        public Page<Book> getBooks(@GraphQLArgument(name = "first") int first, @GraphQLArgument(name = "after") String after) {
            //if you decide to fetch from a SQL DB, you need the limit and offset instead of a cursor
            //so, you can treat "first" as count as "after" as offset
            int offset = Integer.valueOf(after);
            List<Book> books = getBooksFromDB(first, offset);
            Page<Book> bookPage = PageFactory.createOffsetBasedPage(books, totalBookCount, offset);
            return bookPage;
        }
    }
    

    还有很多其他方法可以创建Page 实例,这只是最直接的一种。

    然后你会从你的 Java 类生成一个模式:

    GraphQLSchema schema = new GraphQLSchemaGenerator()
           .withOperationsFromSingleton(new BookService())
           .generate();
    GraphQL graphQL = GraphQLRuntime.newGraphQL(schema).build();
    

    并执行查询:

    ExecutionResult result = graphQL.execute("{books(first:10, after:\"20\") {" +
                    "   pageInfo {" +
                    "       hasNextPage" +
                    "   }," +
                    "   edges {" +
                    "       cursor, node {" +
                    "           title" +
                    "}}}}");
    

    但是,如果您不使用 Relay,那么真的没有必要让事情变得过于复杂。如果您的存储自然支持基于光标的分页,那就去吧。如果没有,只需使用简单的限制/偏移参数并返回一个列表,然后忘记连接规范。创建它是为了使 Relay 能够在各种场景中自动管理分页,因此如果您不使用 Relay 和/或具有基于光标的分页的数据库,它几乎总是完全矫枉过正。

    【讨论】:

    • 精彩的答案!
    • @user3728233 很高兴为您提供帮助:)
    • 对于大多数分页场景,您仍然想知道总计数。只需添加跳过/限制变量,您将如何处理?
    • @Donuts Relay 页面已经有总数(目前在 SPQR 中缺少,但很快就会添加,也可以轻松手动添加)。否则,您可以返回一个封装了结果列表和计数的对象类型。
    • @kaqqao 我指的是不使用中继的选项。所以是的,我看到了 2 个选项: 1,您的查询返回一个类型,该类型既有结果数组又有总数。或 2,在您的 graphql 模式中,您的 Query 类型可能包含计数查询的附加映射——因此您可以有书籍,然后是 booksCount(它返回一个 int)。选项 1 的缺点是,对于每种可以分页的类型,您必须有一个额外的类型来处理分页,因为 graphql 不处理泛型。就像我的后端不能将 PagedResult 映射到 graphql 中的 PagedResult 类型
    猜你喜欢
    • 2018-10-18
    • 2018-07-26
    • 2018-12-06
    • 2019-10-12
    • 2016-12-20
    • 2018-12-09
    • 2020-03-21
    • 2019-07-14
    • 2016-12-20
    相关资源
    最近更新 更多