Gatsby: [bug] ☂️ 架构自定义问题的保护伞问题

创建于 2019-03-04  ·  131评论  ·  资料来源: gatsbyjs/gatsby

这是架构重构引入的所有 2.2.0 问题的元问题。

什么?

有关我们进行重构的原因及其全部内容的详细信息,请参阅博客文章

有关发行说明和最终更新,请参阅发行博客文章

如何?

安装最新版本的 Gatsby 并尝试运行您的站点。 希望这一切都会奏效。 如果需要,您还可以尝试两个新的 API( createTypescreateResolvers )。

yarn add gatsby

变更日志

[email protected]

  • 已发表

[email protected]

  • 移动到显式指令而不是 addResolver

[email protected]

  • 掌握回合并,升级graphql-compose

[email protected]

  • 见#13028

[email protected]

  • :香槟酒:

[email protected]

  • 修复了当空字符串与日期混合导致内容不被解释为日期时的回归

[email protected]

  • 合并最新的主
  • 文档更新

[email protected]

  • 合并最新的主
  • 更好的 SDL 解析错误消息

[email protected]

  • 修复连接命名中的回归,以防类型为小写命名

[email protected]

  • 修复了没有任何过滤器的自定义标量
  • 尝试覆盖 Node 接口或生成的过滤器/排序类型时添加了错误。

[email protected]

  • 添加了一个以 graphql-compose 为模型的新的便捷 API。 请参阅使用类型定义示例。
exports.sourceNodes = ({ actions, schema }) => {
  const { createTypes } = actions
  createTypes([
    schema.buildObjectType({
      name: `CommentJson`,
      fields: {
        text: `String!`,
        blog: {
          type: `BlogJson`,
          resolve(parent, args, context) {
            return context.nodeModel.getNodeById({
              id: parent.author,
              type: `BlogJson`,
            })
          },
        },
        author: {
          type: `AuthorJson`,
          resolve(parent, args, context) {
            return context.nodeModel.getNodeById({
              id: parent.author,
              type: `AuthorJson`,
            })
          },
        },
      },
      interfaces: [`Node`],
    }),
  ])
}

[email protected]

  • 修复了 Node id 字段不是 String 的回归,就像在当前 master 中一样。
  • 升级到[email protected]
  • FilterInput类型现在不以输出类型为前缀,减少类型的扩散

[email protected]

  • 修复了对空查询结果进行分页的问题
  • @dontInfer(noDefaultResolvers: false)实际有效
  • createResolvers解析器info.originalResolver中,即使原始字段上没有解析器也可用

[email protected]

  • 使用适当的发布版本重做 alpha 版本
  • 修复没有添加实际节点的节点类型

[email protected]

  • 初始阿尔法
GraphQL

最有用的评论

已发布[email protected] 。 谢谢大家!

所有131条评论

ID 字段的过滤器现在需要一个 ID 作为他们以前想要一个字符串的输入。 这会中断某些查询,例如:

export const query = graphql`
  query BlogPostTemplateQuery($id: String!) {
    post: sanityPost(id: { eq: $id }) {
      id
      title
    }
  }
`

会报告:

error GraphQL Error Variable "$id" of type "String!" used in position expecting type "ID".

虽然更新查询以反映这一点可能更正确,但这是一个重大更改,所以我想我会报告它。

@rexxars不错的发现! 我假设我们曾经将 ID 过滤器转换为字符串。 我会恢复旧的行为。

发布了新版本。

尝试在解析器中查询架构:

exports.createResolvers = ({ createResolvers, schema }) => {
  createResolvers({
    MenuJson: {
      someResolver: {
        type: `String!`,
        async resolve(source, args, context, info) {
          const foo = await graphql(schema, `
            {
              allPageJson {
                nodes {
                  id
                }
              }
            }
          `, {})

          console.log(foo)

          return 'WIP'
        },
      },
    },
  })
}
TypeError: Cannot read property 'nodeModel' of undefined
         at /private/tmp/test-gatsby/node_modules/gatsby/dist/schema/resolvers.js:22:15
         at /private/tmp/test-gatsby/node_modules/gatsby/dist/schema/resolvers.js:49:44
         at Generator.next (<anonymous>)
[...]

@NicoleEtLui对于字段解析器中的查询,请使用context.nodeModel上提供的方法,即您可以使用context.nodeModel.getAllNodes({ type: 'PageJson' })或更复杂的查询,您可以使用context.nodeModel.runQuery 。 这里有一些基本的例子

如果您需要访问架构,请注意schema参数只是一个中间表示 - 在解析器中,您可以访问info.schema上的最终构建架构。

@stefanprobst感谢您的快速回答以及您所做的所有出色工作!

你好!
任何处理节点之间关系的建议?

例如,文件存储在 JSON 中(没有 id 字段,假设文件名为 id):
data/comments/some-uuid.json = { "message": "Hello", "postId": "some-post" }
data/posts/some-post.json = { "content": "post" }

使用source-filesystemtransformer-json插件,这使得节点具有不可预测的ID因为转换器使用createNodeId() 。 很难从comment找到post comment

你好 !
尝试使用gatsby-config.js任何一个启动 gatsby 项目:

  • gatsby-transformer-sharp
  • gatsby-plugin-sharp
  • gatsby-plugin-manifest

会抛出:

error Plugin gatsby-transformer-sharp returned an error


  Error: Cannot find module 'gatsby/dist/utils/cpu-core-count'

  - loader.js:581 Function.Module._resolveFilename
    internal/modules/cjs/loader.js:581:15

  - loader.js:507 Function.Module._load
    internal/modules/cjs/loader.js:507:25

  - loader.js:637 Module.require
    internal/modules/cjs/loader.js:637:17

  - v8-compile-cache.js:159 require
    [keemotion-corporate]/[v8-compile-cache]/v8-compile-cache.js:159:20
[...]

@NicoleEtLui请更新gatsby-plugin-manifestgatsby-plugin-sharp ,这是在这些包中修复的https://github.com/gatsbyjs/gatsby/pull/12332

@LoicMahieu您可以手动为节点提供 id,然后您可以通过指定fieldName___NODE字段来建立关系。

问:这个用例适合createResolvers吗?

我正在通过gatsby-source-graphql使用远程 CMS,其中包括对多个远程文件的引用。 我目前正在使用createRemoteFileNode提取这些文件。 但是,页面查询很快就会变得尴尬,因为很容易进入我需要 cms 查询的结果(在gatsby-source-graphql数据源上)以从gatsby-source-filesystem找出我需要哪些文件的情况

理想情况下,我想将这些远程文件节点添加/链接/加入(?)从gatsby-source-graphql到 cms 节点。 这是createResolvers可以帮助解决的情况吗?

@skinandbones如果我理解正确,简短的回答是“可能但可能还没有”。

我们支持从第三方架构中添加类型的扩展字段CONFIGS,因此有可能到现场解析器从CMS架构与添加到类型createResolvers ,并使用createRemoteFileNode在解析器。 例如:
https://github.com/stefanprobst/gatsby/blob/5bbfee29b5ec38f13a3070b13de4877aaddd6483/examples/using-gatsby-source-graphql/gatsby-node.js#L56 -L71

