使用连接和分页时GraphQL和数据库之间的关系?

Relationship between GraphQL and database when using connection and pagination?

使用 Relay 设置分页非常容易,但是有一个小细节我不清楚。

我代码中的两个相关部分都标有注释,其他代码用于附加上下文。

const postType = new GraphQLObjectType({
  name: 'Post',
  fields: () => ({
      id: globalIdField('Post'),
      title: {
        type: GraphQLString
      },
  }),
  interfaces: [nodeInterface],
})

const userType = new GraphQLObjectType({
  name: 'User',
  fields: () => ({
      id: globalIdField('User'),
      email: {
        type: GraphQLString
      },
      posts: {
        type: postConnection,
        args: connectionArgs,
        resolve: async (user, args) => {
          // getUserPosts() is in next code block -> it gets the data from db
          // I pass args (e.g "first", "after" etc) and user id (to get only user posts)
          const posts = await getUserPosts(args, user._id)
          return connectionFromArray(posts, args)
        }
      },
  }),
  interfaces: [nodeInterface],
})

const {connectionType: postConnection} = 
              connectionDefinitions({name: 'Post', nodeType: postType})

exports.getUserPosts = async (args, userId) => {
    try {
      // using MongoDB and Mongoose but question is relevant with every db
      // .limit() -> how many posts to return
      const posts = await Post.find({author: userId}).limit(args.first).exec()
      return posts
    } catch (err) {
      return err
    }
}

我的困惑原因:


这是什么逻辑?

我想到的一个解决方案是将 +1 添加到 getUserPosts 中的 first 值,它将检索一个多余的项目并且 hasNextPage 可能会起作用。但这感觉像是 hack 并且总是有多余的项目 returned - 如果有很多 connections 和请求,它会增长得相对较快。

我们期望这样破解它吗? return 全部 的结果是预期的吗?

还是我误解了数据库和 GrahpQL / Relay 之间的整个关系?


如果我使用 FB DataLoader 和 Redis 会怎么样?这会改变那个逻辑吗?

Cause of my confusion

graphql-relay-js 库的效用函数 connectionFromArray 并不能解决各种分页需求。我们需要根据我们首选的分页模型调整我们的方法。

connectionFromArray 函数从给定的数组中导出 hasNextPagehasPrevisousPage 的值。因此,您在“我的困惑的原因”中观察到和提到的是预期的行为。

至于你对是否加载所有数据感到困惑,这取决于你手头的问题。在以下几种情况下加载所有项目可能有意义,例如:

  • 项目数量少,您可以负担得起存储这些项目所需的内存。
  • 经常请求这些项目,您需要缓存它们以便更快地访问。

两种常见的分页模型是编号页面和无限滚动。 GraphQL 连接规范对分页模型没有固执己见,并且允许这两种模型。

对于编号页面,您可以在您的 GraphQL 类型中使用一个额外的字段 totalPost,它可用于在您的 UI 上显示编号页面的链接。在后端,您可以使用 skip 之类的功能来仅获取所需的项目。字段 totalPost 和当前页码消除了对 hasNextPagehasPreviousPage.

的依赖

对于无限滚动,您可以使用 cursor 字段,它可以用作查询中 after 的值。在后端,您可以使用 cursor 的值来检索下一个项目(first 的值)。请参阅 Relay documention on GraphQL connection. See about GraphQL connection and cursor. See this and this 博文中使用游标的示例,这将有助于您更好地理解 cursor.

的思想

What's the logic here?

Are we expected to hack it like that?

不,理想情况下,我们不应该破解并忘记它。这将在项目中留下技术债务,从长远来看可能会导致更多问题。您可以考虑将自己的函数实现到 return 连接对象。在 graphql-relay-js.

array-connection 实现中,您将了解如何做到这一点

Is it expected the return all the results?

同样,取决于问题。


What if I used FB DataLoader and Redis? Would that change anything about that logic?

您可以使用 facebook 数据加载器库来缓存和批处理您的查询。 Redis 是缓存结果的另一种选择。如果您加载 (1) 使用数据加载器的所有项目或将所有项目存储在 Redis 中,并且 (2) 项目是轻量级的,您可以轻松创建所有项目的数组(遵循 KISS 原则)。如果项目是重量级的,创建数组可能是一项昂贵的操作。