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

时间:2016-06-02 05:53:18

标签: relayjs graphql-js

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

我混淆的原因:

  • 如果我传递first参数并在db query中使用它来限制返回的结果,hasNextPage 总是 false。这是有效的,但如果您使用hasNextPage,它会中断hasPreviousPagelast
  • 如果我没有传递first参数并且不在db查询中使用它来限制返回的结果,hasNextPage正在按预期工作但它将返回全部我查询的项目(可能是数千)
    • 即使数据库在同一台机器上(大型应用程序不是这种情况),这看起来非常非常非常低效且非常糟糕。请证明我错了!
    • 据我所知,GraphQL没有任何服务器端缓存,因此没有任何意义可以返回所有结果(即使它确实如此,用户也不会浏览100%的内容)

这里的逻辑是什么?

我想到的一个解决方案是在+1中添加firstgetUserPosts值,它会检索一个多余的项目,而hasNextPage可能会有效。但这感觉就像一个 hack 并且总是返回多余的项目 - 如果有很多connections和请求,它会相对快速地增长。

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

或者我是否误解了数据库与GrahpQL / Relay之间的整个关系?

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

1 个答案:

答案 0 :(得分:4)

  

我混淆的原因

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

connectionFromArray函数从给定数组派生hasNextPagehasPrevisousPage的值。所以,你在“我的困惑的原因”中观察和提到的是预期的行为。

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

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

两种常见的分页模型是编号页面和无限滚动。 GraphQL连接规范并不是关于分页模型的观点,而是允许它们。

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

对于无限滚动,您可以使用cursor字段,该字段可用作查询中after的值。在后端,您可以使用cursor的值来检索下一个项目(first的值)。请参阅Relay documention on GraphQL connection中使用光标的示例。有关GraphQL连接和游标的信息,请参阅this answer。请参阅thisthis博文,这有助于您更好地理解cursor的想法。

  

这里的逻辑是什么?

     

我们是否应该像那样破解它?

不,理想情况下,我们不会想要破解和遗忘它。这将在项目中留下技术债务,这可能会在长期内造成更多问题。您可以考虑实现自己的函数来返回连接对象。您将在graphql-relay-js中的array-connection实现中了解如何执行此操作。

  

预计返回所有结果吗?

再次,取决于问题。

  

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

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