Persisted Queries
Persisted queries are saved query definitions stored as runtime entities. Instead of specifying all query parameters every time, you create a query definition once and execute it by its rtId. This is useful for dashboard widgets, recurring reports, and any scenario where the same query needs to be executed repeatedly.
Query Types
There are four persisted query types, corresponding to the four transient query types:
| CK Type | Purpose | Key Fields |
|---|---|---|
System/StreamDataSimpleQuery | Raw time-series rows | columns (string array), sorting |
System/StreamDataAggregationQuery | Aggregated values | columns (with aggregation type) |
System/StreamDataGroupingAggregationQuery | Grouped aggregation | groupingColumns, columns (with aggregation type) |
System/StreamDataDownsamplingQuery | Time-bucketed aggregation | columns (with aggregation type), required from/to/limit |
Common Fields
All persisted query types share these configuration fields:
| Field | Type | Description |
|---|---|---|
| name | String | Display name for the query |
| description | String | Optional description |
| queryCkTypeId | String | The CK type to query (e.g., "Industry.Energy/EnergyMeter") |
| navigationFilterMode | String | Filter mode for runtime navigation |
| rtIds | [String] | Optional: scope to specific runtime entity IDs |
| from | DateTime | Optional: start of time range |
| to | DateTime | Optional: end of time range |
| limit | Int | Optional: maximum rows to return |
| fieldFilter | [FieldFilter] | Optional: field-level comparison filters |
Persisted queries use the CK-generated AggregationTypes enum with values COUNT, MINIMUM, MAXIMUM, AVERAGE, and SUM. This differs from the transient query AggregationType enum which uses the short forms AVG, MIN, MAX, COUNT, and SUM.
Creating Persisted Queries
Simple Query
Creates a saved query that retrieves raw time-series rows with column selection.
mutation {
runtime {
systemStreamDataSimpleQuerys {
create(entities: [
{
name: "Voltage readings last 24h"
description: "Raw voltage data for all energy meters"
queryCkTypeId: "Industry.Energy/EnergyMeter"
columns: ["voltage", "power"]
from: "2024-03-21T00:00:00Z"
to: "2024-03-22T00:00:00Z"
limit: 5000
sorting: [
{ attributePath: "timestamp", sortOrder: DESCENDING }
]
}
]) {
rtId
ckTypeId
name
description
queryCkTypeId
columns
from
to
limit
sorting {
attributePath
sortOrder
}
fieldFilter {
attributePath
operator
comparisonValue
}
}
}
}
}
The response includes the rtId of the newly created query — use this to execute it later.
Aggregation Query
Creates a saved aggregation query with specified aggregation functions per column.
mutation {
runtime {
systemStreamDataAggregationQuerys {
create(entities: [
{
name: "Energy meter statistics"
description: "Average, min, and max voltage"
queryCkTypeId: "Industry.Energy/EnergyMeter"
columns: [
{ attributePath: "voltage", aggregationType: AVERAGE },
{ attributePath: "voltage", aggregationType: MINIMUM },
{ attributePath: "voltage", aggregationType: MAXIMUM }
]
from: "2024-03-21T00:00:00Z"
to: "2024-03-22T00:00:00Z"
}
]) {
rtId
ckTypeId
name
queryCkTypeId
columns {
aggregationType
attributePath
}
}
}
}
}
Grouping Aggregation Query
Creates a saved query that aggregates values grouped by one or more columns.
mutation {
runtime {
systemStreamDataGroupingAggregationQuerys {
create(entities: [
{
name: "Average voltage per meter"
description: "Grouped by entity"
queryCkTypeId: "Industry.Energy/EnergyMeter"
groupingColumns: ["rtId"]
columns: [
{ attributePath: "voltage", aggregationType: AVERAGE },
{ attributePath: "power", aggregationType: MAXIMUM }
]
}
]) {
rtId
ckTypeId
name
queryCkTypeId
groupingColumns
columns {
aggregationType
attributePath
}
}
}
}
}
Downsampling Query
Creates a saved downsampling query. The from, to, and limit fields define the time range and bucket count.
mutation {
runtime {
systemStreamDataDownsamplingQuerys {
create(entities: [
{
name: "Hourly voltage trend"
description: "24h downsampled to hourly buckets"
queryCkTypeId: "Industry.Energy/EnergyMeter"
columns: [
{ attributePath: "voltage", aggregationType: AVERAGE },
{ attributePath: "voltage", aggregationType: MINIMUM },
{ attributePath: "voltage", aggregationType: MAXIMUM }
]
from: "2024-03-21T00:00:00Z"
to: "2024-03-22T00:00:00Z"
limit: 24
}
]) {
rtId
ckTypeId
name
queryCkTypeId
columns {
aggregationType
attributePath
}
from
to
limit
}
}
}
}
Executing Persisted Queries
Execute a saved query by passing its rtId. Each query type has its own execution endpoint.
Simple Query
query {
streamData {
streamDataQuery(
rtId: "67e1a2b3c4d5e6f7a8b9c0d1"
first: 100
) {
totalCount
pageInfo {
hasNextPage
endCursor
}
items {
rtId
ckTypeId
timestamp
rtWellKnownName
cells {
items {
attributePath
value
}
}
}
}
}
}
Aggregation Query
query {
streamData {
streamDataAggregationQuery(
rtId: "67e1a2b3c4d5e6f7a8b9c0d2"
) {
totalCount
items {
cells {
items {
attributePath
value
}
}
}
}
}
}
Grouping Aggregation Query
query {
streamData {
streamDataGroupingAggregationQuery(
rtId: "67e1a2b3c4d5e6f7a8b9c0d3"
) {
totalCount
items {
cells {
items {
attributePath
value
}
}
}
}
}
}
Downsampling Query
query {
streamData {
streamDataDownsamplingQuery(
rtId: "67e1a2b3c4d5e6f7a8b9c0d4"
) {
totalCount
items {
timestamp
cells {
items {
attributePath
value
}
}
}
}
}
}
Overriding Parameters at Execution Time
All persisted query execution endpoints accept an optional arg parameter of type StreamDataArguments. This allows you to override the saved time range and limit at execution time without modifying the query definition.
query {
streamData {
streamDataQuery(
rtId: "67e1a2b3c4d5e6f7a8b9c0d1"
arg: {
queryMode: DEFAULT
from: "2024-03-20T00:00:00Z"
to: "2024-03-21T00:00:00Z"
limit: 10000
}
first: 100
) {
totalCount
items {
timestamp
cells {
items {
attributePath
value
}
}
}
}
}
}
The simple query execution also supports sortOrder to override sorting at execution time:
streamDataQuery(
rtId: "67e1a2b3c4d5e6f7a8b9c0d1"
arg: { queryMode: DEFAULT }
sortOrder: [{ attributePath: "timestamp", sortOrder: ASCENDING }]
)
Retrieving Query Details
Retrieve the configuration of a saved query:
query {
runtime {
systemStreamDataSimpleQuery(rtId: "67e1a2b3c4d5e6f7a8b9c0d1") {
totalCount
items {
rtId
name
description
queryCkTypeId
columns
rtIds
from
to
limit
sorting {
attributePath
sortOrder
}
fieldFilter {
attributePath
operator
comparisonValue
}
}
}
}
}
Similarly for other types:
systemStreamDataAggregationQuery(rtId: "...")systemStreamDataGroupingAggregationQuery(rtId: "...")systemStreamDataDownsamplingQuery(rtId: "...")
Updating Persisted Queries
Update a saved query by providing its rtId and the fields to change:
mutation {
runtime {
systemStreamDataSimpleQuerys {
update(entities: [
{
rtId: "67e1a2b3c4d5e6f7a8b9c0d1"
name: "Updated voltage query"
columns: ["voltage", "power", "current"]
limit: 10000
}
]) {
rtId
name
columns
limit
}
}
}
}
The same pattern applies for the other query types using systemStreamDataAggregationQuerys, systemStreamDataGroupingAggregationQuerys, and systemStreamDataDownsamplingQuerys.
Persisted vs Transient Comparison
| Persisted | Transient | |
|---|---|---|
| Setup | Create once, execute by rtId | Specify all parameters per request |
| Reusability | Execute the same query many times | One-off queries |
| Dashboard integration | Use rtId in widget data source configuration | Not suitable for widgets |
| Parameter flexibility | Override from/to/limit at execution time via arg | Full control per request |
| Management | Stored as runtime entities, can be listed, updated, deleted | No storage |
| Use case | Production dashboards, recurring reports, shared queries | Ad-hoc exploration, one-time analysis |