Apollo GraphQL订阅响应不';不能处理嵌套查询

Apollo GraphQL订阅响应不';不能处理嵌套查询,graphql,apollo,react-apollo,graphql-subscriptions,Graphql,Apollo,React Apollo,Graphql Subscriptions,我有以下的GraphQL订阅,运行良好: subscription voucherSent($estId: Int!) { voucherSent(estId: $estId) { id name usedAt sentAt } } 但是下面发送了一个“无法读取未定义的属性‘User’”错误 Apollo GraphQL订阅是否处理嵌套查询 这是我的解析器代码: return models.Voucher.update({ sentAt: mom

我有以下的GraphQL订阅,运行良好

subscription voucherSent($estId: Int!) {
  voucherSent(estId: $estId) {
    id
    name
    usedAt
    sentAt
  }
}
但是下面发送了一个“无法读取未定义的属性‘User’”错误

Apollo GraphQL订阅是否处理嵌套查询

这是我的解析器代码:

return models.Voucher.update({
    sentAt: moment().format(),
    usedIn: args.sentTo,
  }, { where: { id: args.id } })
    .then(resp => (
      models.Voucher.findOne({ where: { id: args.id } })
        .then((voucher) => {
          pubsub.publish(VOUCHER_SENT, { voucherSent: voucher, estId: voucher.usedIn });
          return resp;
        })
    ))

Apollo Graphql订阅有关于订阅的非常简短的文档。我想我理解你的问题,我也有同样的问题。基于所有的源代码阅读和测试,我想我知道一个“不太好的解决方案”

让我先解释一下为什么你的代码不起作用。你的代码不起作用是因为用户订阅和用户做了变异不是同一个人。让我详细说明一下。 我看到了你的分解器函数,我假设分解器是某种变异分解器,在这个分解器中,你做了一个pubsub。但问题是,在该解析器中,您的Web服务器正在处理导致变异的请求。它不知道谁订阅了这个频道,他们订阅了哪些字段。因此,最好的办法是发回凭证模型的所有字段,这就是您所做的

 models.Voucher.findOne({ where: { id: args.id } })
但它不适用于订阅嵌套字段的订阅者。当广播到时,您完全可以修改代码

 models.Voucher.include("owner").findOne({ where: { id: args.id } })
 .then(voucher=>pubsub.publish(VOUCHER_SENT, { voucherSent: voucher, estId: voucher.usedIn });
这就像伪代码,但你明白了。如果您总是使用嵌套字段广播数据,那么就可以了。但它不是动态的。如果订阅服务器订阅更多嵌套字段等,您将遇到麻烦

如果您的服务器很简单,并且广播静态数据就足够了。然后你可以停在这里。下一节将详细介绍订阅的工作原理

首先,当客户机进行查询时,将向您的解析器传递4个参数。 对于订阅解析程序,前3个并不重要,但最后一个包含查询、返回类型等。此参数被调用。假设你订阅了

subscription {
  voucherSent(estId: 1) {
    id
    name
    usedAt
    sentAt
  }
}
和另一个常规查询:

query {
  getVoucher(id:1) {
    id
    name
    usedAt
    sentAt
  }
}
Info参数是相同的,因为它存储返回类型、返回字段等。根据您如何设置解析程序,如果查询包含嵌套字段,您应该有一些方法手动获取结果

现在,有两个地方需要编写代码。 1.订阅解析程序。在阿波罗的文件中,例如:

Subscription: {
  postAdded: {
    // Additional event labels can be passed to asyncIterator creation
    subscribe: () => pubsub.asyncIterator([Channel Name]),
  },
},
在这里,subscribe是一个函数,其中第四个参数(Info)对于您了解用户订阅了哪些字段至关重要。因此,如果有多个用户订阅同一张凭证,但字段不同,则需要以某种方式存储凭证,存储这些凭证非常重要。幸运的是,apollo graphql订阅已经做到了这一点

您的订阅功能应为:

Subscription{
  voucherSent(estid:ID):{
    subscribe: (p,a,c,Info)=>{
        // Return an  asyncIterator object. 
    }
  }
}
要了解为什么它必须是asyncIterator对象,请查看文档。因此它有一个很好的助手,函数,它将过滤发布的对象。此函数接受一个函数作为其第二个参数,该函数用于根据订户决定是否广播此对象。这个函数,在本例中,只有2个参数,但在中,它实际上有4个参数,第四个是信息,这是您需要的

你可能还注意到阿波罗的订阅也有一个功能。这意味着,在向客户端广播有效负载后,您可以在该函数中修改有效负载

Subscription{
  voucherSent:{
    resolve: (payload, args, context, info)=>{
       // Here the info should tell you that the user also subscribed to  owner field
       // Use payload.value, or id, do model.voucher.include(owner) to construct the nested fields
       // return new payload. 
    },
    subscribe: (p,a,c,Info)=>{
        // Return an  asyncIterator object. 
    }
  }
}
在此设置中,您的订阅至少应该可以工作,但可能无法优化。因为只要有广播,服务器就可以为每个订阅者进行数据库查询。为每个asyncIterator.next调用此解析器。优化它的方法是,您不能依赖asyncIterator并修改每个订阅者的有效负载,您需要首先遍历所有订阅者,了解他们订阅的所有字段的并集。例如,如果用户1

subscribe{voucherSent(id:1){id, name}}
和用户2

subscribe{ voucherSent(id:1){name, sentAt, owner{id,name}}}
您将需要将它们放在一起,并且知道您将需要访问数据库一次。 假装你在询问

getVoucher(id:1){
  id
  name
  sentAt
  owner{
    id
    name
  }
}
然后发回这个联合载荷。这将要求您手动将所有这些订阅者存储在存储中,并在onConnect、onDisconnect中处理它们。还要了解如何组合这些查询


希望这有帮助,让我知道

阿波罗Graphql订阅有关于订阅的非常简短的文档。我想我理解你的问题,我也有同样的问题。基于所有的源代码阅读和测试,我想我知道一个“不太好的解决方案”

让我先解释一下为什么你的代码不起作用。你的代码不起作用是因为用户订阅和用户做了变异不是同一个人。让我详细说明一下。 我看到了你的分解器函数,我假设分解器是某种变异分解器,在这个分解器中,你做了一个pubsub。但问题是,在该解析器中,您的Web服务器正在处理导致变异的请求。它不知道谁订阅了这个频道,他们订阅了哪些字段。因此,最好的办法是发回凭证模型的所有字段,这就是您所做的

 models.Voucher.findOne({ where: { id: args.id } })
但它不适用于订阅嵌套字段的订阅者。当广播到时,您完全可以修改代码

 models.Voucher.include("owner").findOne({ where: { id: args.id } })
 .then(voucher=>pubsub.publish(VOUCHER_SENT, { voucherSent: voucher, estId: voucher.usedIn });
这就像伪代码,但你明白了。如果您总是使用嵌套字段广播数据,那么就可以了。但它不是动态的。如果订阅服务器订阅更多嵌套字段等,您将遇到麻烦

如果您的服务器很简单,并且广播静态数据就足够了。然后你可以停在这里。下一节将详细介绍订阅的工作原理

首先,当客户机进行查询时,将向您的解析器传递4个参数。 对于订阅解析程序,前3个并不重要,但最后一个包含查询、返回类型等。此参数被调用。假设你订阅了

subscription {
  voucherSent(estId: 1) {
    id
    name
    usedAt
    sentAt
  }
}
还有一个普通的