如何在不编写长查询的情况下查询所有 GraphQL 类型字段?

Posted

技术标签:

【中文标题】如何在不编写长查询的情况下查询所有 GraphQL 类型字段?【英文标题】:How to query all the GraphQL type fields without writing a long query? 【发布时间】:2020-02-26 01:52:37 【问题描述】:

假设您有一个 GraphQL 类型并且它包含许多字段。 如何在不写下包含所有字段名称的长查询的情况下查询所有字段?

例如,如果我有这些字段:

 public function fields()
    
        return [
            'id' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The id of the user'
            ],
            'username' => [
                'type' => Type::string(),
                'description' => 'The email of user'
            ], 
             'count' => [
                'type' => Type::int(),
                'description' => 'login count for the user'
            ]

        ];
    

查询所有字段通常是这样的:

FetchUsersusers(id:"2")id,username,count

但我想要一种无需编写所有字段即可获得相同结果的方法,如下所示:

FetchUsersusers(id:"2")*
//or
FetchUsersusers(id:"2")

有没有办法在 GraphQL 中做到这一点??

我正在使用 Folkloreatelier/laravel-graphql 库。

【问题讨论】:

你在问如何做一些 GraphQL 在设计上不支持的事情。 不支持它是有道理的,假设您有 Student 和 Class 对象,student 有字段“classes”列出他参加的所有课程,class 有字段“students”列出所有参加的学生那堂课。那是一个循环结构。现在,如果您要求所有学生的所有字段,那是否还包括返回的所有课程字段?那些班有学生,他们的领域也包括在内吗?学生们上课,... 我有这个问题,它是为了让我可以看到什至可以拉出什么。许多 GraphQL 客户端(例如 GraphiQL,请参阅 gatsbyjs.org/docs/running-queries-with-graphiql)都有一个模式浏览器,它使用内省向您展示您可以提取的内容,如果这就是想要获得“一切”的原因的话。 这里是讨论:github.com/graphql/graphql-spec/issues/127 【参考方案1】:

很遗憾,您想做的事情是不可能的。 GraphQL 要求您明确指定希望从查询中返回的字段。

【讨论】:

好的,如果我从后端请求一些未知形式的对象,我应该代理或发回? @meandre,graphql 的整体理念是没有“未知形式”这样的东西。 @meandre,我下面的回答可能对你有用吗? 有意思,用graphql真的是不一样的心态 虽然准确,但这不是一个非常有用的答案。提供有关此主题的官方文档的链接和摘录至少会有所帮助。【参考方案2】:

是的,您可以使用introspection 执行此操作。进行类似的 GraphQL 查询(对于 UserType 类型)


   __type(name:"UserType") 
      fields 
         name
         description
        
   

您会得到类似的响应(实际字段名称将取决于您的实际架构/类型定义)


  "data": 
    "__type": 
      "fields": [
        
          "name": "id",
          "description": ""
        ,
        
          "name": "username",
          "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
        ,
        
          "name": "firstName",
          "description": ""
        ,
        
          "name": "lastName",
          "description": ""
        ,
        
         "name": "email",
          "description": ""
        ,
        ( etc. etc. ...)
      ]
    
  

然后,您可以在客户端中读取此字段列表并动态构建第二个 GraphQL 查询以获取所有这些字段的值。

这取决于您知道要为其获取字段的类型的名称——如果您不知道类型,您可以使用内省将所有类型和字段放在一起


  __schema 
    types 
      name
      fields 
        name
        description
      
    
  

注意:这是无线 GraphQL 数据——您需要自己弄清楚如何使用您的实际客户端进行读写。您的 graphQL javascript 库可能已经在某些方面使用了自省,例如 apollo codegen 命令使用自省来生成类型。

【讨论】:

似乎应该表达对递归类型的关心。如果你从树上下来并碰到一个包含自身的类型,以某种形式(列表、单个或其他......),你可能会陷入无限递归。 根据我对这个特定查询的经验,这实际上并没有发生——查询本身定义了分辨率深度。 以上答案仅允许您查询查询中可用的字段类型。它不会返回所有对象字段“值”,这就是原始问题的含义。 根据答案,您必须根据第一个查询的结果动态构建第二个查询——我把它留给读者作为练习。 进行练习的用户会发现类型 UserType 有一个属性 fields,它是一个包含 name 的对象数组description 属性 - 他已经知道了,因为这正是他在之前的查询中提出的问题。【参考方案3】:

