有什么方法可以将 GraphQL 查询的多个 Fragment 扩展拆分为多个调用?
Any way to split up multiple Fragment expansions for a GraphQL query into multiple calls?
上下文
这个问题很可能取决于某些选择,其中一些是可变的,而另一些则不是。我们正在使用以下技术和框架:
- 中继/React/TypeScript
- ContentStack(内容管理系统)
问题
我正在尝试创建一个高度可定制的页面,该页面可以根据提供给它的数据从多种 UI 组件构建(以允许使用 CMS 使用预制件构建页面 UI 以不可预测的顺序)。
我的第一次尝试是为数组中可能引用的潜在 UI 组件创建一组片段:
query CustomPageQuery {
title
description
customContentConnection {
edges {
node {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
"""
Further fragments are added here as we add more kinds of UI
"""
}
}
}
}
在我们使用的 CMS (ContentStack) 中,这个查询的复杂性已经增长到被拒绝的程度,因为它需要在单个查询中调用太多数据库。出于这个原因,我希望有一种方法可以拆分对片段的调用,使它们不属于初始查询的一部分,或者一些类似的解决方案会导致将此查询拆分为多个部分。
我希望 @defer
指令能为我解决这个问题,但 relay-compiler
不支持它。
有什么想法吗?
遗憾的是 @defer
仍然不是标准,因此大多数实现都不支持它(因为您还需要服务器支持它)。
我不确定我是否正确理解了问题,但您可能希望更多地关注使用 @skip
或 @include
来根据事物的类型只获取您需要的片段.但这需要前端事先知道它想要查询什么。
query CustomPageQuery($hero: Boolean, $tweet: Boolean, $video: Boolean) {
title
description
customContentConnection {
edges {
node {
... HeroFragment @include(if: $hero)
... TweetBlockFragment @include(if: $tweet)
... EmbeddedVideoFragment @include(if: $video)
}
}
}
}
通常您希望能够区分类型而无需进行数据库查询。所以说:
type Hero {
id: ID
name: String
}
type Tweet {
id: ID
content: String
}
union Content = Hero | Tweet
{
Content: {
__resolveType: (parent, ctx) => {
// That should be able to resolve the type without a DB query
},
}
}
一旦通过,每个片段就会被解析,进行更多的数据库查询。如果这些数据加载器没有正确地进行批处理,那么你就会遇到 N+1 问题。我不确定您对后端有多少控制权(如果有的话),但没有解决您的问题的灵丹妙药。
如果您无法在后端进行优化,那么我建议您尝试限制连接。他们似乎正在使用基于游标的分页,所以你从 first: 10
开始,一旦返回第一批,你可以通过将 after
设置为前一批的最后一个游标来查询下一个元素:
query CustomPageQuery($after: String) {
customContentConnection(first: 10, after: $after) {
edges {
cursor
node {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
}
}
pageInfo {
hasNextPage
}
}
}
作为最后的手段,您可以尝试先获取所有 ID,然后针对每个 ID(我猜是使用别名)或类型(如果您可以在连接字段上进行过滤)对 CMS 进行后续查询。但是我觉得写它很脏,所以如果可以的话就避免它。
{
one: node(id: "UUID1") {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
}
two: node(id: "UUID2") {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
}
}
上下文
这个问题很可能取决于某些选择,其中一些是可变的,而另一些则不是。我们正在使用以下技术和框架:
- 中继/React/TypeScript
- ContentStack(内容管理系统)
问题
我正在尝试创建一个高度可定制的页面,该页面可以根据提供给它的数据从多种 UI 组件构建(以允许使用 CMS 使用预制件构建页面 UI 以不可预测的顺序)。
我的第一次尝试是为数组中可能引用的潜在 UI 组件创建一组片段:
query CustomPageQuery {
title
description
customContentConnection {
edges {
node {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
"""
Further fragments are added here as we add more kinds of UI
"""
}
}
}
}
在我们使用的 CMS (ContentStack) 中,这个查询的复杂性已经增长到被拒绝的程度,因为它需要在单个查询中调用太多数据库。出于这个原因,我希望有一种方法可以拆分对片段的调用,使它们不属于初始查询的一部分,或者一些类似的解决方案会导致将此查询拆分为多个部分。
我希望 @defer
指令能为我解决这个问题,但 relay-compiler
不支持它。
有什么想法吗?
遗憾的是 @defer
仍然不是标准,因此大多数实现都不支持它(因为您还需要服务器支持它)。
我不确定我是否正确理解了问题,但您可能希望更多地关注使用 @skip
或 @include
来根据事物的类型只获取您需要的片段.但这需要前端事先知道它想要查询什么。
query CustomPageQuery($hero: Boolean, $tweet: Boolean, $video: Boolean) {
title
description
customContentConnection {
edges {
node {
... HeroFragment @include(if: $hero)
... TweetBlockFragment @include(if: $tweet)
... EmbeddedVideoFragment @include(if: $video)
}
}
}
}
通常您希望能够区分类型而无需进行数据库查询。所以说:
type Hero {
id: ID
name: String
}
type Tweet {
id: ID
content: String
}
union Content = Hero | Tweet
{
Content: {
__resolveType: (parent, ctx) => {
// That should be able to resolve the type without a DB query
},
}
}
一旦通过,每个片段就会被解析,进行更多的数据库查询。如果这些数据加载器没有正确地进行批处理,那么你就会遇到 N+1 问题。我不确定您对后端有多少控制权(如果有的话),但没有解决您的问题的灵丹妙药。
如果您无法在后端进行优化,那么我建议您尝试限制连接。他们似乎正在使用基于游标的分页,所以你从 first: 10
开始,一旦返回第一批,你可以通过将 after
设置为前一批的最后一个游标来查询下一个元素:
query CustomPageQuery($after: String) {
customContentConnection(first: 10, after: $after) {
edges {
cursor
node {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
}
}
pageInfo {
hasNextPage
}
}
}
作为最后的手段,您可以尝试先获取所有 ID,然后针对每个 ID(我猜是使用别名)或类型(如果您可以在连接字段上进行过滤)对 CMS 进行后续查询。但是我觉得写它很脏,所以如果可以的话就避免它。
{
one: node(id: "UUID1") {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
}
two: node(id: "UUID2") {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
}
}