2

使用 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
    }
}

我困惑的原因:

  • 如果我传递参数并在 db 查询中使用它first限制返回的结果,hasNextPage总是. 这是有效的,但它会破坏(如果你使用) falsehasNextPagehasPreviousPagelast
  • 如果我不传递first参数并且不在数据库查询中使用它来限制返回的结果,hasNextPage则按预期工作,但它将返回我查询的所有项目(可能是数千个)
    • 即使数据库在同一台机器上(对于更大的应用程序而言并非如此),这似乎非常、非常、非常低效和糟糕。请证明我错了!
    • 据我所知,GraphQL 没有任何服务器端缓存,因此没有必要返回所有结果(即使有,用户也不会浏览 100% 的内容)

这里的逻辑是什么?

我想到的一种解决方案是增加+1value firstin getUserPosts,它将检索一个多余的项目并且hasNextPage可能会起作用。但这感觉就像是一种黑客行为,并且总是有多余的物品被退回——如果有很多connections和请求,它会相对快速地增长。

我们是否期望像那样破解它?是否期望返回所有结果?

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


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

4

1 回答 1

4

我困惑的原因

graphql-relay-js 库的实用功能connectionFromArray并不能解决各种分页需求。我们需要根据我们喜欢的分页模型来调整我们的方法。

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

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

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

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

对于编号页面,您可以在 GraphQL 类型中使用额外字段totalPost,该字段可用于在 UI 上显示指向编号页面的链接。在后端,您可以使用类似功能skip仅获取所需的项目。该字段totalPost和当前页码消除了对hasNextPageor的依赖hasPreviousPage

For infinite scrolling, you can use the cursor field, which can be used as the value for after in your query. On the back-end, you can use the value of cursor to retrieve the next items (value of first). See an example of using cursor in Relay documention on GraphQL connection. See this answer about GraphQL connection and cursor. See this and this blog posts, which will help you better understand the idea of cursor.


What's the logic here?

Are we expected to hack it like that?

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

是否期望返回所有结果?

再次,取决于问题。


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

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

于 2016-06-03T05:04:13.337 回答