【问题标题】:Load only the data that's needed from database with Graphql仅使用 Graphql 从数据库中加载所需的数据
【发布时间】:2017-06-27 07:20:17
【问题描述】:

我正在学习 graphql,我想我发现了它的一个缺陷。 假设我们有这样的架构

type Hero {
  name: String
  friends: [Person]
}

type Person {
  name: String
}

还有两个查询

{
  hero {
    name
    friends {
      name
    }
  }
}

还有这个

{
  hero {
    name
  }
}

还有一个关系型数据库,它有两个对应的表HerosPersons

如果我的理解是正确的,我无法解决此查询,因此对于第一个查询,生成的 sql 查询将是

select Heros.name, Persons.name
from Heros, Persons
where Hero.name = 'Some' and Persons.heroid = Heros.id

第二个

select Heros.name, Persons.name from Heros

这样只有查询真正需要的字段才会从数据库中加载。

我说的对吗? 此外,如果 graphql 能够仅返回查询所需的数据,而不是对完整架构有效的数据,我认为这是可能的,对吧?

【问题讨论】:

  • @p0k8_ 为什么不呢?
  • @p0k8_ it's not good make own custom batching request to database, because the user may not query for the nested type。如果用户没有查询嵌套类型,则 sql 中不应该有任何连接,正如我在第二个查询中所展示的那样。要么你不明白我在问什么,要么我不明白你在说什么。
  • 这正是我写Join Monster时想要解决的问题。它仅在实际请求嵌套类型时生成 SQL 并动态添加连接。它可以任意深入并使用 postgres(很快还会使用 mariadb)进行分页。

标签: sql graphql


【解决方案1】:

是的,这绝对是可能的,并且值得鼓励。然而,它的要点是 GraphQL 在您明确解释如何获取数据之前基本上不了解您的存储层。好消息是,无论数据位于何处,您都可以使用 graphql 优化查询。

如果您使用 javascript,有一个包 graphql-fields 可以在理解查询的选择集方面简化您的生活。它看起来像这样。

如果你有这个问题

query GetCityEvents {
  getCity(id: "id-for-san-francisco") {
    id
    name
    events {
      edges {
        node {
          id
          name
          date
          sport {
            id
            name
          }
        }
      }
    }
  }
}

那么解析器可能看起来像这样

import graphqlFields from 'graphql-fields';

function getCityResolver(parent, args, context, info) {
  const selectionSet = graphqlFields(info);
  /**
    selectionSet = {
      id: {},
      name: {},
      events: {
        edges: {
          node: {
            id: {},
            name: {},
            date: {},
            sport: {
              id: {},
              name: {},
            }
          }
        }
      }
    }
  */
  // .. generate sql from selection set
  return db.query(generatedQuery);
}

还有像 join monster 这样的高级工具可能会对此有所帮助。

这是一篇博客文章,其中更详细地介绍了其中一些主题。 https://scaphold.io/community/blog/querying-relational-data-with-graphql/

【讨论】:

    【解决方案2】:

    在 Scala 实现(Sangria-grahlQL)中,您可以通过以下方式实现:

    假设这是客户端查询:

    query BookQuery { 
        Books(id:123) { 
          id 
          title 
          author {
            id
            name
          }
        }
    }
    

    这是您在 Garphql 服务器中的 QueryType。

    val BooksDataQuery = ObjectType(
        "data_query",
        "Gets books data",
        fields[Repository, Unit](
          Field("Books", ListType(BookType), arguments = bookId :: Nil, resolve = Projector(2, (context, fields) =>{ c.ctx.getBooks(c.arg(bookId), fields).map(res => res)}))
        )
    )
    val BookType = ObjectType( ....)
    val AuthorType = ObjectType( ....)
    
    Repository class:
    
    def getBooks(id: String, projectionFields: Vector[ProjectedName]) {
    /* Here you have the list of fields that client specified in the query. 
        in this cse Book's id, title and author - id, name. 
        The fields are nested, for example author has id and name. In this case author will have sequence of id and name. i.e. above query field will look like:
        Vector(ProjectedName(id,Vector()), ProjectedName(title,Vector()),ProjectedName(author,ProjectedName(id,Vector()),ProjectedName(name,Vector())))
    
        Now you can put your own logic to read and parse fields the collection and make it appropriate for query in database. */
    }
    

    所以基本上,您可以在 QueryType 的字段resolver 中截取客户端指定的字段。

    【讨论】:

    • 好的,很好!奇怪的是,graphql 网站上没有这样的例子。这是 Scala 库所做的某种 hack...
    • 是的,graphql 官方网站提供的不多,而且由于社区小,google 也帮不上什么忙。顺便说一句,Graphql link 有几个服务器端框架,上面一个是 Sangria Graphql for scala。您应该尝试检查这些框架的 github 并浏览演示项目和代码。
    猜你喜欢
    • 2020-09-17
    • 2021-04-10
    • 2014-12-06
    • 2019-08-17
    • 2018-05-28
    • 2018-05-06
    • 1970-01-01
    • 2020-02-28
    • 1970-01-01
    相关资源
    最近更新 更多