在 Gatsby 中查询 child 个节点字段
Querying child node fields in Gatsby
我有以下 GraphQL 模式,它定义了 3 种类型:CondaPackage
有很多 CondaVersion
,有很多 CondaExecutable
。我希望能够查询一个 CondaVersion
并询问“你拥有多少 CondaExecutable
,我的分析成功了”。目前我已经写了一个 succeededExeCount
和 allExeCount
,它们通过加载所有 children 并手动计算成功的 children 的数量来解析这个字段。
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
,我 运行 为其 children 单独查询,这是一个经典的 N+1 查询问题。有没有办法告诉 Gatsby/GraphQL 像我使用 SQL 那样简单地“加入”两个表来避免这种情况?
我的第二个问题是,我现在需要从顶级类型中计算成功 children 的数量:CondaPackage
。我想问一下“你的childCondaVersion
有多少CondaExecutable
我的分析成功了”。同样,在 SQL 中这很容易,因为我只需要 JOIN
3 种类型。但是,我目前唯一可以做到这一点的方法是对每个 child 使用 getNodesByIds
,然后对每个 child 的 child 使用 n*m*o
运行时间,太可怕了。我想 运行 一个 GraphQL 查询作为字段解析的一部分,这样我就可以从每个 child 中获取 succeededExeCount
。但是,Gatsby 的 runQuery
似乎 return 节点没有包含派生字段,并且它不会让我 select 附加字段到 return。如何在 Gatsby 中访问节点 child 的 child 上的字段?
编辑
这是 Gatsby 维护者关于解决方法的回复:
Gatsby has an internal mechanism to filter/sort by fields with custom resolvers. We call it materialization. [...] The problem is that this is not a public API. This is a sort of implementation detail that may change someday and that's why it is not documented.
原答案
这里有一点 '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 只会解析类型上的所有可解析字段。相反,它只解析 'used' 的字段。因此,为了解决这个问题,我们添加了一个什么都不做的过滤器。
但这还不是全部,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
也可以在 [=28 中访问=],因此您可以直接添加此 succeeded
字段。
我有以下 GraphQL 模式,它定义了 3 种类型:CondaPackage
有很多 CondaVersion
,有很多 CondaExecutable
。我希望能够查询一个 CondaVersion
并询问“你拥有多少 CondaExecutable
,我的分析成功了”。目前我已经写了一个 succeededExeCount
和 allExeCount
,它们通过加载所有 children 并手动计算成功的 children 的数量来解析这个字段。
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
,我 运行 为其 children 单独查询,这是一个经典的 N+1 查询问题。有没有办法告诉 Gatsby/GraphQL 像我使用 SQL 那样简单地“加入”两个表来避免这种情况?
我的第二个问题是,我现在需要从顶级类型中计算成功 children 的数量:CondaPackage
。我想问一下“你的childCondaVersion
有多少CondaExecutable
我的分析成功了”。同样,在 SQL 中这很容易,因为我只需要 JOIN
3 种类型。但是,我目前唯一可以做到这一点的方法是对每个 child 使用 getNodesByIds
,然后对每个 child 的 child 使用 n*m*o
运行时间,太可怕了。我想 运行 一个 GraphQL 查询作为字段解析的一部分,这样我就可以从每个 child 中获取 succeededExeCount
。但是,Gatsby 的 runQuery
似乎 return 节点没有包含派生字段,并且它不会让我 select 附加字段到 return。如何在 Gatsby 中访问节点 child 的 child 上的字段?
编辑
这是 Gatsby 维护者关于解决方法的回复:
Gatsby has an internal mechanism to filter/sort by fields with custom resolvers. We call it materialization. [...] The problem is that this is not a public API. This is a sort of implementation detail that may change someday and that's why it is not documented.
原答案
这里有一点 '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 只会解析类型上的所有可解析字段。相反,它只解析 'used' 的字段。因此,为了解决这个问题,我们添加了一个什么都不做的过滤器。
但这还不是全部,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
也可以在 [=28 中访问=],因此您可以直接添加此 succeeded
字段。