使用连接和分页时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
}
}
我的困惑原因:
- 如果我传递
first
参数并在数据库查询中使用它来限制 returned 结果,hasNextPage
总是 false
。这很有效,但它会破坏 hasNextPage
(hasPreviousPage
如果你使用 last
)
- 如果我不传递
first
参数并且不在数据库查询中使用它来限制 returned 结果,hasNextPage
会按预期工作,但它会 return 所有 我查询的项目(可能是几千个)
- 即使数据库在同一台机器上(较大的应用程序并非如此),这似乎非常非常低效且糟糕。请证明我错了!
- 据我所知,GraphQL 没有任何服务器端缓存,因此 return 所有结果都没有任何意义(即使有,用户也不会浏览 100 %含量)
这是什么逻辑?
我想到的一个解决方案是将 +1
添加到 getUserPosts
中的 first
值,它将检索一个多余的项目并且 hasNextPage
可能会起作用。但这感觉像是 hack 并且总是有多余的项目 returned - 如果有很多 connections
和请求,它会增长得相对较快。
我们期望这样破解它吗? return 全部 的结果是预期的吗?
还是我误解了数据库和 GrahpQL / Relay 之间的整个关系?
如果我使用 FB DataLoader 和 Redis 会怎么样?这会改变那个逻辑吗?
Cause of my confusion
graphql-relay-js 库的效用函数 connectionFromArray
并不能解决各种分页需求。我们需要根据我们首选的分页模型调整我们的方法。
connectionFromArray
函数从给定的数组中导出 hasNextPage
和 hasPrevisousPage
的值。因此,您在“我的困惑的原因”中观察到和提到的是预期的行为。
至于你对是否加载所有数据感到困惑,这取决于你手头的问题。在以下几种情况下加载所有项目可能有意义,例如:
- 项目数量少,您可以负担得起存储这些项目所需的内存。
- 经常请求这些项目,您需要缓存它们以便更快地访问。
两种常见的分页模型是编号页面和无限滚动。 GraphQL 连接规范对分页模型没有固执己见,并且允许这两种模型。
对于编号页面,您可以在您的 GraphQL 类型中使用一个额外的字段 totalPost
,它可用于在您的 UI 上显示编号页面的链接。在后端,您可以使用 skip
之类的功能来仅获取所需的项目。字段 totalPost
和当前页码消除了对 hasNextPage
或 hasPreviousPage
.
的依赖
对于无限滚动,您可以使用 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 原则)。如果项目是重量级的,创建数组可能是一项昂贵的操作。
使用 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
参数并在数据库查询中使用它来限制 returned 结果,hasNextPage
总是false
。这很有效,但它会破坏hasNextPage
(hasPreviousPage
如果你使用last
) - 如果我不传递
first
参数并且不在数据库查询中使用它来限制 returned 结果,hasNextPage
会按预期工作,但它会 return 所有 我查询的项目(可能是几千个)- 即使数据库在同一台机器上(较大的应用程序并非如此),这似乎非常非常低效且糟糕。请证明我错了!
- 据我所知,GraphQL 没有任何服务器端缓存,因此 return 所有结果都没有任何意义(即使有,用户也不会浏览 100 %含量)
这是什么逻辑?
我想到的一个解决方案是将 +1
添加到 getUserPosts
中的 first
值,它将检索一个多余的项目并且 hasNextPage
可能会起作用。但这感觉像是 hack 并且总是有多余的项目 returned - 如果有很多 connections
和请求,它会增长得相对较快。
我们期望这样破解它吗? return 全部 的结果是预期的吗?
还是我误解了数据库和 GrahpQL / Relay 之间的整个关系?
如果我使用 FB DataLoader 和 Redis 会怎么样?这会改变那个逻辑吗?
Cause of my confusion
graphql-relay-js 库的效用函数 connectionFromArray
并不能解决各种分页需求。我们需要根据我们首选的分页模型调整我们的方法。
connectionFromArray
函数从给定的数组中导出 hasNextPage
和 hasPrevisousPage
的值。因此,您在“我的困惑的原因”中观察到和提到的是预期的行为。
至于你对是否加载所有数据感到困惑,这取决于你手头的问题。在以下几种情况下加载所有项目可能有意义,例如:
- 项目数量少,您可以负担得起存储这些项目所需的内存。
- 经常请求这些项目,您需要缓存它们以便更快地访问。
两种常见的分页模型是编号页面和无限滚动。 GraphQL 连接规范对分页模型没有固执己见,并且允许这两种模型。
对于编号页面,您可以在您的 GraphQL 类型中使用一个额外的字段 totalPost
,它可用于在您的 UI 上显示编号页面的链接。在后端,您可以使用 skip
之类的功能来仅获取所需的项目。字段 totalPost
和当前页码消除了对 hasNextPage
或 hasPreviousPage
.
对于无限滚动,您可以使用 cursor
字段,它可以用作查询中 after
的值。在后端,您可以使用 cursor
的值来检索下一个项目(first
的值)。请参阅 Relay documention on GraphQL connection. See 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 原则)。如果项目是重量级的,创建数组可能是一项昂贵的操作。