问题是createRemoteFileNode会触发onCreateNode API 调用,而在字段解析器中,我们目前无法知道这些后续 API 调用何时完成并且解析器返回是安全的。 (解决此问题的一种方法可能是#12202。)因此,根据您打算对远程文件执行的具体操作,这可能会也可能不会起作用。

@LoicMahieu你有一个可以链接到的示例项目吗?

@stefanprobst是的,你理解正确,异步问题是有道理的。 您链接的示例与我期望的非常相似,因此我可以试一试,看看会发生什么。 我的计划是在页面查询中通过gatsby-transformer-sharp运行这些文件节点。

通过sourceNodes API(像往常一样)使用createRemoteFileNode然后使用新 API 将这些节点链接到第 3 方模式是另一种可行的方法吗? 到目前为止,我还没有能够进入 3rd 方模式来做到这一点。

@skinandbones对不起,我应该澄清:在我挂的例子中,部分目前还没有工作正是因为这个问题,当现场解析器返回时, File节点将被创建,但ImageSharp节点(在触发的onCreateNode API 调用中创建)还没有。

至于第二种方法,我会对你的发现感兴趣——应该可以使用context.nodeModel.getAllNodes({ type: 'File' })或类似context.nodeModel.runQuery({ type: 'File', query: { filter: { name: { regex: "/^remote/" } } } })东西在解析器中查询添加的远程File节点context.nodeModel.runQuery({ type: 'File', query: { filter: { name: { regex: "/^remote/" } } } })

@stefanprobst
下面是一个例子:

  1. https://github.com/LoicMahieu/test-gatsby-refactor-schema
    在这里,我们可以通过对父级File复杂查找comments链接到post File
  1. https://github.com/LoicMahieu/test-gatsby-refactor-schema/tree/custom-transformer-json
    在这里,我们可以通过使用自定义 JSON 转换器来链接它们,我们可以在其中转换对象并更改 id。
    此方法有效,但是:如果帖子被删除并且评论中仍然存在引用,则 gatsby 将失败。
    可以通过不使用___NODE方式而是使用新的createResolvers来修复它:演示

嘿,我刚刚读了这篇关于模式定制的

@baobabKoodaa

Gatsby/GraphQL wrt 中是否存在某种限制? 空值?

不可以。在 GraphQL 中,您可以显式设置字段是否可以为空。 更多信息在这里

@stefanprobst我得到了这个工作它适用于ImageSharp 。 非常酷,并且是使用 3rd 方模式的游戏规则改变者 🎉 🎉

至于第二种方法,我会对你的发现感兴趣——应该可以使用context.nodeModel.getAllNodes({ type: 'File' })或类似context.nodeModel.runQuery({ type: 'File', query: { filter: { name: { regex: "/^remote/" } } } })东西在解析器中查询添加的远程File节点context.nodeModel.runQuery({ type: 'File', query: { filter: { name: { regex: "/^remote/" } } } })

这就是我所做的......

exports.sourceNodes = async ({ actions, store, cache, createNodeId }) => {
  ... do createRemoteFileNode stuff ...
}

exports.createResolvers = ({ createResolvers, schema }) => {
  createResolvers({
    CMS_Thing: {
      thumbFile: {
        type: 'File!',
        async resolve(source, args, context, info) {
          const data = await context.nodeModel.runQuery({
            type: 'File',
            query: { filter: { fields: { ThingThumb: { eq: 'true' }, thingId: { eq: source.id } } } }
          })
          return data[0];
        }
      }
    }
  });
}

(这显然取决于我创建具有某些字段的文件节点。)

最佳路径(对于我的用例)将能够在createResolvers使用createRemoteFileNode createResolvers所以希望我们能解决这个问题。

@skinandbones非常酷! 顺便说一句,您可以在runQuery使用firstOnly: true runQuery来仅获得第一个结果。

发布[email protected]

暂定目标是在下周将其合并到 master 并发布。 请发表评论并尝试:)

似乎没有生成createResolvers()中解析的类型字段的过滤器。

例子:

  • post.json : { "id": "some-post", "author": "Loic", message: "Hello" }
  • comment.json : { "postId": "some-post", message: "World" }
createResolvers({
  CommentsJson: {
    post: {
      type: `PostsJson`,
      resolve (source, args, context, info) {
        const allNodes = context.nodeModel.getNodeById({ id: source.postId, type: 'PostsJson' })
      }
    }
  }
})

CommentsJsonFilterInput类型不包含post字段。 所以这样的查询无法工作:

{
  allCommentsJson(filter: {post: {author: {eq: "foo"}}}) {
    nodes {
      id
    }
  }
}

谢谢

@LoicMahieu这是预期的行为(至少现在是这样):在createResolvers添加的字段将不会出现在输入过滤器中,因为createResolvers在模式生成中最后运行。 这里推荐的方法是使用createTypes操作定义字段类型,然后在createResolvers扩展它 - 或者使用最近添加的buildObjectType助手(见上文)。

抱歉,这还没有得到更好的记录 - 现在您可以直接在分支中查看 API 文档: createResolverscreateTypes

@LoicMahieu这是有意为之,在所有处理之后添加解析器,旨在作为对架构进行最终调整的工具。 如果您希望它们显示在过滤器中,您应该在createTypes添加字段。 您可以使用一种新的速记语法,请参阅顶部帖子。

编辑:哎呀,没有看到@stefanprobst已经回复了:D

感谢两位的澄清。 createResolverscreateTypes似乎达到了相同的目标。


真正有用的一件事是保存生成的模式。 它将允许对类型进行“快照”并确保架构将保持如我们预期的那样。
我写了一个似乎运行良好的 POC:

const { printType } = require("graphql")
const fs = require("fs-extra")
const path = require("path")

const schemaFilePath = path.join(__dirname, "./src/schema.gql")

exports.sourceNodes = async ({ actions }) => {
  const { createTypes } = actions

  if (await fs.exists(schemaFilePath)) {
    const typeDefs = (await fs.readFile(schemaFilePath)).toString()
    createTypes(typeDefs)
  }
}

exports.onPostBootstrap = async ({ store }) => {
  const { schema } = store.getState()
  const types = ["CommentsJson", "PostsJson"]
  const typeDefs = types
    .map(type => printType(schema.getType(type)))
    .join("\n\n")
  await fs.writeFile(schemaFilePath, typeDefs + "\n")
}

有了这个,我们甚至可以删除所有数据,架构仍然如我们所愿。
演示: https :

@LoicMahieu :+1: 这样的事情是我们路线图的一部分。

这不涉及架构构建的线程,对吗?

参考评论: https :

只有一个 Node 进程在工作(这个屏幕截图是 _not_ 来自 beta 版本,只是在这里添加它来说明我的观点):

Screenshot 2019-01-10 10 14 07

@hilja新的模式定制 API 不是关于运行查询,而是关于模式生成,所以这还没有改变。 正在进行实验性工作以允许字段解析器将工作卸载到其他进程,但尚未准备就绪。

发布2.2.0-rc.1 。 很快。

对新创建的字段进行查询排序似乎有问题。

我可以通过执行以下操作来创建一个新字段:

exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
    type MyNode implements Node {
      copyOfId: ID!
    }
  `
  createTypes(typeDefs)
}

exports.createResolvers = ({ createResolvers }) => {
  createResolvers({
    MyNode: {
      copyOfId: {
        resolve(source, args, context, info) {
          return info.originalResolver(
            {
              ...source,
              copyOfId: source.id,
            },
            args,
            context,
            info,
          )
        },
      },
    },
  })
}

但是如果我查询:

query {
    allMyNode (sort: {fields: [copyOfId]}) {
      nodes {
        copyOfId
    }
  }
}

结果未排序并按原始顺序显示。

如果我按id排序,当然一切都很好。

我想这与@LoicMahieu之前提到的问题相同,

PS:忘记提了……这个新的 API 太棒了!! :D 不要让我的评论误导您认为我处于悲观情绪中。 我只是想变得有用。 ;) 感谢您做的伟大工作!

@MarcCoet感谢您的测试! 这是一个已知问题 - 我们目前不为排序字段调用字段解析器,请参阅 #11368。 一旦合并,这是我们将要解决的下一件事!

哦对不起。 我没有考虑寻找setFieldsOnGraphQLNodeType问题。 我的错。
我很高兴知道它正在处理中。

所以真的没有办法用新的 API 来解决这个问题吗? 我们必须坚持createNodeField的旧方式吗?

我尝试使用buildObjectType但结果相同。

exports.sourceNodes = ({ actions, schema }) => {
  const { createTypes } = actions

  createTypes([
    schema.buildObjectType({
      name: `MyNode`,
      fields: {
        copyOfId: {
          type: `ID!`,
          resolve(parent) {
            return parent.id
          },
        },
      },
      interfaces: [`Node`],
    }),
  ])
}

任何其他未记录的方法来偶然实现对新节点字段的排序?

除此之外,我只是在@infer()指令中遇到了语法错误。 但我想这就是上述已知问题中“允许对非 SDL 类型的推理选项”的含义?!

@MarcCoet

@infer()指令的语法错误

这对你有用吗?

createTypes('type MyNode implements Node <strong i="11">@infer</strong> { foo: Boolean }')

至于排序问题:这个问题与模式定制重构有点正交。 问题是:当我们想要对不在节点对象本身但由字段解析器添加的字段进行过滤或排序时,我们需要先调用这些解析器,以便我们拥有可用于过滤和排序的完整节点。 例如,在您的示例中,节点对象没有copyOfId字段——为了使其可用,我们需要在过滤和排序之前调用解析器。 目前,我们只对过滤字段执行此操作,而不对排序字段执行此操作。 作为一种解决方法,您可以尝试为copyOfId添加一个虚拟过滤器到您的查询中:

query {
  allMyNode (sort: { fields: [copyOfId] }, filter: { copyOfId: { ne: null } }) {
    nodes {
      copyOfId
    }
  }
}

另请参阅此评论

我正在尝试为 gatsby-transformer-remark 生成的 frontmatter 节点设置架构,但总是遇到此错误: Error: Schema must contain unique named types but contains multiple types named "MarkdownRemarkFrontmatter".

我的架构如下所示:

type MarkdownRemarkFrontmatter implements Node {
  title: String
}

@kennedyrose感谢您的测试! 以下对您有用吗?

exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions
  createTypes(`
    type MarkdownRemarkFrontmatter {
      title: String
    }
    type MarkdownRemark implements Node {
      frontmatter: MarkdownRemarkFrontmatter
    }
  `)
}

只有顶级类型,即由源和转换器插件生成的节点类型(如MarkdownRemarkImageSharp )应该实现 Node 接口,而不是像 frontmatter 类型这样的嵌套类型。
(虽然我们在直接定位嵌套推断类型时似乎确实有一个错误)

那个有效。 谢谢!

已发布[email protected]

已发布[email protected] 。 谢谢大家!

@stefanprobst

这对你有用吗?
createTypes('type MyNode implements Node <strong i="9">@infer</strong> { foo: Boolean }')

确实如此! 只是原始文章具有误导性。

我想随着适当文件的到来,迷雾会升起。 ;)

关于排序问题,非常感谢您的澄清。 这真的很有帮助。
解决方法似乎有效,所以现在是完美的。

我对 Gatsby 的新可能性感到非常兴奋。 非常感谢你们为 Gatsby 所做的工作。

想x-post gatsbyjs/gatsby#12696,特别是这个帖子

看起来文件推断/检测可能在这里略有调整? 更新到2.2.2失败,而~2.1.0似乎正确推断文件节点。

刚刚在尝试使用 API 创建自定义接口时发现了这一点。 在接口上使用 resolveType 函数有效,但在共享接口的对象上使用 isTypeOf 函数则无效。 我认为这归结为 schema.js 中的以下代码,当没有声明函数时,它假定 resolveType 应该是节点。 这个假设现在对于自定义接口类型是不正确的。

if (!typeComposer.getResolveType()) {
      typeComposer.setResolveType(node => node.internal.type);
    }

顺便说一句,很棒的工作,正好赶上我的项目!

@Wolfsun是的,由于无法通过 SDL 提供resolveType ,我们提供了一个在大多数情况下都可以使用的默认值,并允许通过graphql-js类型提供自定义的。 确实,这与isTypeOf效果不佳。 出于好奇:是否有理由在resolveType使用isTypeOf resolveType

@stefanprobst ,感谢您的解释。 在这个阶段使用 resolveType 对我来说是完全可行的,但是使用 isTypeOf 会稍微干净一些,因为我有一个包含许多实现对象的接口。 我使用数据结构来表示相当复杂的基于网格的布局,我有一个 Card 接口,以及许多实现这个接口的 CardType,它们存储在相对于它们在网格上的位置的数据结构中。 由于我事先不知道 CardType,因此需要一个接口。 如果我想为额外的卡片使用插件系统,isTypeOf 可能会很有用,但我还没有考虑这么多,它甚至可能不是一个好主意,所以改天再说吧!

为了可发现性,我链接到这个例子,也许其他人也觉得有用。 希望这没问题。

我遇到了与@kennedyrose完全相同的问题,但就我而言, @stefanprobst提供的解决方案并没有太大帮助,尽管我不确定自己在做什么,但我只是尝试了所有可能的方法来创建它schema 但它总是返回有关multiple types named...的错误。

这是代码:

exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions;
  const typeDefs = `
    type Wordpress__PAGEAcfIntro {
      title: String
      content: String
    }
    type Wordpress__PAGEAcfThe_problem {
      title: String
      content: String
    }
    type Wordpress__PAGEAcfThe_solution {
      title: String
      content: String
    }
    type Wordpress__PAGEAcf {
      Intro: Wordpress__PAGEAcfIntro
      The_problem: Wordpress__PAGEAcfThe_problem
      The_solution: Wordpress__PAGEAcfThe_solution
    }
    type Wordpress__PAGE implements Node {
      acf: Wordpress__PAGEAcf
    }
  `;
  createTypes(typeDefs);
};

起初我尝试只设置我需要的类型(前 3 个)和他们的字段,但后来我得到了Error: Schema must contain unique named types but contains multiple types named "Wordpress__PAGEAcfIntro". 。 在寻找解决方案时,我尝试了@stefanprobst解决方案(如在我的代码中,但不确定是否正确)但没有任何运气。

此外,这只是使某些字段具有可选字符串值(如果任何字段没有标题或内容,则查询中断)的工作量很大,这只是初始实现,我可能必须这样做在整个网站中相同的更多领域也可能是可选的,所以我非常害怕这个解决方案,真的希望有更好的方法来解决它。

好吧,经过更多的挖掘和尝试之后,实际上并不知道我在做什么,我想出了这个解决问题的代码片段:

exports.createResolvers = ({ createResolvers }) => {
  createResolvers({
    Wordpress__PAGEAcfIntro: {
      title: { type: `String` },
      content: { type: `String` },
    },
    Wordpress__PAGEAcfThe_problem: {
      title: { type: `String` },
      content: { type: `String` },
    },
    Wordpress__PAGEAcfThe_solution: {
      title: { type: `String` },
      content: { type: `String` },
    },
  });
};

虽然我仍然不确定为什么我的第一个解决方案不起作用。 不过,这似乎更容易维护。

最后的想法是,我不确定我是否只是缺乏 Gatsby API 的知识,或者文档对这个特定问题具有误导性。 我认为应该更容易找到how to have optional fields in a graphql query using gatsby问题的解决方案,因为在使用像 WordPress 这样的 CMS 时,这是一件非常基本的事情,您可以在其中拥有多个自定义字段,而且很可能其中很多都会是可选的。

将字段声明为文件数组对我不起作用。

exports.sourceNodes = ({ actions }) => { const { createTypes } = actions const typeDefs = ` type MarkdownRemarkFrontmatter implements Node { images: [File] } type MarkdownRemark implements Node { frontmatter: MarkdownRemarkFrontmatter } ` createTypes(typeDefs) }
可能与https://github.com/gatsbyjs/gatsby/issues/12696 有关

我遇到了与@kennedyrose完全相同的问题,但就我而言, @stefanprobst提供的解决方案并没有太大帮助,尽管我不确定自己在做什么,但我只是尝试了所有可能的方法来创建它schema 但它总是返回有关multiple types named...的错误。

这是代码:

exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions;
  const typeDefs = `
    type Wordpress__PAGEAcfIntro {
      title: String
      content: String
    }
    type Wordpress__PAGEAcfThe_problem {
      title: String
      content: String
    }
    type Wordpress__PAGEAcfThe_solution {
      title: String
      content: String
    }
    type Wordpress__PAGEAcf {
      Intro: Wordpress__PAGEAcfIntro
      The_problem: Wordpress__PAGEAcfThe_problem
      The_solution: Wordpress__PAGEAcfThe_solution
    }
    type Wordpress__PAGE implements Node {
      acf: Wordpress__PAGEAcf
    }
  `;
  createTypes(typeDefs);
};

起初我尝试只设置我需要的类型(前 3 个)和他们的字段,但后来我得到了Error: Schema must contain unique named types but contains multiple types named "Wordpress__PAGEAcfIntro". 。 在寻找解决方案时,我尝试了@stefanprobst解决方案(如在我的代码中,但不确定是否正确)但没有任何运气。

此外,这只是使某些字段具有可选字符串值(如果任何字段没有标题或内容,则查询中断)的工作量很大,这只是初始实现,我可能必须这样做在整个网站中相同的更多领域也可能是可选的,所以我非常害怕这个解决方案,真的希望有更好的方法来解决它。

@eddiemf ,不完全确定,但我认为您可能遇到的问题是您尝试定义的类型已经从 Wordpress 数据源推断出来。 所以实际上它们被定义了两次。 使用 createResolvers 将简单地在现有类型上创建新字段,或者通过事物的外观覆盖现有字段(?)。

2.2.10许多小修复,包括显式节点关系正常工作。

@Wolfsun是的,这正是我的想法,但我觉得文档引导您使用createTypes作为此问题的解决方案,而实际上您应该使用createResolvers

正如我所说,我认为这是处理 WordPress 时的一个非常常见的问题,因此最好为此提供一个有据可查的解决方案。

@eddiemf我对 wordpress 插件不是很熟悉,但是using-wordpress示例中的扩展类型似乎按预期工作。 如果您能够提供指向您的回购的链接,我可以看看可能是什么问题。

请注意,我们知道我们对文档仍然非常了解。 我的猜测是这个问题与哪些类型必须实现Node接口有关——这应该主要由插件本身处理(一旦它们被移植到新的 API)。

Node接口的语义是在 Gatsby 的内部数据存储中标记实际节点支持的类型,即插件通过createNode操作创建的对象,并具有id字段。 这意味着虽然Frontmatter类型只是在MarkdownRemark节点上定义frontmatter字段上的形状,但我猜想 wordpress 插件实际上注册了很多顶部 -级别节点类型。

@stefanprobst我完全理解现在对文档的了解,我只是想帮忙:)

简而言之,我的应用程序中的 WordPress 页面具有类似于page -> acf -> someGroupOfFields -> actualField 。 在查询中,我将执行以下操作作为示例:

wordpressPage(slug: { eq: "home" }) {
  acf {
    intro {
      title
      content
    }
  }
}

类型会自动生成为类似于WordPress__PAGE -> WordPress__PAGEAcf -> WordPress__PAGEAcfIntro 。 问题是这些字段通常只是可选字段( titlecontent ),所以如果没有提供这些字段之一,我的查询就会中断,因为它是根据获取的自动生成的数据。

我看不到插件自动创建这些字段的可能方法(它怎么知道?),但我可能错了。 所以我的第一个解决方案是只使用createTypes并使用适当的字段创建这个WordPress__PAGEAcfIntro类型,但后来我得到了提到的错误。

我会说我的类型创建和 Gatsby/源插件在获取数据后创建相同类型之间存在一些冲突,但我真的不是做出假设的最佳位置,因为我对 GraphQL 或 Gatsby API 一点经验都没有,所以我不确定事情应该如何实际运作。

但正如我所说,就目前而言,就像在我上一篇文章中一样创建解析器工作得非常好,所以我基本上是为我需要获取的所有内容创建类型,因为它们在 CMS 中始终是可选的。

您可以在此处查看我的gatsby-node.js以了解我是如何做到的。

@eddiemf这很有趣。 您是否有“架构必须包含唯一命名类型但包含多个名为“Wordpress__PAGEAcfIntro”的类型”错误的示例? Gatsby 应该能够处理推断的覆盖类型,所以我不确定为什么您的代码不起作用。

@freiksenet我在尝试解决这个问题时

exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions;
  const typeDefs = `
    type Wordpress__PAGEAcfIntro {
      title: String
      content: String
    }
  `;
  createTypes(typeDefs);
};

title字段是在 CMS 中设置的,但是content是空的,所以我尝试了上面的代码片段并得到了这个唯一名称错误。

我也认为可以覆盖类型也是如此,因为这就是有关此新功能的文档和文章的内容,所以我很惊讶地发现它没有按预期工作。

我认为这与以下问题几乎相同:

我正在尝试为 gatsby-transformer-remark 生成的 frontmatter 节点设置架构,但总是遇到此错误: Error: Schema must contain unique named types but contains multiple types named "MarkdownRemarkFrontmatter".

我的架构如下所示:

type MarkdownRemarkFrontmatter implements Node {
  title: String
}

不同的是,对他有用的解决方案实际上对我不起作用。 但看起来implements Node部分在这里被错误地使用,而在我的情况下它应该是正确的。

@eddiemf您需要在某处使用非节点类型来覆盖它。 所以MarkdownFrontmatter (没有实现Node)需要在Markdown节点中使用, Wordpress__PageAcfIntro应该在WorpressPage中使用。 我们将添加一个更好的错误,但我们已经通过这种方式实现了这种行为,这样人们就不会意外地覆盖内联节点。

@freiksenet你的意思是像我在这里做的那样?

我遇到了与@kennedyrose完全相同的问题,但就我而言, @stefanprobst提供的解决方案并没有太大帮助,尽管我不确定自己在做什么,但我只是尝试了所有可能的方法来创建它schema 但它总是返回有关multiple types named...的错误。

这是代码:

exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions;
  const typeDefs = `
    type Wordpress__PAGEAcfIntro {
      title: String
      content: String
    }
    type Wordpress__PAGEAcfThe_problem {
      title: String
      content: String
    }
    type Wordpress__PAGEAcfThe_solution {
      title: String
      content: String
    }
    type Wordpress__PAGEAcf {
      Intro: Wordpress__PAGEAcfIntro
      The_problem: Wordpress__PAGEAcfThe_problem
      The_solution: Wordpress__PAGEAcfThe_solution
    }
    type Wordpress__PAGE implements Node {
      acf: Wordpress__PAGEAcf
    }
  `;
  createTypes(typeDefs);
};

起初我尝试只设置我需要的类型(前 3 个)和他们的字段,但后来我得到了Error: Schema must contain unique named types but contains multiple types named "Wordpress__PAGEAcfIntro". 。 在寻找解决方案时,我尝试了@stefanprobst解决方案(如在我的代码中,但不确定是否正确)但没有任何运气。

此外,这只是使某些字段具有可选字符串值(如果任何字段没有标题或内容,则查询中断)的工作量很大,这只是初始实现,我可能必须这样做在整个网站中相同的更多领域也可能是可选的,所以我非常害怕这个解决方案,真的希望有更好的方法来解决它。

我试图通过设置每个可能的字段并在示例中使用它们来创建“整个类型树”或“类型模型”,但我遇到了相同的错误(有时会出现稍微不同的错误,指责其他类型的不是唯一的)。

虽然我必须承认,当我尝试这个时,我不知道自己在做什么,我只是尝试了各种可能的解决方案组合,但对我来说所有这些都失败了。

@eddiemf是否可以获取您正在使用的 wordpress 网站的网址? 我想自己测试它以找出导致错误的原因。

@freiksenet当然,网址是https://gatsbyislove.com
我将介绍部分中的title字段留空,以便您可以对其进行检查。

您可以在https://gatsbyislove.netlify.com/看到介绍部分没有标题,但由于我创建了解析器,它可以正常工作。

@eddiemf 非常感谢! 得到错误,将调查。

大声笑,这是最有趣的错误。 所以我们不小心大写了 wordpress 类型。 它们都应该以wordpress开头。 相反,我们将 Nodes 保持为小写,但将内部对象保持为大写。 所以这会奏效:

     type Wordpress__PAGEAcfIntro {
       title: String
       content: String
     }

    type Wordpress__PAGEAcf {
      Intro: Wordpress__PAGEAcfIntro
    }

    type wordpress__PAGE implements Node {
      acf: wordpress__PAGEAcf
    }

我现在正在推动修复,所以所有类型都以小写w开头。

厉害了😄
但现在我认为,至少对于这种特定情况,使用解析器可以使代码更好地组织,因为我不需要一直声明这些嵌套字段,直到实现Node的主要字段。

它真的更好还是我在这里没有看到什么?

无论如何,我会尝试不同的解决方案,看看哪一个最适合我。

感谢您的大力支持!

@eddiemf可以使用createResolvers ,但是您不会在filtersort此类根字段的参数中获得这些字段。 如果对您来说没问题,请使用createResolvers

这是修复: https :

我明白了,在大多数情况下这应该不是问题,因为filtersort通常是在始终填充的字段上制作的,但我会记住这一点。 再次感谢 :)

编辑:这不再发生,即使我没有更新任何东西。 我不知道发生了什么,或者是什么让我相信它不起作用; 但它现在有效......伟大的工作伙计们!

大家好,我玩过这个代码:

exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions
  createTypes(`
    type MarkdownRemarkFrontmatter {
      image: File  // <---- could be a relative path, could be an empty string
    }

    type MarkdownRemark implements Node {
      frontmatter: MarkdownRemarkFrontmatter
    }
  `)
}

我希望在查询降价备注节点时得到File节点或null节点。 相反,我总是得到null ...我必须手动查找文件:

createResolvers({
    MarkdownRemarkFrontmatter: {
      image: {
        type: `File`,
        resolve(src, args, context) {
          const filePath = src[info.fieldName]
          // find & return the file node
        }
      }
    }
  })

有没有办法让我简单地告诉 gatsby,“这将是一个文件节点,请找到它或返回 null”?

你好! 即使设置了createTypes是否有可能出现warning There are conflicting field types in your data. GraphQL schema will omit those fields以防止 GraphQL 省略字段?

这是来自gatsby develop的警告:

ThumbprintToken.tokens.value.web:
 - type: number
   value: 1025
   source: File "../packages/thumbprint-tokens/src/tokens/breakpoint.json"
 - type: string
   value: '4px'
   source: File "../packages/thumbprint-tokens/src/tokens/border-radius.json"

这是createTypes的用法:

exports.sourceNodes = ({ actions }) => {
    const { createTypes } = actions;
    const typeDefs = `
        type ThumbprintToken implements Node {
            tokens: [ThumbprintTokenTokens!]!
        }

        type ThumbprintTokenTokens {
            value: ThumbprintTokenTokensValue!
        }

        type ThumbprintTokenTokensValue {
            web: String
            ios: String
            android: String
        }
    `;
    createTypes(typeDefs);
};

在不使用createTypes ,GraphQL 确实省略了这些字段。 这些 JSON 文件的数据来自gatsby-source-filesystemgatsby-transformer-json 。 我使用了一个函数typeName的配置gatsby-transformer-json

如果似乎不应该出现警告,很高兴制作一个小的复制品。

你好!

使用内置的JSON类型时出现错误。

// gatsby-node.js

export const sourceNodes = ({ actions: { createTypes }, schema }) => {
  createTypes([
    schema.buildObjectType({
      name: 'MyTypeName',
      fields: {
        id: 'ID!',
        json: 'JSON!',
      },
    }),
  ])
}

发出以下错误:

success source and transform nodes — 1.550 s
error UNHANDLED REJECTION


  Error: Schema must contain unique named types but contains multiple types named "JSON".

  - Array.reduce

  - SchemaComposer.js:122 SchemaComposer.buildSchema
    [gatsby-ww]/[graphql-compose]/lib/SchemaComposer.js:122:12

  - schema.js:480
    [gatsby-starter-ww]/[gatsby]/dist/schema/schema.js:480:47

  - Generator.next

  - new Promise

  - schema.js:539 addCustomResolveFunctions
    [gatsby-starter-ww]/[gatsby]/dist/schema/schema.js:539:18

  - schema.js:162
    [gatsby-starter-ww]/[gatsby]/dist/schema/schema.js:162:11

  - Generator.next

这可能是graphql-compose提供自己的JSON类型而不是使用gatsby/graphql版本的结果吗?



gatsby info输出:


  System:
    OS: macOS 10.14.2
    CPU: (4) x64 Intel(R) Core(TM) i5-7600K CPU @ 3.80GHz
    Shell: 5.6.2 - /usr/local/bin/zsh
  Binaries:
    Node: 10.15.3 - /var/folders/3z/fgqk0pmx30l2pc4801884_sm0000gn/T/yarn--1554423671012-0.23391063288947023/node
    Yarn: 1.12.3 - /var/folders/3z/fgqk0pmx30l2pc4801884_sm0000gn/T/yarn--1554423671012-0.23391063288947023/yarn
    npm: 6.4.1 - ~/.n/bin/npm
  Languages:
    Python: 2.7.10 - /usr/bin/python
  Browsers:
    Chrome: 73.0.3683.86
    Firefox: 64.0
    Safari: 12.0.2
  npmPackages:
    gatsby: 2.3.11 => 2.3.11

@angeloashmore它应该通过https://github.com/gatsbyjs/gatsby/pull/13028修复,我今天会制作一个预发布版本,以便您进行测试。

@danoc目前没有办法禁用这样的警告。 使用https://github.com/gatsbyjs/gatsby/pull/13028如果您使用@dontInfer ,则不应进行示例值检查,并且不会出现警告。 但是也不会有任何推断。

@d4rekanguok你能为此提供一个小的复制品吗? 它应该像你描述的那样工作。

@samovertonjr @smurrayatwork请不要取消这个问题。 谢谢!

不知道点击会影响每个人。

已发布2.4.0-alpha.2

@prashant-andani Pleae 不要解开这个问题。 谢谢!

嗨,那是偶然的……抱歉

在星期二,二零一九年四月三十日在18:38,伦纳特[email protected]写道:

@prashant-andani https://github.com/prashant-andani请不要取消固定
这个问题。 谢谢!


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/gatsbyjs/gatsby/issues/12272#issuecomment-487944961
或静音线程
https://github.com/notifications/unsubscribe-auth/AAGKROYK4RRFYRE3MOQL2NLPTBAE7ANCNFSM4G3OVU5Q
.

>

问候
普拉尚·S·安达尼

映射的执行顺序是什么? 我有一组帖子,我试图将它们映射到诸如post -> authors示例之类的类别。 我的原始源代码有编号,所以我使用创建类型来强制执行字符串假设。 不幸的是,如果我使用createTypes ,映射似乎不起作用。 我认为createTypes在创建主模式之前运行了吗?

//gatsby-config.js
  mapping: {
    "MarkdownRemark.frontmatter.speaker": `AuthorsYaml.name`, // works as expected
    "MarkdownRemark.frontmatter.categories": `CategoriesYaml`,
  },
//gatsby-node.js
exports.sourceNodes = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
  type MarkdownRemarkFrontmatter {
    categories: [String]
  }

  type MarkdownRemark implements Node {
    frontmatter: MarkdownRemarkFrontmatter
  }
  `
  createTypes(typeDefs)
}

