Ternary operator in Influx flux - influxdb

I am doing a join in influx to get the first and last values for a interval and then getting the difference.
Preset = 600
FirstValues = from(bucket: "Historian/oneday")
|> range(start: dashboardTime)
|> filter(fn: (r) =>
r._measurement == "TestMeasurement" and
r._field =="Value" and
r.Loc=="TXS"
)
|>window(every:15m)
|>first()
LastValues = from(bucket: "Historian/oneday")
|> range(start: dashboardTime)
|> filter(fn: (r) =>
r._measurement == "TestMeasurement" and
r._field =="Value" and
r.Loc=="TXS"
)
|>window(every:15m)
|>last()
CombinedValues = join (
tables:{first:FirstValues,last:LastValues},
on:["_stop","_start"]
)
totaliser = CombinedValues
|>map(fn: (r) => ({
_time: r._start,
//Want to do this, r._value_first < r._value_last ? Preset : r._value_first
_value: r._value_first - r._value_last
}))
totaliser
|>window(every:inf)
This works fine till the time difference returns a positive number.
But if the 1st value from join returns is less than 2nd value , I want to update that with a perset value.
Ex:
Preset = 600
totaliser = CombinedValues
|>map(fn: (r) => ({
_time: r._start,
//Want to do this, r._value_first < r._value_last ? Preset : r._value_first
_value: r._value_first - r._value_last
}))

Conditional logic is pretty new to Flux, but it does exist. Currently there is only if, else if, and else:
// Pattern
if <condition> then <action> else <alternative-action>
// Example
if color == "green" then "008000" else "ffffff"
But this will be expanded with time.
https://v2.docs.influxdata.com/v2.0/query-data/guides/conditional-logic/

Related

Sum values in InfluxDB

