【问题标题】:Graphql: How can I solve the N + N problem?Graphql:如何解决 N + N 问题?
【发布时间】:2021-12-10 21:43:27
【问题描述】:

在各自的解析器中实现数据加载器来解决 N+1 问题后,我还需要能够解决 N+N 问题。

我需要一个相当有效的数据加载机制来获得这样的关系:

{
  persons (active: true) {
    id,
    given_name,
    projects (active: true) {
      id,
      title,
    }
  }
}

我为此创建了一个简单的实现,返回

{
  persons: [
    {
      id: 1,
      given_name: 'Mike'
      projects: [
        { 
          id: 1,
          title: 'API'
        },
        { 
          id: 2,
          title: 'Frontend'
        }      
      ]
    } 
    {
      id: 2,
      given_name: 'Eddie'
      projects: [
        { 
          id: 2,
          title: 'Frontend'
        },      
        { 
          id: 3,
          title: 'Testing'
        }
      ]
    } 
  ]
}

在 SQL 中,底层结构将由多对多关系表示。

是否有类似 dataloader 的工具来解决这个问题,或者甚至可以用 dataloader 本身解决这个问题?

【问题讨论】:

    标签: graphql graphql-js


    【解决方案1】:

    对 GraphQL 的期望是访问数据库通常是您能做的最快的事情,因此您只需将解析器添加到 Person.projects 以调用数据库。你仍然可以使用 dataLoaders。

    const resolvers = {
      Query: {
        persons(parent, args, context) {
          // 1st call to database
          return someUsersService.list()
        },
      },
      Person: {
        projects(parent, args, context) {
          // this should be a dataLoader behind the scenes.
          // Makes second call to database
          return projectsService.loadByUserId(parent.id)
        }
      }
    }
    

    请记住,现在您的 dataLoader 期望在每个插槽中返回一个对象数组,而不是单个对象。

    【讨论】:

    • 但在我的情况下,人员的结果是项目的根(父)。结果不是父母是一个人,而是一群人。因此,在项目解析器中,您需要处理 someProjectsDataLoaderByPersonId.load(parent.map(parentItem => parentItem.id))。我认为您的 const resolvers 应该重命名为 schema
    • 没有。在 Person.projects 中,父对象是在persons 调用中返回的数组的一个实例。这就是您使用数据加载器的原因:因此,在进行了整个person.projects 调用数组时,您可以使用一个数据加载器来进行单个数据库调用。另外:为什么我将解析器命名为“模式”? “Schema”是 GraphQL SDL 字符串。
    • 据我了解——如果我错了,请纠正我——数据加载器 someUsersDataLoader.load(args.id) 只会查询单个用户。因此 args.id,对吗?但是persons-resolver需要能够查询多个用户,即用户ID。我希望我能理解你的意图。关于架构:我不使用 SDL,并且我(无论如何)误解了您的解析器作为代码架构。不知道为什么。
    • 这就是你调用数据加载器的方式,但不是它们的工作方式。您在很多地方调用someUsersDataLoader.load(id),并将dataLoader 定义为(ids => { doSomeBatchLoadCall(ids); return ids.map(findInResult) }。这意味着您对数据加载器进行了 100 次调用,它对数据库进行了一次调用,并在每次调用时返回其结果。
    • 我更新了上面的代码以更好地反映这一点。
    猜你喜欢
    • 2018-05-20
    • 2020-03-28
    • 2011-02-05
    • 2021-01-22
    • 2021-08-27
    • 1970-01-01
    • 2015-02-12
    • 2019-01-18
    • 1970-01-01
    相关资源
    最近更新 更多