GraphQL动态查询构建

时间:2018-07-13 09:48:15

标签: graphql apollo apollo-client graphql-tag

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

query fetchData {
    timeseriesData(sourceId: "source1") {
      data {
        time
        value
      }
    }
}

在我的前端,我希望允许用户选择1个或多个源,并为每个源显示一个带有线的图表。似乎可以通过使用如下查询来实现:

query fetchData {
    series1: timeseriesData(sourceId: "source1") {
      data {
        time
        value
      }
    }
    series2: timeseriesData(sourceId: "source2") {
      data {
        time
        value
      }
    }
}

大多数GraphQL教程似乎都专注于静态查询(例如,唯一变化的是变量,而不是请求的实际形状)-但在我的情况下,我需要查询本身是动态的(对我选择的每个ID都有一个timeseriesData请求)。

我有以下限制条件:

  1. 修改服务器的架构不是一种选择(例如,我无法将ID数组传递给解析器)
  2. 我的查询是使用模板字符串指定的,例如gql` ...`
  3. 我不想手动以字符串形式建立查询,因为这看起来像是灾难的秘诀,并且意味着我会失去所有工具优势(例如,自动完成,语法突出显示,掉毛)

我正在使用的堆栈是:

  • Apollo客户程序(特别是Apollo角形)
  • 角度
  • TypeScript
  • graphql-tag(用于定义查询)

理想情况下,我想做的是通过某种方式将两个查询合并为一个,这样我就可以按照第一个示例定义它们,然后将它们合并到一个抽象层中,这样我就可以得到一个查询通过电线发送的第二个示例。

但是我不确定如何实现此目标,因为graphql-tag正在将查询解析为AST,并且我正努力了解以这种方式操作查询是否可行。

有什么技术可以生成这样的动态查询,而该查询的 shape 并不是预先知道的?

3 个答案:

答案 0 :(得分:2)

我认为,除了当用户动态选择传感器甚至在开发时(而不是运行时)不知道传感器时使用字符串功能之外,您别无选择。

const mainQuery = gql
  `query fetchData($sourceId: String!) {
    timeseriesData(sourceId: $sourceId) {
      data {
        time
        value
      }
    }
  }`;

const mainQueryStr = mainQuery.loc.source.body;

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(因为您是动态创建的)

答案 1 :(得分:1)

我认为您可以为此使用fragments!但是在这种情况下,"queries"仍然需要写2 fragments

首先让我们为每个fragment创建一个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}
`    

答案 2 :(得分:1)

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