问题描述
i有一个GraphQl Server,该服务器能够为指定源提供时间表数据(例如,传感器数据).获取传感器数据的示例查询可能是:
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的一个limeseriesdata请求).
我有以下约束:
- 修改服务器架构不是一个选项(因此,我不能将ID数组传递给解析器)
- 使用模板字符串,例如,我的查询是指gql` ...
- 我不想手动构建查询作为字符串,因为这似乎是灾难的秘诀,这意味着我失去了所有工具好处(例如,自动完成,语法突出显示,覆盖片) )
我正在使用的堆栈是:
- 阿波罗客户端(特别是阿波罗 - 角)
- Angular
- 打字稿
- graphQl-tag(用于定义查询)
理想情况下,我想做的是将两个查询合并为一个,以便我可以按照第一个示例来定义它们,然后将它们与抽象层一起加入,以便我得到一个像The一样的查询第二个示例要通过电线发送.
但是,我不确定如何实现这一目标,因为GraphQl-tag将查询解析为AST,我很难理解以这种方式操纵查询是否可行.
有哪些技术来生成这样的动态查询,其中 shape 尚不清楚?
推荐答案
graqhql提供了有关此目的的指令.
创建一个片段来定义公共字段,在该片段上使用@include(if: Boolean)和@skip(if: Boolean)指令以获取动态字段.通过动态字段,我们的意思是在执行时间已知的字段.
根据规格,最好避免手动字符串插值来构建动态查询.
指令 1 使包括或跳过基于布尔表达式的字段作为查询变量传递.可以将指令附加到字段或片段包含上,并且可以以任何方式影响服务器所需的查询执行.
query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name friends @include(if: $withFriends) { name } } }
和变量:
{ "episode": "JEDI", "withFriends": false }
当然,您可以像第二个示例一样发送命名查询.客户将自动批处理要求.
其他推荐答案
我认为您别无选择,除了使用字符串功能时,当用户动态选择传感器时,即使您在开发时间(不运行时)也不知道传感器.
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(因为您是动态创建这个)
其他推荐答案
我想您可以使用 fragments 为此!但是您仍然必须写2 "queries"在这种情况下fragments.
首先,让我们为每个timeSeries创建一个fragment,请检查您的时间表查询类型,我将其称为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} `
问题描述
I have a GraphQL server which is able to serve timeseries data for a specified source (for example, sensor data). An example query to fetch the data for a sensor might be:
query fetchData { timeseriesData(sourceId: "source1") { data { time value } } }
In my frontend, I want to allow the user to select 1 or more sources and show a chart with a line for each one. It seems like this would be possible by using a query like this:
query fetchData { series1: timeseriesData(sourceId: "source1") { data { time value } } series2: timeseriesData(sourceId: "source2") { data { time value } } }
Most GraphQL tutorials seem to focus on static queries (e.g. where the only thing that is changing is the variables, but not the actual shape of the request) - but in my case I need the query itself to be dynamic (one timeseriesData request for each of my selected ids).
I have the following constraints:
- Modifying the server's schema is not an option (so I can't pass an array of IDs to the resolver, for example)
- My query is specified using a template string e.g. gql`...`
- I don't want to have to manually build up the query as a string, because that seems like a recipe for disaster and would mean that I lose all tooling benefits (e.g. autocomplete, syntax highlighting, linting)
The stack I'm using is:
- Apollo client (specifically apollo-angular)
- Angular
- TypeScript
- graphql-tag (for defining queries)
Ideally, what I want to do is have some way of merging two queries into one, so that I can define them as per the first example but then join them together in an abstraction layer so that I get a single query like the second example to be sent over the wire.
However I'm not sure how to achieve this because graphql-tag is parsing the query into an AST and I'm struggling to understand whether it's feasable to manipulate the query in this way.
What techniques are there for generating a dynamic query like this, where the shape of the query is not known upfront?
推荐答案
GraqhQL provides directives for this very purpose.
Create a fragment to define common fields, use @include(if: Boolean) and @skip(if: Boolean) directives on that fragment to get dynamic fields. By dynamic fields we mean fields that are known at execution time.
According to spec, it is best to avoid manual string interpolation to construct dynamic queries.
Directives1 make it possible to include or skip a field based on a boolean expression passed as a query variable. A directive can be attached to a field or fragment inclusion, and can affect execution of the query in any way the server desires.
query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name friends @include(if: $withFriends) { name } } }
And in variables:
{ "episode": "JEDI", "withFriends": false }
Of course you can send named queries as you did in your second example. Clients will batch the requests automatically.
其他推荐答案
I think you have no choice except using String features when the user chooses sensors dynamically and even you don't know sensors on development time (not run-time).
const mainQuery = gql `query fetchData($sourceId: String!) { timeseriesData(sourceId: $sourceId) { data { time value } } }`; const mainQueryStr = mainQuery.loc.source.body;
The mainQueryStr is the string value of your query (to handle dynamicity of your problem) Then loop on the sensors and replace $sourceId with the id of each sensor
// 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) )
Then you should join sensorQueries and make new GraphQL query
const finalQuery = gql` query fetchData { ${sensorsQueries.join(`\n`)}` };
In this case, you can use tooling benefits like autocomplete, syntax highlighting and ... for the mainQuery query, not finalQuery (Because of you create this one dynamically)
其他推荐答案
I think you could use fragments for this! But you still have to write 2 "queries" in this case fragments.
First let's create a fragment for each timeSeries, please check your timeSeries query type, I'm going to refer to it as 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 } } } }
And then just stitch them up in the query:
export const mainQuery = gql` query fetchData { ...series1 ...series2 } ${series1Q} ${series2Q} `