我有以下GraphQL架构,它定义了3种类型:一个hasmany CondaPackageCondaVersion,hasmany CondaExecutable的一个。我希望能够查询CondaVersion并询问“您拥有多少个CondaExecutable,它们成功完成了我的分析”。目前,我已经编写了succeededExeCountallExeCount,它们通过加载所有子代并手动计算成功的子代数来解决此字段。

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
您可能会发现succeededExeCountundefined
诀窍是这样做:
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(在CondaExecnode.fields.succeeded吗?),您也可以在createTypes中访问exports.sourceNodes,因此您可以直接添加此succeeded字段。

10-05 20:30
查看更多