我想这样做的唯一方法是利用可重复使用的片段:

fragment UserFragment on Users 
    id
    username
    count
 

FetchUsers 
    users(id: "2") 
        ...UserFragment
    

【讨论】:

如果我这样做了,那么我仍然必须“至少在片段中”写下每个字段名称,这与我试图避免的事情相比,似乎 GraphQL 迫使我们变得明确。跨度> 如何在 POSTMan 查询中添加这个?或 jquery/UI 框架来制作字符串化的 JSON 。这个 graphiQL 似乎对实际开发目的毫无用处。 这仅供重复使用。 @BlackSigma 考虑到GraphQL documentation,这应该是公认的最佳答案 @JPVentura:不,我的朋友,可重用性和通配符在概念和应用上都有区别。文档“GraphQL 包括称为片段的可重用单元”中明确了片段的用途。使用片段很有用,但不是问题的答案。【参考方案4】:

当我需要从 google places API 加载已序列化到数据库中的位置数据时,我遇到了同样的问题。一般来说,我想要整个东西,所以它可以与地图一起使用,但我不想每次都指定所有字段。

我是用 Ruby 工作的,所以我不能给你 php 的实现,但原理应该是一样的。

我定义了一个名为 JSON 的自定义标量类型,它只返回一个文字 JSON 对象。

ruby 实现是这样的(使用 graphql-ruby)

module Graph
  module Types
    JsonType = GraphQL::ScalarType.define do
      name "JSON"
      coerce_input -> (x)  x 
      coerce_result -> (x)  x 
    end
  end
end

然后我像这样将它用于我们的对象

field :location, Types::JsonType

不过,我会非常谨慎地使用它,仅在您知道始终需要整个 JSON 对象的情况下使用它(就像我在我的情况下所做的那样)。否则更笼统地说,它正在击败 GraphQL 的对象。

【讨论】:

这正是我所需要的,谢谢。我的用例是我在整个系统中都有用户可翻译的字符串,它们以 json 格式存储在数据库中,如"en": "Hello", "es": "Hola"。由于每个用户都可以为他们的用例实现他们自己的语言子集,因此 UI 查询每个可能的子集是没有意义的。您的示例完美运行。【参考方案5】:

GraphQL query format 旨在允许:

    查询和结果形状完全相同。 服务器确切地知道所请求的字段,因此客户端只下载基本数据。

但是,根据GraphQL documentation,您可以创建fragments 以使选择集更可重用:

# Only most used selection properties

fragment UserDetails on User 
    id,
    username
 

然后您可以通过以下方式查询所有用户详细信息:

FetchUsers 
    users() 
        ...UserDetails
    

你也可以add additional fields alongside your fragment:

FetchUserById($id: ID!) 
    users(id: $id) 
        ...UserDetails
        count
    

【讨论】:

【参考方案6】:

包 graphql-type-json 支持自定义标量类型 JSON。 使用它可以显示您的 json 对象的所有字段。 这是 ApolloGraphql Server 中示例的链接。 https://www.apollographql.com/docs/apollo-server/schema/scalars-enums/#custom-scalars

【讨论】:

【参考方案7】:

哈哈,我前一天发布的框架可以解决你的问题

https://github.com/babyfish-ct/graphql-ts-client

长查询:

const employees = findEmployees(
    , 
    employee$.
    .id
    .firstName
    .lastName
    .gender
    .salary
    .subordinates(
        employee$.id
    )
)

短查询(employee$$ = employee$ + 所有标量字段):

const employees = findEmployees(
    , 
    employee$$.
    .subordinates(
        employee$.id
    )
)

【讨论】:

以上是关于如何在不编写长查询的情况下查询所有 GraphQL 类型字段?的主要内容,如果未能解决你的问题,请参考以下文章

Gatsby + Contentful - 如何在不重新启动服务器的情况下在本地重做 GraphQL 查询(npm run dev)?

GraphQL 是不是适合在不进行多次查询的情况下从后端检索多级 facet 数据?

我可以在不使用 prisma 的情况下在我的 graphQL 服务器中使用 MongoDB 吗?

在不使用 Union ALL 的情况下添加多个 select 语句

我不确定如何编写下面的查询

如何在 SQL 中编写搜索查询