GraphQL动态查询构建

GraphQL动态查询构建,graphql,apollo,apollo-client,graphql-tag,Graphql,Apollo,Apollo Client,Graphql Tag,我有一个GraphQL服务器,它能够为指定源(例如传感器数据)提供timeseries数据。获取传感器数据的查询示例可能是: query fetchData { timeseriesData(sourceId: "source1") { data { time value } } } 在我的前端,我希望允许用户选择一个或多个源,并显示一个图表,每个图表都有一条线。使用这样的查询似乎可以做到这一点: query fetchD

我有一个GraphQL服务器,它能够为指定源(例如传感器数据)提供timeseries数据。获取传感器数据的查询示例可能是:

query fetchData {
    timeseriesData(sourceId: "source1") {
      data {
        time
        value
      }
    }
}
在我的前端,我希望允许用户选择一个或多个源,并显示一个图表,每个图表都有一条线。使用这样的查询似乎可以做到这一点:

query fetchData {
    series1: timeseriesData(sourceId: "source1") {
      data {
        time
        value
      }
    }
    series2: timeseriesData(sourceId: "source2") {
      data {
        time
        value
      }
    }
}
大多数GraphQL教程似乎侧重于静态查询(例如,唯一改变的是变量,而不是请求的实际形状),但在我的例子中,我需要查询本身是动态的(每个选定ID的一次数据请求)

我有以下限制:

  • 修改服务器的模式不是一个选项(例如,我无法将ID数组传递给解析器)
  • 我的查询是使用模板字符串指定的,例如gql``
  • 我不想手动将查询构建为字符串,因为这似乎是一个灾难,意味着我将失去所有工具优势(例如自动完成、语法突出显示、linting)
  • 我使用的堆栈是:

    • 阿波罗客户机(特别是阿波罗角机)
    • 棱角的
    • 打字稿
    • graphql标记(用于定义查询)
    理想情况下,我想做的是以某种方式将两个查询合并为一个查询,这样我就可以按照第一个示例定义它们,然后在抽象层中将它们连接在一起,这样我就可以得到一个像第二个示例一样的查询,并通过网络发送

    但是,我不确定如何实现这一点,因为graphql标记正在将查询解析为AST,我很难理解是否可以以这种方式操作查询

    有什么技术可以生成这样的动态查询,而查询的形状事先是未知的?

    我想您可以使用它!但是在本例中,您仍然需要编写2个
    查询
    片段

    首先,让我们为每个
    timeSeries
    创建一个
    片段
    ,请检查您的timeSeries查询类型,我将其称为
    timeseriesDataQuery

    const series1Q = gql`
      fragment series1 on timeseriesDataQuery {
        series1: timeseriesData(sourceId: "source1") {
          data {
            time
            value
          }
        }
      }
    }
    
    const series2Q = gql`
      fragment series2 on timeseriesDataQuery {
        series2: timeseriesData(sourceId: "source2") {
          data {
            time
            value
          }
        }
      }
    }
    
    然后在查询中缝合它们:

    export const mainQuery = gql`
        query fetchData {
          ...series1 
          ...series2
        }
        ${series1Q}
        ${series2Q}
    `    
    

    您可以使用fragment定义公共字段,并在该片段上使用变量绑定的
    @include(if:Boolean)
    @skip(if:Boolean)
    指令来获取执行时已知的动态字段

    根据规范,最好避免手动字符串插值来构造动态查询

    指令可以基于作为查询变量传递的布尔表达式包含或跳过字段。指令可以附加到字段或片段包含,并且可以以服务器希望的任何方式影响查询的执行

    query Hero($episode: Episode, $withFriends: Boolean!) {
      hero(episode: $episode) {
        name
        friends @include(if: $withFriends) {
          name
        }
      }
    }
    
    在变量中:

    {
      "episode": "JEDI",
      "withFriends": false
    }
    

    当然,您可以像在第二个示例中那样发送命名查询。客户端将自动批处理请求。

    我认为当用户动态选择传感器时,即使您在开发时(不是运行时)不知道传感器,您也别无选择,只能使用字符串功能

    mainQueryStr
    是查询的字符串值(用于处理问题的动态性) 然后在传感器上循环并用每个传感器的id替换
    $sourceId

    // You have to remove the query wrapper first
    // Then replace sensor id
    const sensorsQueries = sensors.map(sid => mainQueryStr
      .split(`\n`)
      .slice(1, 7)
      .replace(`$sourceId`, sid)
    )
    
    然后您应该加入sensorQueries并创建新的GraphQL查询

    const finalQuery = gql`
      query fetchData {
        ${sensorsQueries.join(`\n`)}`
      };
    
    在这种情况下,您可以使用诸如自动完成、语法突出显示和。。。
    对于
    mainQuery
    查询,而不是
    finalQuery
    (因为您动态创建了这个)

    快速问题,如果我有多个片段,我不知道长度,我可以这样构造mainQuery吗?比如,动态地将多个片段添加到查询中?@Evilsanta,只要您在主查询中动态地定义反片段及其引用,我认为这是可能的。@Matt Wilson您有没有想出解决方案?我也有同样的情况。我从另一个服务获得了一个ID数组,对于每个ID,我需要调用一个graphql查询,并希望以1或in的形式调用它们batches@Mr.7嗨,我正在使用Apollo和Apollo客户端,发现这个“BatchHttpLink”是一个方法。我把我的解决方案放在这里:Apollo文档:在客户端,您只是创建一个到(Apollo)graphql服务器的新连接,该服务器在批量发送一个或多个请求之前等待一段时间。在过去的几个月里,它一直在处理100次用户静默,没有任何问题。hanks@user3067684我来看看:)
    const finalQuery = gql`
      query fetchData {
        ${sensorsQueries.join(`\n`)}`
      };