查询…

{
  allMarkdownRemark {
    nodes {
      frontmatter {
        categories
        speaker {
          name
        }
      }
    }
  }
}

产量…

    "allMarkdownRemark": {
      "nodes": [
        {
          "frontmatter": {
            "categories": [
              "22"
            ],
            "speaker": {
              "name": "Sean Higgins"
            }
          }
        }
]

我找到了一个可行的解决方案,我告诉类型期望 CategoryYaml 类型,而不是将其强制为字符串并使用自定义解析器来获取数据。

似乎有了映射机制,我可能应该能够强制进行字符串强制转换,然后使用映射为我进行连接。

也许我错过了什么?

嵌套类型:

例如

  PrismicNewsBody
    PrismicNewsBody
      PrismicNewsBodyPrimary
      PrismicNewsBodyItems

类型具有可重复组的概念(最终出现在 Items 中),但如果组中没有任何内容,则查询将失败(因为您正在查询尚未推断的类型成员)。

但是,如果您定义类型以便可以声明items ,则一旦您实际使用重复组,它就会被称为重复项; 让您处于拥有 [0.. ] 条数据但只能拥有支持 0 或 1.. 的代码的状态。

那么代码(是否已经有方法)可以合并不是implements Node吗?


下一个: Prismic 的类型系统是一个皇家 PITA,当使用切片等时,会创建名称附加到其容器名称的类型。 这是合理的,因为它的行为方式 - 它有一个“切片库”的概念,但它是一个复制库,而不是一个参考库 - 类型定义只是复制到内容类型的类型 def 中。

OTOH 程序员讨厌重复。 所以我通过在一页上定义它,将它保存到“库”并向外复制来确保给定名称的 Slice 类型在任何地方都是相同的 - 我们(我们吗?)有能力吗?添加接口而不是

query {
  pageOne {
    data {
      body {
        ... on PageOneBodyMyslice {
          primary {
            field
          }
        }
      }
    }
  }
  pageTwo {
    data {
      body {
        ... on PageTwoBodyMyslice {
          primary {
            field
          }
        }
      }
    }
  }
}

...我们可以

``
接口 MySlice {
基本的 {
场地
}
}

不知何故,我们将页面切片类型标记为实现这一点

询问 {
第一个{
数据 {
身体 {
... 在 MySlice {
... MySliceFragment

等等

``

@awilkins gatsby-source-prismic作者在这里。

我们正在努力实现自动模式加载,以便 Gatsby 了解有关您的自定义类型形状的所有信息。 我们希望在本周发布一个测试版(其中还包括一个预览系统!)。

您将需要向插件提供您的架构。

回复:你关于接口的问题,这个想法是合理的,但我不确定这些类型是否合并。 所有切片都实现Node接口并遵循pascalcase(${customType} ${sliceZoneName} ${sliceName})命名约定。

@motleydev嗨! 所以 createTypes 和 mappings 几乎是同时运行的。 用户定义的类型总是优先于映射类型。 在这种情况下,您需要使用自定义解析器。 在这种情况下,Gatsby 无法真正知道您想要一个将被序列化为字符串的对象,因为 String 和 CategoriesYaml 不是兼容的类型。

@angeloashmore感谢您的回复! 我会期待的; 我的客户无疑会想要改变,这会让事情变得更容易。

接口

我相信您已经注意到,由于 Prismic 的工作方式,它的“切片库”只是让您将切片类型信息复制到您的自定义类型中 - 因此,您可以实际拥有的 POV 中需要不同的类型名称在不同的页面类型中具有相同名称的完全不同的切片类型。

在 CMS 中更新切片类型是一个 PITA,因为您必须在一个地方进行,然后将其复制到所有其他类型,然后打开所有内容并更新它(如果您引入了任何不兼容的更改)。

但是……如果您表现得很好并且始终如一地执行此操作(或者只是拥有一组非常有用且成熟的切片),那么能够为每种切片类型声明一个全局 GraphQL 片段或(也许? ) 即使是所有切片的一个也会很可爱 - 但片段只能应用于一种类型或接口,因此希望将通用接口分配给不同页面上具有相同名称的不同但相同的切片类型。

已发布[email protected]

大家好。 我已经更新到 Gatsby 2.5.0但似乎遇到了与@angeloashmore相同的类型冲突, graphql-composegatsby之间的 JSON 类型。 有什么简单的方法可以解决这个冲突吗?

如果有帮助,我将从 Gatsby 1.9.x 升级。 日志如下:

``
错误未处理的拒绝

错误:架构必须包含唯一命名的类型,但包含多个名为“JSON”的类型。

  • Array.reduce

  • Array.reduce

  • SchemaComposer.js:130 SchemaComposer.buildSchema
    [博客]/[graphql-compose]/lib/SchemaComposer.js:130:12

  • schema.js:500
    [博客]/[gatsby]/dist/schema/schema.js:500:47

  • 生成器.next

  • debuggability.js:313 Promise._execute
    [npm]/[gatsby-cli]/[bluebird]/js/release/debuggability.js:313:9

  • promise.js:483 Promise._resolveFromExecutor
    [npm]/[gatsby-cli]/[bluebird]/js/release/promise.js:483:18

  • promise.js:79 新承诺
    [npm]/[gatsby-cli]/[bluebird]/js/release/promise.js:79:10

  • schema.js:559 addCustomResolveFunctions
    [博客]/[gatsby]/dist/schema/schema.js:559:18

  • schema.js:163
    [博客]/[gatsby]/dist/schema/schema.js:163:11

  • 生成器.next

  • util.js:16 tryCatcher
    [npm]/[gatsby-cli]/[bluebird]/js/release/util.js:16:23

  • promise.js:512 Promise._settlePromiseFromHandler
    [npm]/[gatsby-cli]/[bluebird]/js/release/promise.js:512:31

  • promise.js:569 Promise._settlePromise
    [npm]/[gatsby-cli]/[bluebird]/js/release/promise.js:569:18

  • promise.js:606 Promise._settlePromiseCtx
    [npm]/[gatsby-cli]/[bluebird]/js/release/promise.js:606:10

  • async.js:142 _drainQueueStep
    [npm]/[gatsby-cli]/[bluebird]/js/release/async.js:142:12
    ````

@Spiderpig86升级到 2.4 时有这个问题吗? 我想知道您是否可以提供一个复制项目(或您的应用程序的代码)。

最后,我最终废弃了我拥有的所有依赖项并完全重新安装了所有依赖项。 这是我现在使用的配置,效果很好。

我们遇到了gatsby-source-prismic v3.0.0-alpha.2 和gatsby-source-filesystem文件节点的问题。

我们告诉盖茨比图像领域将有localFile型字段File ,但如果没有File在网站的任何地方创建节点,盖茨比不知道File是。

Error: Type with name "File" does not exists

抱歉,这可能是一个gatsby-source-filesystem问题,而不是架构定制问题,但只是想知道是否有一种方法可以可靠地告诉 Gatsby File是什么。 gatsby-source-filesystem是否需要从源插件可以显式提供给 Gatsby 的包中导出文件类型定义?

相关问题: https :

@angeloashmore我们可以让gatsby-source-filesystem显式注册File类型(并且在配置的路径不存在时使它不是panic )。 这样就可以只在gatsby-config提供gatsby-source-filesystem (不带选项),并在模式中提供File类型。 在你的情况下,这样的事情会起作用吗? 这仍然需要安装gatsby-source-filesystem ,因为它拥有该类型——但目前还没有具体的计划可能会在某个时候将File提升为核心类型。

@stefanprobst是的,类似的东西看起来可以工作。

关于插件作者,如果插件不确定地创建了一个 File 节点,这意味着:

  1. 插件作者应该要求插件的用户总是将gatsby-source-filesystem到他们的gatsby-config.js以确保 Gatsby 知道File类型,或者
  2. 如果插件可以确定将创建File节点,则插件作者仅在类型定义中包含使用File类型的字段。 即,如果我们知道将不会使用File字段创建节点,则不要在使用它的节点上包含File字段。 如果查询包含现在不存在的字段,这可能会破坏构建。

使用选项 1,如果用户不提供路径,则用户可以在每个developbuild上看到不存在的路径错误消息。 在某些项目中,除了提供File类型之外,可能不需要使用gatsby-source-filesystem 。 也许我们可以更改它,以便它仅在提供路径的情况下检查路径是否存在?

使用选项 2,插件作者需要处理跟踪File节点创建的复杂性增加。 这可能就像更新全局变量一样简单,但仍然需要考虑。

如果gatsby-source-filesystem导出File类型(通过 GraphQL 对象或 SDL 字符串)并允许插件作者使用createType注册它,则用户不需要更改他们的工作流程和插件作者不需要跟踪File使用情况。

如果/当File类型更改并且插件使用具有不兼容的File定义的不同gatsby-source-filesystem版本时,这可能会导致将来出现问题。 这现在可以正常工作了,因为据我所知,Gatsby 综合推断了形状。

作为参考,这是我们使用File定义字段的地方: standardTypes.graphql 。 这会直接传递给createTypes

如果我错,请纠正我@stefanprobst ,但我认为将File设为核心类型不会是一个重大变化。 它只会打破过去失败的情况,因为File不在那里。 所以我建议我们这样做。

很抱歉把这个放在这里,如果你能引导我朝着正确的方向前进,我会提交一份更有针对性的票。

tl; dr Frontmatter 驱动的类型。

我来自 CMS 背景,因此控制我的模式很有吸引力。 我的 Gatsby 站点在 YAML 中使用带有type: author Markdown frontmatter 来指定“类型”,但由于一切都是MarkdownRemark节点类型,因此我没有得到真正的模式。

我现在通过createTypes有一个Author类型。 我尝试了多种方法来创建Author节点,最好是作为MarkdownRemark节点的子节点(因此它会被免费删除),但我找不到办法所以。 createNode在解析器中似乎不可用。

@pauleveritt在字段之间设置外键关系的一种方法是使用@link指令。 对于典型的博客(帖子为.md文件、作者信息和.yaml文件中的标签),它可能如下所示:

exports.sourceNodes = ({ actions }) => {
  actions.createTypes(`
    type MarkdownRemark implements Node <strong i="10">@dontInfer</strong> {
      frontmatter: Frontmatter
    }
    type Frontmatter {
      title: String!
      author: AuthorYaml @link(by: "email")
      tags: [TagYaml] @link(by: "slug")
    }
    type AuthorYaml implements Node <strong i="11">@dontInfer</strong> {
      name: String!
      email: String!
      info: String
      posts: [MarkdownRemark] @link(by: "frontmatter.author.email", from: "email")
    }
    type TagYaml implements Node <strong i="12">@dontInfer</strong> {
      slug: String!
      title: String!
      description: String
      posts: [MarkdownRemark] @link(by: "frontmatter.tags.slug", from: "slug")
    }
  `)
}

我也把它放在这个 repo 中

我们也终于有了在架构定制的API有点文档这里。 如果有任何不清楚或遗漏的地方,请告诉我们。

@freiksenet我认为这取决于是否意味着将File的类型定义移动到核心,或者现在gatsby-source-filesystem (部分)是否也应该随之移动。 我认为单独移动File应该没问题。

@stefanprobst 简洁的概念......但我认为挑战就在这里:

https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-transformer-remark/src/on-node-create.js#L45

我不知道如何根据来自现有gatsby-source-filesystemgatsby-transformer-remark的frontmatter 将节点创建为AuthorYaml gatsby-transformer-remark

我可能需要停止将链接目标视为 Markdown 内容并使用.yaml gatsby-transformer-yaml 来命名节点类型。

@pauleveritt

使用.yaml gatsby-transformer-yaml

是的,这就是我在链接示例中所做的。 有什么理由将作者信息放在 Markdown 文件中?

@stefanprobst “有什么理由将作者信息放在

我希望一切——作者、类别、标签等——都成为一种丰富的“资源”,人们可以在 Markdown 中编辑、添加图像、代码块、MDX 内容等。

但我不会轻易得到那个。 :) 我会为那些切换到 YAML。 唉,我的“常规内容”(教程、文章、博客文章)也可以使用具有不同模式的类型,但我会坚持使用推断模式,而不是自 2.2 以来推广的显式类型/模式的新世界。

@pauleveritt是的, gatsby-transformer-remark只会创建一个MarkdownRemark类型 - 使用它来表示不同的实体会有问题。 所以这不是一个真正与模式相关的问题,但你需要一个 Markdown 转换器插件,它可以创建不同命名的 makrkdown 节点(这应该是非常可行的)。

也就是说(仅作为实验),可以使用MarkdownRemark节点并在 frontmatter 字段上使用@link来执行此操作。 它看起来有点难看,但它有效,请参见此处的示例。

@stefanprobst我理解你的意思,但我认为如果目标是减少推断模式和更多声明模式,Markdown frontmatter 应该在范围内。 带有 Markdown 文件的 Gatsby 是一种流行的组合。 一世

如果gatsby-transformer-remark没有使分叉的表面积如此之大,那么在 Markdown 中拥有不同的类型/模式会更容易。 执行节点类型分配的箭头函数中有很多事情要做。

让我们结束这次谈话。 我可能是少数希望在 Markdown 中使用不同内容类型的人。 非常感谢示例代码,这就是我现在要采取的方向。

关于runQuery及其query参数......我有filter正常工作,但我无法得到sort工作的示例。 使用 sort 的测试似乎使用来自模板字符串的runQuery 。 当我尝试sort: { order: 'DESC', fields: ['frontmatter___date'] } (或使用 'desc' 作为字符串)时,解析就失效了。

@pauleveritt它是否适用于order上的数组?

@stefanprobst唉,不。 这个:

sort: { order: ['DESC'], fields: ['frontmatter___date'] }

...造成:

error gatsby-node.js returned an error

  TypeError: Cannot read property 'resolve' of undefined

  - prepare-nodes.js:34 awaitSiftField
    [gatsby-theme-bulmaio]/[gatsby]/dist/redux/prepare-nodes.js:34:13

  - prepare-nodes.js:52 
    [gatsby-theme-bulmaio]/[gatsby]/dist/redux/prepare-nodes.js:52:69

  - Array.map

  - prepare-nodes.js:52 resolveRecursive
    [gatsby-theme-bulmaio]/[gatsby]/dist/redux/prepare-nodes.js:52:44
...

@pauleveritt不幸的是,我无法使用此基本设置进行重现——您能否为此问题提供一个小的重现? 这将非常有帮助,谢谢!

@stefanprobst找到原因:它是 2.8.x——要我提交一张新票吗?

想象一下您的gatsby-blog用于 YAML 存储库。 将此添加到gatsby-node.js

exports.createResolvers = ({ createResolvers, schema }) => {
  createResolvers({
    Query: {
      allResourcesByType: {
        type: ['MarkdownRemark'],
        args: {
          resourceType: 'String'
        },
        resolve(source, args, context, info) {
          return context.nodeModel.runQuery({
            query: {
              filter: {
                frontmatter: {}
              },
              sort: { fields: ["frontmatter___title"], order: ["ASC"] },
            },
            type: `MarkdownRemark`
          })
        }
      }
    }
  })
}

在 gatsby 2.7.6 中,您可以在资源管理器中查询allResourcesByType 。 在 2.8.0-2.8.2 中,您会收到解析错误。

@stefanprobst我正在尝试将我在帖子的前端指定为字符串数组的标签转换为Tag其中包含titleslug字段,其中title只是我写的字符串和slug = _.kebabCase(title) 。 我把这个片段放在一起

exports.sourceNodes = ({ actions, schema }) => {
  actions.createTypes([
    `type MarkdownRemark implements Node {
      frontmatter: MarkdownRemarkFrontmatter
    }`,
    `type Tag { title: String!, slug: String! }`,
    schema.buildObjectType({
      name: `MarkdownRemarkFrontmatter`,
      fields: {
        tags: {
          type: `[Tag!]`,
          resolve(source) {
            if (!source.tags) return null
            return source.tags.map(tag => ({
              title: tag,
              slug: kebabCase(tag),
            }))
          },
        },
      },
    }),
  ])
}

这适用于将Tag类型添加到MarkdownRemarkFrontmatter.tags 。 但是当然,当我尝试将所有标签与

tags: allMarkdownRemark {
  group(field: frontmatter___tags) {
    title: fieldValue
    count: totalCount
  }
}

我只得到我写到 frontmatter 的字符串。 我试着像这样为allTags编写一个解析器

exports.createResolvers = ({ createResolvers }) => {
  const resolvers = {
    Query: {
      allTags: {
        type: [`Tag`],
        resolve(source, args, context) {
          return context.nodeModel.getAllNodes({
            type: `MarkdownRemarkFrontmatterTags`,
          })
        },
      },
    },
  }
  createResolvers(resolvers)
}

但无法让它工作。 有什么建议吗?

@janosh

  • 当改变的字段类型frontmatter.tags[String][Tag] ,则field的参数group也将必须要么调整到frontmatter___tags___titlefrontmatter___tags___slug 。 不幸的是,这还不能正常工作,因为我们不会为group字段调用字段解析器,仅适用于filtersort字段(请参阅#11368)。
  • 目前,您可以通过使用虚拟过滤器触发字段解析器来解决此问题:
{
  allMarkdownRemark(filter: {frontmatter: {tags: {elemMatch: {title: {ne: null}}}}}) {
    group(field: frontmatter___tags___title) {
      fieldValue
      totalCount
      nodes {
        frontmatter {
          title
        }
      }
    }
  }
}

解决这个问题在我的待办事项清单上——我会尽快解决

  • 您的createResolvers片段的问题是: getAllNodes将按类型检索节点,其中“节点”表示由源或转换器插件创建的具有唯一 ID 的对象(使用createNode动作)。 因此,您必须检索MarkdownRemark节点,然后在解析器中进一步操作结果。
  • 根据您的用例,您甚至可能不需要使用createTypes ,但可以简单地添加自定义根查询字段以按标签对帖子进行分组,并包含一个 slug 字段:
// gatsby-node.js
const { kebabCase } = require(`lodash`)

exports.createResolvers = ({ createResolvers }) => {
  createResolvers({
    Query: {
      allMarkdownRemarkGroupedByTag: {
        type: [
          `type MarkdownRemarkGroup {
            nodes: [MarkdownRemark]
            count: Int
            tag: String
            slug: String
          }`,
        ],
        resolve(source, args, context, info) {
          const allMarkdownRemarkNodes = context.nodeModel.getAllNodes({
            type: `MarkdownRemark`,
          })

          const groupedByTag = allMarkdownRemarkNodes.reduce((acc, node) => {
            const { tags } = node.frontmatter
            tags.forEach(tag => {
              acc[tag] = (acc[tag] || []).concat(node)
            })
            return acc
          }, {})

          return Object.entries(groupedByTag).map(([tag, nodes]) => {
            return {
              nodes,
              count: nodes.length,
              tag,
              slug: kebabCase(tag),
            }
          })
        },
      },
    },
  })
}

@stefanprobst感谢您的快速回复!

因此,您必须检索 MarkdownRemark 节点,然后在解析器中进一步操作结果。

我想这样做,但怀疑我忽略了一些东西,因此从错误的角度接近它。 感谢您解决此问题并计划为group字段添加对字段解析器的支持!

也许另一种解决方案是让Tag实现Node这样 Gatsby 就会自动创建allTag查询。

type Tag implements Node { title: String!, slug: String! }

然后我所要做的就是每次在调用schema.buildObjectType for MarkdownRemarkFrontmatter遇到一个新节点时,实际创建Tag类型的节点。 是否可以从解析器中应用这样的副作用? 即在这种情况下检查Tag是否存在标题MyTag ,如果不存在createNode({type: `Tag`, title: `MyTag, slug: `my-tag` })

@pauleveritt抱歉回复晚了!

在您的示例中,排序应该适用于:

exports.createResolvers = ({ createResolvers }) => {
  createResolvers({
    Query: {
      allResourcesByType: {
        type: ["MarkdownRemark"],
        args: {
          resourceType: "String",
        },
        resolve(source, args, context, info) {
          return context.nodeModel.runQuery({
            query: {
              sort: { fields: ["frontmatter.post_title"], order: ["DESC"] },
            },
            type: "MarkdownRemark",
          })
        },
      },
    },
  })
}

请注意, sort fieldsorder都是数组,并且fields使用点符号代替三重下划线来分隔字段。 (我们必须记录下来!编辑:#14681)
runQuery与使用查询字符串时(即在 Graph_i_ql 资源管理器中)不同的原因有两个:

  • 在列表字段上,标量值将自动解析为单项数组(这就是为什么可以只说DESC )。 我们目前没有用runQuery (我们可能应该这样做)
  • fieldsorder都是GraphQLEnum字段。 在 Graph_i_ql 中,您将使用枚举键,而runQuery需要内部枚举值。 例如fields枚举用三重下划线分隔字段,因为 graphql 不允许使用点,但在内部这会转换为点表示法中的字段。

@janosh对已解析字段的分组现在应该与[email protected]

@stefanprobst哇,真快! 感谢更新。

有没有办法在单个分组中同时获得标签的标题和 slug? 我只能弄清楚如何分组,然后只能访问其中一个。

{
  tags: allMarkdownRemark {
    group(field: frontmatter___tags___(slug|title)) {
     (slug|title): fieldValue
      count: totalCount
    }
  }
}

如果我尝试按frontmatter___tags分组,

{
  tags: allMarkdownRemark {
    group(field: frontmatter___tags) {
      tag: fieldValue
      count: totalCount

    }
  }
}

我只得到一个结果

{
  "data": {
    "tags": {
      "group": [
        {
          "tag": "[object Object]",
          "count": 52
        }
      ]
    }
  }
}

@janosh您只能按一个字段分组(不可能进行分层子分组)。 如果您需要titleslug ,您可以按一个分组,并从结果中获取另一个的值(也许我误解了?):

{
  allMarkdownRemark {
    group(field: frontmatter___tags___title) {
      fieldValue
      totalCount
      nodes {
        frontmatter {
          tags {
            slug
            title
          }
        }
      }
    }
  }
}

@stefanprobst这不适合我的用例,但这只是一个小小的不便。 感谢您为架构定制所做的所有出色工作!

自 2.2.0 以来,我一直遇到一个问题,这似乎与操作系统有关。 我很高兴让另一个 Windows 10 用户关注它进行测试,以便我可以确认或排除它: https :

谢谢!

@laradevitt我目前无法访问 Windows 机器,而且我也不熟悉gatsby-plugin-netlify-cms-paths - 但是如果您将frontmatter.image上的路径替换为相对路径,它是否有效,即../static/media/gatsby-astronaut.png

@stefanprobst - 感谢您的回复! 是的,确实如此,但插件的重点是您不必使用相对路径:

一个 gatsby 插件,用于在使用 Netlify CMS 编辑 Markdown 文件时将 Markdown 文件中的文件路径更改为 Gatsby 友好的路径。

我创建了一个带有修复程序的拉取请求,该修复程序使返回的相对路径平台不可知。

我仍然不知道为什么它会在 2.2.0 中崩溃。 🤷‍♀

@laradevitt啊,对不起,应该检查自述文件。

我仍然不知道为什么它会在 2.2.0 中崩溃。

这很可能是 Gatsby 中的回归,因为 2.2.0 类型推断发生了一些变化——但在gatsby-plugin-netlify-cms-paths规范化路径肯定看起来更正确:+1:

@stefanprobst

我刚刚创建了一个与架构定制相关的新票证。 如果我最好关闭它并将它放在这个伞线上,请告诉我。

https://github.com/gatsbyjs/gatsby/issues/16099

谢谢!

描述

使用模式自定义时,graphQL 查询中的参数不起作用。

在观看了关于高级 GraphQL 的 Jason 流学习(由于技术困难被分成多个部分)后,我将很多讨论的内容融入到我的主题中。
但是,使用依赖于自定义架构值的 graphQL 参数(如过滤器和排序)不起作用。

重现步骤

在本地运行我的主题的这个分支
启动 /___graphql 并查询所有博客帖子。
现在尝试使用按降序排序的方式再次查询 allBlogPosts。
结果:顺序没有变化(ASC 或 DESC)

示例 2

查询所有帖子并显示其标题。

query MyQuery {
  allBlogPost {
    nodes {
      title
    }
  }
}

结果:工作

现在查询所有带有“lorem-ipsum”标签的帖子

query MyQuery {
  allBlogPost(filter: {tags: {elemMatch: {slug: {eq: "lorem-ipsum"}}}}) {
    nodes {
      title
    }
  }
}

结果:空节点数组。

临时解决方法,它不起作用的可能原因?

尝试查询单个 BlogPost(如果没有给出参数,则默认选择第一个)。
现在尝试查询blogPost(slug: {eq: "herp-derpsum"})
我认为这是有效的,因为我将 slug 添加到 MdxBlogPost 节点。
https://github.com/NickyMeuleman/gatsby-theme-nicky-blog/blob/d29c966e639f4733caf9ee43e9f5755df42db71d/theme/gatsby-node.js#L209 -L210

似乎 graphql 参数使用来自 MdxBlogPost 节点的数据,而不是运行其解析器后的最终结果。 我的怀疑接近了吗?

环境

系统:
操作系统:Linux 4.19 Ubuntu 18.04.2 LTS(仿生海狸)
CPU:(4) x64 Intel(R) Core(TM) i5-2500K CPU @ 3.30GHz
外壳:4.4.19 - /bin/bash
二进制文件:
节点:12.4.0 - /tmp/yarn--1565124765846-0.45931526383359134/node
纱线:1.16.0 - /tmp/yarn--1565124765846-0.45931526383359134/yarn
npm: 6.9.0 - ~/.nvm/versions/node/v12.4.0/bin/npm
语言:
Python:2.7.15+ - /usr/bin/python

补充说明

相关部分代码在主题目录的gatsby-node.js中。
特别是createSchemaCustomizationonCreateNode生命周期。
我试图大量评论以显示我的过程。

@NickyMeuleman抱歉还没来得及仔细观察,但您似乎在 BlogPost 界面上缺少链接扩展名。

您定义MdxBlogPost.tags字段以按名称链接:

tags: {
  type: "[Tag]",
  extensions: {
    link: { by: "name" },
  },
},

在 BlogPost 界面的 typedef 中,您有:

  interface BlogPost <strong i="12">@nodeInterface</strong> {
    tags: [Tag]
  }

它是否与

  interface BlogPost <strong i="16">@nodeInterface</strong> {
    tags: [Tag] @link(by: "name")
  }

相关:#16466

添加您的建议后,使用基于标签的filter查询allBlogPost确实有效! 🎉谢谢!

在未来,我是否能够从接口中删除@link ,因为标签的链接可能因实现 BlogPost 接口的类型而异? (作者的相同问题,即使将@link移至界面级别,过滤那里也不起作用。

@NickyMeuleman
感谢您的尝试!
看完之后,我认为这是我们需要在核心中解决的问题:这两个问题都与我们在手动准备节点时使用接口的解析器这一事实有关,以便我们可以对它们运行过滤器。 相反,我们应该运行我们从接口的resolveType获得的类型的解析器,这也是 graphql 处理选择集时发生的情况。

https://github.com/gatsbyjs/gatsby/pull/17284合并后,我尝试删除主题中的逻辑来解决这个问题。

二手 Gatsby 版本: 2.15.14
它似乎不起作用。
https://github.com/NickyMeuleman/gatsby-theme-nicky-blog/blob/bbc782332e6938daaa2fca1b25d6df7e78f19c6c/theme/gatsby-node.js#L278 -L282
当您注释掉上面链接的行时,不再可能在 GraphQL 中对这些字段进行过滤。

LekoArts 建议我比在不和谐中更公开地提出这个问题,因为它可能是一个很好的文档主题。

我如何用 createSchemaCustomization 描述多对多关系?

我的用例如下:

  1. 我有一个产品和一个商店。 商店可以容纳任意数量的产品,并且产品不是任何一家商店独有的。 表达这两种类型的节点的最佳方式是什么?
  2. 我正在尝试为游戏系统制作一个页面。 我已经将每个能力表示为一个节点。 能力有任意数量的先决条件,以及任意数量的依赖技能。 最好我想链接到技能本身页面中的那些页面(先决条件和依赖项)。 我如何在 graphQL 中以及在 Gatsby 的 api 中为这些关系建模?

@Everspace试试这个 1. 添加到gatsby-node.js 。 我没有尝试过这个,但我正在做一些类似的从自定义类型(使用节点接口)链接到图像的操作,并且它有效。 它确实依赖于链接扩展,因此您需要使用 Gatsby 分配的 ID。 我想您可以在createPages期间将您在数据源中使用的任何 ID 映射到 Gatsby ID 吗?

exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = [
    schema.buildObjectType({
      name: 'Store',
      fields: {
        products: {
          type: "[Product]",
          extensions: {
            link: {},
          },
        },
      },
      interfaces: ['Node'],
    }),
    schema.buildObjectType({
      name: 'Product',
      fields: {
        stores: {
          type: "[Store]",
          extensions: {
            link: {},
          },
        },
      },
      interfaces: ['Node'],
    }),
  ]
  createTypes(typeDefs)
}

@NickyMeuleman你的意思是你没有得到结果或者你没有在输入对象中看到字段?

当我注释掉该链接中date时,按日期排序不再有任何影响。

query MyQuery {
  allBlogPost(sort: {fields: date, order: DESC}) {
    nodes {
      date(fromNow: true)
    }
  }
}

具有与顺序设置为ASC的相同查询相同的(非空)结果。
希望没有必要,因为有一个自定义日期解析器

有没有关于如何处理来自 Contentful 的可选图像的模式自定义的示例?

Schema Customization 发布已经有一段时间了,一切都很稳定。 我想是时候关闭这个问题了🙂

令人难以置信的工作@freiksenet@stefanoverna ❤️

伙计们,如果您遇到与架构定制相关的问题,让我们打开独立的问题。 谢谢!

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

ferMartz picture ferMartz  ·  3评论

dustinhorton picture dustinhorton  ·  3评论

3CordGuy picture 3CordGuy  ·  3评论

andykais picture andykais  ·  3评论

magicly picture magicly  ·  3评论