在一个请求中接受一组动态大小和常见标量类型的 GraphQL 突变

Posted

技术标签:

【中文标题】在一个请求中接受一组动态大小和常见标量类型的 GraphQL 突变【英文标题】:GraphQL mutation that accepts an array of dynamic size and common scalar types in one request 【发布时间】:2017-04-03 12:22:33 【问题描述】:

我需要能够在单个请求中创建一个用户并添加它最喜欢的电影(一组对象,其中引用了 Movies 集合和他对每部电影的个人评分)。

可能看起来像这样的东西(伪代码)

var exSchema = `
 type Mutation 
    addUser(
        name: String!
        favMovies: [ movie: String! #ref to movies coll
                      personal_rating: Int! # this is different for every movie
                   ]
    ) : User
 
...
`

在单个请求中执行此操作的 graphql 方式是什么?我知道我可以通过多个突变/请求来实现结果,但我想一次性完成。

【问题讨论】:

在具有namefavMovies 的架构上定义一个input type。让addUser() 将该类型的实例作为其参数。 AFAIK,列表字段对输入类型有效。 是的,我必须试一试,不过我一直在寻找更好的例子 javascript 中,您可以像这样转换 json 数组以满足此模式:favMovies: $JSON.stringify(moviesArray).replace(/"([^(")"]+)":/g,"$1:") 【参考方案1】:

对于那些不需要为一个请求传递数组并且愿意为每个突变发出请求的想法的人。 (我用的是Vue3,compisition Api,但是React和Angular开发者还是可以理解的)。

不能这样循环突变:

function createProject() 
    for (let i = 0; i < state.arrOfItems.length; i++) 
      const  mutate: addImplementation  = useMutation(
        post_dataToServer,
        () => (
          variables: 
            implementation_type_id: state.arrOfItems[i],
            sow_id: state.newSowId,
          ,
        )
      );

      addImplementation();
    

