使用 Graphql 仅从数据库加载所需的数据

问题描述 投票:0回答:2

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

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

type Person {
  name: String
}

和两个查询

{
  hero {
    name
    friends {
      name
    }
  }
}

还有这个

{
  hero {
    name
  }
}

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

Heros
Persons

如果我的理解是正确的,我无法解析此查询,因此对于第一个查询,生成的 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 能够仅返回查询所需的数据,而不返回对完整模式有效的数据,我认为这是可能的,对吗?

sql graphql
2个回答
4
投票

是的,这绝对是可能的并且值得鼓励。然而,其要点是,除非您明确解释如何获取数据,否则 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);
}

还有一些更高级别的工具,例如加入怪物,可能会对此有所帮助。

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


1
投票

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

假设这是客户端查询:

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

这是您在 Garphql Server 中的查询类型。

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

© www.soinside.com 2019 - 2024. All rights reserved.