QuestDB
Writes time series at volume, or queries them back. Use it for series you generate yourself — a calculated OEE, a derived rate — that deserve a trend but are not tags.
Type: questdb · Category: Datasource · Ports: one input · one output


When to use it
- Storing a calculated value you want to chart over time
- High-rate writes that would overwhelm a relational database
- Querying history back for a report
For tag history, enable history on the tag instead — the historian already does this, correctly, with rollups and retention.
Settings
| Setting | Key | Default | Purpose |
|---|---|---|---|
| Mode | mode | ingress | ingress (high-rate writes) or query. |
| Connection | connectionId | — | Which QuestDB connection. |
| Table | table | — | Ingestion target. |
| Symbols | symbols | — | Which fields are indexed labels rather than values. |
| Query | query | — | For query mode. |
| Timeout / retries | timeout, maxRetries, retryDelay | 0 | Per-write limits. |
Example — writing a derived series
OEE is calculated, not measured, so it is not a tag — but you still want to trend it.
Mode: ingress
Table: oee_by_line
Symbols: line, shift
In
{
"payload": {
"line": "LINE1",
"shift": "A",
"availability": 0.92,
"performance": 0.95,
"quality": 0.97,
"oee": 0.847,
"timestamp": 1786454700000
}
}
What you can reference
| Path | Resolves to |
|---|---|
{{payload}} | The whole payload |
{{payload.units}} | A field, at any depth — {{payload.order.line}} works |
{{topic}} | The message topic |
{{timestamp}} · {{id}} | The message timestamp and id |
{{vars.shift}} | Message metadata, also available as {{metadata.shift}} |
{{origin.id}} · {{origin.type}} | The node that produced the message |
With no upstream message the query runs exactly as typed, with nothing substituted — which is what
makes a plain SELECT work behind an Inject.
{{payload.data}} against a message with no data field is not blanked and not bound —
the token stays in the statement exactly as you typed it and is handed to the database as raw SQL:
-- you wrote
WHERE units = {{payload.units}}
WHERE data = {{payload.data}}
-- the database receives
WHERE units = ? -- bound, value 42
WHERE data = {{payload.data}} -- raw text, no argument
Which is a syntax error, and the error the database returns names the {{, not the missing field.
The rule is deliberate — a silent NULL would make a WHERE match the wrong rows and a
DELETE match too many — but it means a typo in a path fails at the database, not in the
editor. Check the path against a real message in the Debug node before you rely on it.
Note this is the opposite of what the notification and AI nodes do with the same syntax, where an unresolved token becomes empty.
Gotchas
- Mark labels as symbols, never values. A symbol is stored as a repeated label and is what you filter and group by. Making a continuously varying number a symbol builds an enormous index for nothing, and it is painful to undo once the table has data in it.