简体   繁体   English

在 Gatsby 中查询子节点字段

[英]Querying child node fields in Gatsby

I have the following GraphQL schema, which defines 3 types: a CondaPackage which hasmany CondaVersion , which hasmany CondaExecutable .我有以下 GraphQL 架构,它定义了 3 种类型:一个有CondaPackageCondaVersion ,它有很多CondaExecutable I want to be able to query a CondaVersion and ask "how many CondaExecutable s do you own which succeeded my analysis".我希望能够查询一个CondaVersion并询问“您拥有多少CondaExecutable s,它成功了我的分析”。 Currently I've written a succeededExeCount and allExeCount which resolve this field by loading all children and manually counting the number of children that succeeded.目前我已经编写了一个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"]
    })
  ])
}

My first problem is that this seems incredibly inefficient.我的第一个问题是这似乎非常低效。 For each CondaVersion I'm running a separate query for its children, which is a classic N+1 query problem.对于每个CondaVersion ,我为其子项运行单独的查询,这是一个经典的 N+1 查询问题。 Is there a way to tell Gatsby/GraphQL to simply "join" the two tables like I would using SQL to avoid this?有没有办法告诉 Gatsby/GraphQL 像我使用 SQL 一样简单地“加入”这两个表来避免这种情况?

My second problem is that I now need to count the number of succeeding children from the top level type: CondaPackage .我的第二个问题是我现在需要从顶级类型计算继承孩子的数量: CondaPackage I want to ask "how many CondaExecutable s do your child CondaVersion s own which succeeded my analysis".我想问“您的孩子CondaVersion拥有多少CondaExecutable ,这成功了我的分析”。 Again, in SQL this would be easy because I would just JOIN the 3 types.同样,在 SQL 中,这很容易,因为我只需JOIN 3 种类型。 However, the only way I can currently do this is by using getNodesByIds for each child, and then for each child's child, which is n*m*o runtime, which is terrifying.但是,我目前可以做到这一点的唯一方法是对每个孩子使用getNodesByIds ,然后对每个孩子的孩子使用n*m*o运行时,这太可怕了。 I would like to run a GraphQL query as part of the field resolution which lets me grab the succeededExeCount from each child.我想运行一个 GraphQL 查询作为字段解析的一部分,这让我可以从每个孩子那里获取succeededExeCount However, Gatsby's runQuery seems to return nodes without including derived fields, and it won't let me select additional fields to return.但是,盖茨比的runQuery似乎返回的节点不包括派生字段,它不会让我 select 额外的字段返回。 How can I access fields on a node's child's child in Gatsby?如何在 Gatsby 中访问节点子节点的字段?

Edit编辑

Here's the response from a Gatsby maintainer regarding the workaround:以下是 Gatsby 维护者对解决方法的回复:

Gatsby has an internal mechanism to filter/sort by fields with custom resolvers. Gatsby 有一个内部机制,可以使用自定义解析器按字段进行过滤/排序。 We call it materialization.我们称之为物化。 [...] The problem is that this is not a public API . [...] 问题是这不是公共的 API This is a sort of implementation detail that may change someday and that's why it is not documented.这是一种实现细节,有朝一日可能会改变,这就是它没有被记录的原因。

See the full thread here .这里查看完整的线程

Original Answer原始答案

Here's a little 'secret' (not mentioned anywhere in the docs at the time of writing):这是一个小“秘密”(在撰写本文时,文档中的任何地方都没有提到):

When you use runQuery , Gatsby will try to resolve derived fields... but only if that field is passed to the query's options (filter, sort, group, distinct).当您使用runQuery时,Gatsby 将尝试解析派生字段......但前提是该字段被传递给查询的选项(过滤器、排序、组、不同)。

For example, in CondaVersion , instead of accessing children nodes and look up fields.succeeded , you can do this:例如,在CondaVersion中,您可以执行以下操作,而不是访问子节点并查找fields.succeeded

const succeededNodes = await context.nodeModel.runQuery({
  type: "CondaExecutable",
  query: { filter: { succeeded: { eq: true } } }
})

Same thing for CondaPackage . CondaPackage You might try to do this你可以尝试这样做

const versionNodes = await context.nodeModel.runQuery({
  type: "CondaVersion",
  query: {}
})

return versionNodes.reduce((acc, nodes) => acc + node.succeededExeCount, 0) // Error

You'll probably find that succeededExeCount is undefined .您可能会发现succeededExeCountundefined

The trick is to do this:诀窍是这样做:

const versionNodes = await context.nodeModel.runQuery({
  type: "CondaVersion",
- query: {}
+ query: { filter: { succeededExeCount: { gte: 0 } } }
})

It's counter intuitive, because you'd think Gatsby would just resolve all resolvable fields on a type.这是反直觉的,因为您会认为 Gatsby 只会解析类型上的所有可解析字段。 Instead it only resolves fields that is 'used'.相反,它只解析“使用”的字段。 So to get around this, we add a filter that supposedly does nothing.所以为了解决这个问题,我们添加了一个应该什么都不做的过滤器。

But that's not all yet, node.succeededExeCount is still undefined .但这还不是全部, node.succeededExeCount仍然是undefined

The resolved data ( succeededExeCount ) is not directly stored on the node itself, but in node.__gatsby_resolved source .解析后的数据( succeededExeCount )不直接存储在节点本身,而是在node.__gatsby_resolved source中。 We'll have to access it there instead.我们将不得不在那里访问它。

const versionNodes = await context.nodeModel.runQuery({
  type: "CondaVersion",
  query: { filter: { succeededExeCount: { gte: 0 } } }
})

return versionNodes.reduce((acc, node) => acc + node.__gatsby_resolved.succeededExeCount, 0)

Give it a try & let me know if that works.试一试,让我知道它是否有效。

PS: I notice that you probably use createNodeField (in CondaExec 's node.fields.succeeded ?) createTypes is also accessible in exports.sourceNodes , so you might be able to add this succeeded field directly. PS:我注意到您可能使用createNodeField (在CondaExecnode.fields.succeeded ?) createTypes也可以在exports.sourceNodes中访问,因此您可以直接添加这个succeeded的字段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM