我有以下GraphQL架构,它定义了3种类型:一个hasmany CondaPackage
的CondaVersion
,hasmany CondaExecutable
的一个。我希望能够查询CondaVersion
并询问“您拥有多少个CondaExecutable
,它们成功完成了我的分析”。目前,我已经编写了succeededExeCount
和allExeCount
,它们通过加载所有子代并手动计算成功的子代数来解决此字段。
exports.createSchemaCustomization = ({ actions: { createTypes }, schema }) => {
createTypes([
schema.buildObjectType({
name: "CondaPackage",
fields: {
succeededExeCount: {
type: "Int!",
resolve(source, args, context){
// TODO
}
},
allExeCount: {
type: "Int!",
resolve(source, args, context){
// TODO
}
}
},
interfaces: ["Node"]
}),
schema.buildObjectType({
name: "CondaVersion",
fields: {
succeededExeCount: {
type: "Float!",
resolve(source, args, context){
const children = context.nodeModel.getNodesByIds({
ids: source.children,
type: "CondaExecutable"
})
return children.reduce((acc, curr) => acc + curr.fields.succeeded, 0)
}
},
allExeCount: {
type: "Int!",
resolve(source, args, context){
return source.children.length;
}
}
},
interfaces: ["Node"]
}),
schema.buildObjectType({
name: "CondaExecutable",
fields: {
succeeded: {
type: "Boolean!",
resolve(source, args, context, info) {
return source.fields.succeeded || false;
}
},
},
interfaces: ["Node"]
})
])
}
我的第一个问题是,这似乎效率极低。对于每个CondaVersion
,我正在为其子项运行单独的查询,这是经典的N + 1查询问题。有没有办法告诉Gatsby / GraphQL像我使用SQL来避免这种情况那样简单地“联接”两个表?我的第二个问题是,我现在需要从顶级类型
CondaPackage
算起后续子代的数量。我想问“您的 child CondaExecutable
拥有多少CondaVersion
,这使我的分析成功了”。同样,在SQL中这很容易,因为我只需要JOIN
这三种类型。但是,我目前唯一能做到的方法是对每个 child 使用getNodesByIds
,然后对每个 child 的 child 使用n*m*o
,这对succeededExeCount
运行时来说是可怕的。我想运行GraphQL查询作为字段解析的一部分,这使我可以从每个 child 那里获取runQuery
。但是,Gatsby的 ojit_code 似乎返回了不包含派生字段的节点,并且它不允许我选择其他字段来返回。如何在Gatsby中访问节点的 child 的 child 上的字段? 最佳答案
编辑
这是Gatsby维护人员关于此变通办法的回应:
参见full thread here。
原始答案
这是一个“ secret ”(在撰写本文时,文档中的任何地方都未提及):
当您使用runQuery
时,Gatsby会尝试解析派生字段……但前提是该字段传递给查询的选项(过滤器,排序,分组,不重复)。
例如,在CondaVersion
中,您可以执行以下操作,而不是访问子节点并查找fields.succeeded
:
const succeededNodes = await context.nodeModel.runQuery({
type: "CondaExecutable",
query: { filter: { succeeded: { eq: true } } }
})
CondaPackage
也是如此。您可以尝试执行此操作const versionNodes = await context.nodeModel.runQuery({
type: "CondaVersion",
query: {}
})
return versionNodes.reduce((acc, nodes) => acc + node.succeededExeCount, 0) // Error
您可能会发现succeededExeCount
是undefined
。诀窍是这样做:
const versionNodes = await context.nodeModel.runQuery({
type: "CondaVersion",
- query: {}
+ query: { filter: { succeededExeCount: { gte: 0 } } }
})
这是违反直觉的,因为您会认为Gatsby只会解析类型上的所有可解析字段。相反,它只解析“已使用”的字段。因此,为了解决这个问题,我们添加了一个过滤器,该过滤器据说什么也不做。但这还不是全部,
node.succeededExeCount
仍然是undefined
。解析的数据(
succeededExeCount
)不直接存储在节点本身上,而是存储在node.__gatsby_resolved
source中。我们将不得不在那里访问它。const versionNodes = await context.nodeModel.runQuery({
type: "CondaVersion",
query: { filter: { succeededExeCount: { gte: 0 } } }
})
return versionNodes.reduce((acc, node) => acc + node.__gatsby_resolved.succeededExeCount, 0)
试试看,让我知道是否可行。PS:我注意到您可能使用了
createNodeField
(在CondaExec
的node.fields.succeeded
吗?),您也可以在createTypes
中访问exports.sourceNodes
,因此您可以直接添加此succeeded
字段。