I’m tryingto sum values in InfluxDB but I’m struggling a bit.
So, I have a _measurement "plug" with a field "value".
I have different records within the same bucket with a different ID tag.
I can get the evolution of 1 plug with this query:
from(bucket: "test-bucket")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "plug")
|> filter(fn: (r) => r["_field"] == "value")
|> filter(fn: (r) => r["id"] == "tag1")
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|> yield(name: "mean")
What I would like is the exact same graph with the sum of all r["id"].
So, if there is 34 for tag ID "tag1", 11.2 for "tag2" and 0 for "tag3", I would like a graph with 45.2 for that given time.
I’ve tried to use «group()» method, but I get a strange value, more like an average than a sum.
I’ve also tried to use «sum» method, but then, I feel like Influx is summing all the values across the whole timeline. That’s not what I want.
I just like to have a graph with with the sum of «value» field of all "tag" at a given time.
Thanks a lot for you help.
Right now you have a table per tag value. You can use pivot function to merge into a single table, where all the different _value columns are now named after their corresponding tag value:
from(bucket: "test-bucket")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r._measurement == "plug")
|> filter(fn: (r) => r._field == "value")
|> pivot(rowKey: ["_time"], columnKey: ["id"], valueColumn: "_value")
If you know the tag values in advance, the next step is easy:
|> map(fn: (r) => ({ _time: r._time, _value: r["tag1"] + r["tag2"] + r["tag3]}))
If you don't, it gets a bit more complicated. What I would try next in this case is to write a function that combines experimental.unpivot() (note: available since InfluxDB 2.4) with sum(). The trick here is to call this method within map(), so it will operate on a single row (ie, single timestamp) at a time:
sumColumns = (r) => r
|>experimental.unpivot()
|>group
|>sum()
|>findRecord(fn: (key) => true, idx: 0)
from(bucket: "test-bucket")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r._measurement == "plug")
|> filter(fn: (r) => r._field == "value")
|> pivot(rowKey: ["_time"], columnKey: ["id"], valueColumn: "_value")
|> map(fn: sumColumns)
Note that I have not tested this. It is just to give you an idea.

What does `|>` mean in TICKscript

Trying to write my fist TICKscript to work out when two sensor values cross: if the outside temperature has changed from lower to higher than the inside temperature then I need to close the windows (and conversely).
Using the query builder in InfluxDB I'm getting this for the meadian of the temperature values inside the house over the last 15 minutes:
from(bucket: "zigbee")
|> range(start: -15m, stop: now())
|> filter(fn: (r) => r["room"] == "Kitchen" or r["room"] == "DiningRoom" or r["room"] == "Bed3" or r["room"] == "Bed1")
|> filter(fn: (r) => r["_field"] == "temperature")
|> group(columns: ["_measurement"])
|> aggregateWindow(every: 15m, fn: mean, createEmpty: false)
|> yield(name:"inside")
The syntax |> appears to undocumented -- can you provide a reference?
Replacing |> with | breaks it.
It seems that group and aggregateWindow do not commute?
Presumably because aggregateWindow is forced to choose a single representative _time value for each window?
I think the plan is to
assign this to a stream,
copy and edit to creata a second stream shifted by 15 minutes,
create a second pair of streams for the outside temperature.
join all four streams and caluclate a value indicating whether the inside and outside temperatures have crossed over.
Unless you have a better idea?
(Right now it's looking easier to import the data into SQL.)
Check InfluxDB Flux language documentation for |>:
InfluxDB Pipe-forward operator
According to your flux syntax query:
from(bucket: "zigbee")
|> range(start: -15m, stop: now())
|> filter(fn: (r) => r["room"] == "Kitchen" or r["room"] == "DiningRoom" or r["room"] == "Bed3" or r["room"] == "Bed1")
|> filter(fn: (r) => r["_field"] == "temperature")
|> group(columns: ["_measurement"])
|> aggregateWindow(every: 15m, fn: mean, createEmpty: false)
|> yield(name:"inside")
You are taking data from bucket "zigbee"
Data from source are passed to range filter function with pipe-forward |> operator
Results from range filter data are passed to next filter function with another pipe-forward operator
Etc.
So all data flows as a result from one function to another.
You can group by but in your case columns are "room" key values if I understand your intentions correctly, so try:
|> group(columns: ["room"])
There is a difference between key values and measurement names - you should check InfluxDB documentation for understatnding data structure.
Flux data model documentation
I'ts not TICKscript, it's something do to with InfluxDB that might be called flux.
mean = from(bucket: "zigbee")
|> range(start: -5d, stop: now())
|> filter(fn: (r) => r["room"] == "Outside")
|> filter(fn: (r) => r["_measurement"] == "temperature")
|> aggregateWindow(every: 30m, fn: mean, createEmpty: false)
shift = mean
|> timeShift(duration: -3h)
j = join(tables: {mean: mean, shift: shift}, on: ["_time"])
|> map(fn: (r) => ({ r with diff: float(v: r._value_mean) - float( v: r._value_shift) }))
// yield contains 1 table with the required columns, but the UI doesn't understand it.
// The UI requires 1 table for each series.
j |> map(fn: (r) => ({_time: r._time, _value: r._value_mean})) |> yield(name: "mean")
j |> map(fn: (r) => ({_time: r._time, _value: r._value_shift})) |> yield(name: "shift")
j |> map(fn: (r) => ({_time: r._time, _value: r.diff})) |> yield(name: "diff")
The |> in TickScript "Declares a chaining method call which creates an instance of a new node and chains it to the node above it." as said in the official documentation

InfluxDB 2.0 - Flux query: How to sum a column and use the sum for further calculations

I am new to flux query language (with Influx DB 2) and cant find a solution for the following problem:
I have data with changing true and false values:
I was able to calculate the time in seconds until the next change by using the events.duration function:
Now I want to calculate the total time and the time of all "false"-events and after that I want to calculate the percentage of all false events. I tryed the following
import "contrib/tomhollingworth/events"
total = from(bucket: "********")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "********")
|> filter(fn: (r) => r["Server"] == "********")
|> filter(fn: (r) => r["_field"] == "********")
|> filter(fn: (r) => r["DataNode"] == "********")
|> events.duration(
unit: 1s,
columnName: "duration",
timeColumn: "_time",
stopColumn: "_stop"
)
|> sum(column: "duration")
|> yield(name: "total")
downtime = from(bucket: "********")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "********")
|> filter(fn: (r) => r["Server"] == "********")
|> filter(fn: (r) => r["_field"] == "********")
|> filter(fn: (r) => r["DataNode"] == "********")
|> events.duration(
unit: 1s,
columnName: "duration",
timeColumn: "_time",
stopColumn: "_stop"
)
|> pivot(rowKey:["_time"], columnKey: ["_value"], valueColumn: "duration")
|> drop(columns: ["true"])
|> sum(column: "false")
|> yield(name: "downtime")
downtime_percentage = downtime.false / total.duration
With this I am getting the following error error #44:23-44:31: expected {A with false:B} but found [C]
I also tryed some variations but couldnet get it to work.
I guess I am getting some basic things wrong but I couldnt figure it out yet. Let me know, if you need more information.
I have found a way to solve my problem. Although I am sure that there is a more elegant solution, I document my way here, maybe it helps someone and we can improve it together.
import "contrib/tomhollingworth/events"
//Set time window in seconds (based on selected time)
time_window = int(v: v.timeRangeStart)/-1000000000
//Filter (IoT-)Data
data= from(bucket: "*******")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "*******")
|> filter(fn: (r) => r["Server"] == "*******")
|> filter(fn: (r) => r["Equipment"] == "*******")
|> filter(fn: (r) => r["DataNode"] == "******")
//Use events.duration to calculate the duration in seconds of each true/false event.
|> events.duration(
unit: 1s,
columnName: "duration",
timeColumn: "_time",
stopColumn: "_stop"
)
//Sum up the event times via "sum()" and save them as an array variable via "findColumn()". This is the only way to access the value later (As far as I know. please let me know if you know other ways!).
total_array = data
|> sum(column: "duration")
|> findColumn(
fn: (key) => key._field == "*******",
column: "duration",
)
//Calculate "missing time" in seconds in the time window, because the first event in the time window is missing.
missing_time = time_window - total_array[0]
//Create an array with the first event to determine if it is true or false
first_value_in_window = data
|> first()
|> findColumn(
fn: (key) => key._field == "*******",
column: "_value",
)
//Calculate the downtime by creating columns with the true and false values via pivot. Then sum up the column with the false values
downtime = data
|> map(fn: (r) => ({ r with duration_percentage: float(v: r.duration)/float(v: time_window) }))
|> pivot(rowKey:["_time"], columnKey: ["_value"], valueColumn: "duration_percentage")
|> map( fn: (r) => ({r with
downtime: if exists r.false then
r.false
else
0.0
}))
|> sum(column: "downtime")
//Create an array with the downtime so that this value can be accessed later on
downtime_array = downtime
|> findColumn(
fn: (key) => key._field == "PLS_Antrieb_laeuft",
column: "downtime",
)
//If the first value in the considered time window is true, then the remaining time in the time window (missing_time) was downtime. Write this value in the column "false_percentage_before_window".
//The total downtime is calculated from the previously calculated sum(downtime_array) and, if applicable, the downtime of the remaining time in the time window if the first value is true (first_value_in_window[0])
data
|> map( fn: (r) => ({r with
false_percentage_before_window: if first_value_in_window[0] then
float(v: missing_time)/float(v: time_window)
else
0.0
}))
|> map(fn: (r) => ({ r with _value: (downtime_array[0] + r.false_percentage_before_window) * 100.00 }))
|> first()
|> keep(columns: ["_value"])
|> yield(name: "Total Downtime")
This solution assumes that the true/false events only occur alternately.

Query last value in Flux

I'm trying to get the last value from some IoT sensors and I actually achieved an intermediary result with the following Flux query:
from(bucket:"mqtt-bucket")
|> range(start:-10m )
|> filter(fn: (r) => r["_measurement"] == "mqtt_consumer")
|> filter(fn: (r) => r["thingy"] == "things/green-1/shadow/update"
or r["thingy"] == "things/green-3/shadow/update"
or r["thingy"] == "things/green-2/shadow/update")
|> filter(fn: (r) => r["_field"] == "data")
|> filter(fn: (r) => r["appId"] == "TEMP" or r["appId"] == "HUMID")
|> toFloat()
|> last()
The problem: I would like to get the last mesured value independently of a time range.
I saw in the docs that there is no way to unbound the range function. Maybe there is a work around ?
I just found this:
from(bucket: "stockdata")
|> range(start: 0)
|> filter(fn: (r) => r["_measurement"] == "nasdaq")
|> filter(fn: (r) => r["symbol"] == "OPEC/ORB")
|> last()

How to calculate uptime in seconds using Flux?

Using the new Flux langauge, how best to calculate uptime? My current Flux query looks a bit like this:
from(bucket: "my-bucket")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "process_start_time_seconds")
|> filter(fn: (r) => r["_field"] == "gauge")
|> map(fn: (r) => ({
r with
_value: (int(v: now()) / 1000000000) - int(v: r._value)
})
)
|> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)
This works but seems to be incredibly complex for such a small thing, in Prometheus it's basically one line:
(time() - process_start_time_seconds{job="my-job"})
Is there a way I can improve the Flux query?
I don't think you can simplify it a lot, but here are some ideas:
Store the converted current time in a variable
Don't use aggregateWindow() when you only want to fetch a single value over time
Move the map() as far out as you can for better performance
Use prettier syntax
It could then look like this (just a sketch, not tested for syntax):
currentSeconds = (int(v: now()) / 1000000000)
from(bucket: "my-bucket")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r._measurement == "process_start_time_seconds")
|> filter(fn: (r) => r._field == "gauge")
|> last()
|> map(fn: (r) => ({
r with _value: currentSeconds - int(v: r._value)
})
)

Resources