使用 Graphql 仅加载数据库中需要的数据
Load only the data that's needed from database with Graphql
我正在学习 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 能够 return 仅查询所需的数据,而不是对完整模式有效的数据,我认为这是可能的,对吗?
在 Scala 实现中(Sangria-grahlQL),您可以通过以下方式实现:
假设这是客户端查询:
query BookQuery {
Books(id:123) {
id
title
author {
id
name
}
}
}
这是您在 Garphql Server 中的 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 基本上不了解你的存储层,直到你明确解释如何获取数据。好消息是,无论数据位于何处,您都可以使用 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,可能对此有所帮助。
这是一篇博客 post,其中更详细地介绍了其中的一些主题。 https://scaphold.io/community/blog/querying-relational-data-with-graphql/
我正在学习 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 能够 return 仅查询所需的数据,而不是对完整模式有效的数据,我认为这是可能的,对吗?
在 Scala 实现中(Sangria-grahlQL),您可以通过以下方式实现:
假设这是客户端查询:
query BookQuery {
Books(id:123) {
id
title
author {
id
name
}
}
}
这是您在 Garphql Server 中的 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 基本上不了解你的存储层,直到你明确解释如何获取数据。好消息是,无论数据位于何处,您都可以使用 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,可能对此有所帮助。
这是一篇博客 post,其中更详细地介绍了其中的一些主题。 https://scaphold.io/community/blog/querying-relational-data-with-graphql/