这会给你一个错误,因为突变必须在 setup() 中。 (这是您将收到的错误:https://github.com/vuejs/vue-apollo/issues/888)

而是创建一个子组件,并将数组映射到父组件中。

在 Parent.vue 中

<div v-for="(card, id) in state.arrOfItems">
  <ChildComponent
    :id="id"
    :card="card"
  />
</div>

在 ChildComponent.vue 中

收到道具并:

 const  mutate: addImplementation  = useMutation(
    post_dataToServer,
    () => (
      variables: 
        implementation_id: props.arrOfItems,
        id: props.id,
      ,
    )
  );

【讨论】:

【参考方案2】:

我对您的要求的理解是,如果您有以下代码

const user = 
    name:"Rohit", 
    age:27, 
    marks: [10,15], 
    subjects:[
        name:"maths",
        name:"science"
    ]
;

const query = `mutation 
        createUser(user:$user) 
            name
        
`

你一定会得到类似的东西

"mutation 
        createUser(user:[object Object]) 
            name
        
"

而不是预期的

"mutation 
        createUser(user:
            name: "Rohit" ,
            age: 27 ,
            marks: [10 ,15 ] ,
            subjects: [
                name: "maths"  ,
                name: "science"  
                ] 
            ) 
            name
        
"

如果这是您想要实现的,那么gqlast 是一个不错的标签功能,您可以使用它来获得预期的结果

只需从here 中获取 js 文件并将其用作:

const user = 
    name:"Rohit", 
    age:27, 
    marks: [10,15], 
    subjects:[
        name:"maths",
        name:"science"
    ]
;

const query = gqlast`mutation 
        createUser(user:$user) 
            name
        
`

存储在变量query 中的结果将是:

"mutation 
        createUser(user:
            name: "Rohit" ,
            age: 27 ,
            marks: [10 ,15 ] ,
            subjects: [
                name: "maths"  ,
                name: "science"  
                ] 
            ) 
            name
        
"

【讨论】:

【参考方案3】:

我想出了这个简单的解决方案 - 不使用 JSON。仅使用一个输入。希望它会帮助别人。

我必须添加到这种类型:

type Option 
    id: ID!
    status: String!
    products: [Product!]!

我们可以添加突变类型并添加输入如下:

type Mutation 
    createOption(data: [createProductInput!]!): Option!
    // other mutation definitions


input createProductInput 
    id: ID!
    name: String!
    price: Float!
    producer: ID!
    status: String

然后可以使用以下解析器:

const resolvers = 
    Mutation: 
      createOption(parent, args, ctx, info) 

        const status = args.data[0].status;

        // Below code removes 'status' from all array items not to pollute DB.
        // if you query for 'status' after adding option 'null' will be shown. 
        // But 'status': null should not be added to DB. See result of log below.
        args.data.forEach((item) => 
            delete item.status
        );

        console.log('args.data - ', args.data);

        const option = 
            id: uuidv4(),
            status: status,  // or if using babel    status,
            products: args.data
        

        options.push(option)

        return option
      ,
    // other mutation resolvers
    

现在您可以使用它来添加一个选项(STATUS 取自数组中的第一项 - 它可以为空):

mutation
  createOption(data:
    [
            id: "prodB",
            name: "componentB",
            price: 20,
            producer: "e4",
            status: "CANCELLED"
        ,
        
            id: "prodD",
            name: "componentD",
            price: 15,
            producer: "e5"
        
    ]
  ) 
    id
    status
    products
      name
      price
    
  

生产:


  "data": 
    "createOption": 
      "id": "d12ef60f-21a8-41f3-825d-5762630acdb4",
      "status": "CANCELLED",
      "products": [
        
          "name": "componentB",
          "price": 20,
        ,
        
          "name": "componentD",
          "price": 15,
        
      ]
    
  

不用说要得到以上结果,你需要添加:

type Query 
    products(query: String): [Product!]!
    // others


type Product 
    id: ID!
    name: String!
    price: Float!
    producer: Company!
    status: String

我知道这不是最好的方法,但我没有在文档中找到这样做的方法。

【讨论】:

【参考方案4】:

我最终手动解析了正确的架构,因为 JavaScript 数组和 JSON.stringify 字符串不被接受为 graphQL 架构格式。

const id = 5;
const title = 'Title test';

let formattedAttachments = '';
attachments.map(attachment => 
  formattedAttachments += ` id: $attachment.id, short_id: "$attachment.shortid" `;      
  //  id: 1, short_id: "abcxyz"  id: 2, short_id: "bcdqrs" 
);

// Query
const query = `
  mutation 
    addChallengeReply(
      challengeId: $id, 
      title: "$title", 
      attachments: [$formattedAttachments]
    ) 
      id
      title
      description
    
  
`;

【讨论】:

这拯救了我的一天。非常感谢sn-p【参考方案5】:

你可以像这样传递一个数组

var MovieSchema = `
  type Movie 
   name: String
  
  input MovieInput 
   name: String
  
  mutation 
   addMovies(movies: [MovieInput]): [Movie]
  
`

然后在你的突变中,你可以传递一个数组,如

mutation 
  addMovies(movies: [name: 'name1', name: 'name2']) 
    name
  

没有测试过代码,但你明白了

【讨论】:

这太酷了,但是当我将 movies: String 更改为 movies: [MovieInput] 时,graphql 会抛出 Error: Expected Input type. 当然在此之前我已经声明了 Type MovieInput @octohedron 非常感谢!我一直在努力解决这个问题。 @EdmondTamas 注意MovieInput 不是一个类型,而是一个input;因此,如答案所示,必须有两个定义,一个用于type,另一个用于input - 并且名称也必须彼此不同,因为不允许使用两个具有相同名称的定义。 【参考方案6】:

将它们作为 JSON 字符串传递。我就是这么做的。

【讨论】:

@Gazta 我有同样的问题,你有没有办法将 JSON 数组作为字符串传递?你是怎么逃出来的? @EdmondTamas 添加了解决方案 JSON.stringify 也解析了密钥: addMovies(movies: [ "name": "name1" ] -- 这会引发错误

以上是关于在一个请求中接受一组动态大小和常见标量类型的 GraphQL 突变的主要内容,如果未能解决你的问题,请参考以下文章

FreeMarker类型

OpenCL:如何避免重复的标量/向量函数?

PHP7:标量返回类型声明不应该接受整数吗?

数据结构与算法知识点

apache 工作过程和模式

在多个 DLL 中注册常见的 Qt 元类型