From f3fb06841965ae7764de811607c1100feb644c5b Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 21 Jan 2019 08:57:35 -0700 Subject: [PATCH 01/14] added structure for reference navigation --- CONTRIBUTING.md | 23 +++++++ assets/js/content-interactions.js | 6 +- assets/styles/layouts/_layout-sidebar.scss | 11 +++ content/v2.0/reference/cli/_index.md | 11 +++ layouts/partials/sidebar.html | 79 ++++++++++++++++++++++ 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 content/v2.0/reference/cli/_index.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e99320ae..b3f3243f0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -240,3 +240,26 @@ to seeing the full content block. Truncated markdown content here. {{% /truncate %}} ``` + +### Reference content +The InfluxDB documentation is "task-based," meaning content primarily focuses on +what a user is **doing**, not what they are **using**. +However, there is a need to document tools and other things that don't necessarily +fit in the task-based style. +This is referred to as "reference content." + +Reference content is styled just as the rest of the InfluxDB documentation. +The only difference is the `menu` reference in the page's frontmatter. +When defining the menu for reference content, use the following pattern: + +```yaml +# Pattern +menu: + v__ref: + # ... + +# Example +menu: + v2_0_ref: + # ... +``` diff --git a/assets/js/content-interactions.js b/assets/js/content-interactions.js index e10316967..e6b312ac8 100644 --- a/assets/js/content-interactions.js +++ b/assets/js/content-interactions.js @@ -1,6 +1,10 @@ ///////////////////////////// Make headers linkable ///////////////////////////// -$("h2,h3,h4,h5,h6").each(function() { +$(".article--content h2, \ + .article--content h3, \ + .article--content h4, \ + .article--content h5, \ + .article--content h6" ).each(function() { var link = "" $(this).wrapInner( link ); }) diff --git a/assets/styles/layouts/_layout-sidebar.scss b/assets/styles/layouts/_layout-sidebar.scss index 087c4916d..d7a426f5b 100644 --- a/assets/styles/layouts/_layout-sidebar.scss +++ b/assets/styles/layouts/_layout-sidebar.scss @@ -207,6 +207,17 @@ &:after { transform: rotate(180deg); } } } + + // Reference title styles + h4 { + margin: 2rem 0 0 -1rem; + color: rgba($article-heading, .8); + font-style: italic; + font-weight: 700; + text-transform: uppercase; + font-size: .85rem; + letter-spacing: .08rem; + } } } diff --git a/content/v2.0/reference/cli/_index.md b/content/v2.0/reference/cli/_index.md new file mode 100644 index 000000000..dde15e9e7 --- /dev/null +++ b/content/v2.0/reference/cli/_index.md @@ -0,0 +1,11 @@ +--- +title: Command line tools +seotitle: Command line tools for managing InfluxDB +description: InfluxDB comes with command line tools meant to aid in managing and working with InfluxDB. +menu: + v2_0_ref: + name: Command line tools + weight: 1 +--- + +_Placeholder for command line content._ diff --git a/layouts/partials/sidebar.html b/layouts/partials/sidebar.html index e35a13214..1c4fb838d 100644 --- a/layouts/partials/sidebar.html +++ b/layouts/partials/sidebar.html @@ -97,5 +97,84 @@ {{ end }} {{end}} + + + {{ $refMenuID := print (replaceRE "[.]" "_" $currentVersion) "_ref" }} +

Reference

+ + {{ range (index .Site.Menus $refMenuID) }} + + + {{end}} + + From ef8f8674253ba8d0e4d735ca40d44dd893874bc2 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 21 Jan 2019 17:05:24 -0700 Subject: [PATCH 02/14] added flux getting started docs --- content/v2.0/query-data/_index.md | 11 + content/v2.0/query-data/flux/_index.md | 37 +++ .../query-data/flux/get-started/_index.md | 87 +++++++ .../flux/get-started/query-influxdb.md | 128 +++++++++++ .../flux/get-started/syntax-basics.md | 217 ++++++++++++++++++ .../flux/get-started/transform-data.md | 176 ++++++++++++++ .../flux-windowed-aggregates-ungrouped.png | Bin 0 -> 27645 bytes static/img/flux-windowed-aggregates.png | Bin 0 -> 15688 bytes static/img/flux-windowed-data.png | Bin 0 -> 103752 bytes 9 files changed, 656 insertions(+) create mode 100644 content/v2.0/query-data/_index.md create mode 100644 content/v2.0/query-data/flux/_index.md create mode 100644 content/v2.0/query-data/flux/get-started/_index.md create mode 100644 content/v2.0/query-data/flux/get-started/query-influxdb.md create mode 100644 content/v2.0/query-data/flux/get-started/syntax-basics.md create mode 100644 content/v2.0/query-data/flux/get-started/transform-data.md create mode 100644 static/img/flux-windowed-aggregates-ungrouped.png create mode 100644 static/img/flux-windowed-aggregates.png create mode 100644 static/img/flux-windowed-data.png diff --git a/content/v2.0/query-data/_index.md b/content/v2.0/query-data/_index.md new file mode 100644 index 000000000..6a0c716dd --- /dev/null +++ b/content/v2.0/query-data/_index.md @@ -0,0 +1,11 @@ +--- +title: Query data in InfluxDB +seotitle: Query data stored in InfluxDB +description: placeholder +menu: + v2_0: + name: Query data + weight: 2 +--- + +_Placeholder content for the query data page._ diff --git a/content/v2.0/query-data/flux/_index.md b/content/v2.0/query-data/flux/_index.md new file mode 100644 index 000000000..ad0ba78f0 --- /dev/null +++ b/content/v2.0/query-data/flux/_index.md @@ -0,0 +1,37 @@ +--- +title: Introduction to Flux +description: > + Flux is InfluxData's functional data scripting language designed for querying, + analyzing, and acting on data. +menu: + v2_0: + parent: Query data + name: Flux + weight: 1 +--- + +Flux is InfluxData's functional data scripting language designed for querying, analyzing, and acting on data. + +## Flux design principles +Flux is designed to be usable, readable, flexible, composable, testable, contributable, and shareable. +Its syntax is largely inspired by [2018's most popular scripting language](https://insights.stackoverflow.com/survey/2018#technology), +Javascript, and takes a functional approach to data exploration and processing. + +The following example illustrates querying data stored from the last five minutes, +filtering by the `cpu` measurement and the `cpu=cpu-usage` tag, windowing the data in 1 minute intervals, +and calculating the average of each window: + +```js +from(bucket:"example-bucket") + |> range(start:-1h) + |> filter(fn:(r) => + r._measurement == "cpu" and + r.cpu == "cpu-total" + ) + |> aggregateWindow(every: 1m, fn: mean) +``` + +## Get started with Flux +The best way to familiarize yourself with Flux is to walk through creating a simple Flux query. + +[Get Started with Flux](/v2.0/query-data/flux/get-started) diff --git a/content/v2.0/query-data/flux/get-started/_index.md b/content/v2.0/query-data/flux/get-started/_index.md new file mode 100644 index 000000000..b292174e0 --- /dev/null +++ b/content/v2.0/query-data/flux/get-started/_index.md @@ -0,0 +1,87 @@ +--- +title: Get started with Flux +description: > + Get started with Flux, InfluxData's functional data scripting language. + This step-by-step guide through the basics of writing a Flux query. +menu: + v2_0: + name: Get started + identifier: get-started + parent: Flux + weight: 2 +--- + +Flux is InfluxData's new functional data scripting language designed for querying, +analyzing, and acting on data. + +This multi-part getting started guide walks through important concepts related to Flux, +how to query time series data from InfluxDB using Flux, and introduces Flux syntax and functions. + +## Key concepts +Flux introduces important new concepts you should understand as you get started. + +### Pipe-forward operator +Flux uses pipe-forward operators (`|>`) extensively to chain operations together. +After each function or operation, Flux returns a table or collection of tables containing data. +The pipe-forward operator pipes those tables into the next function or operation where +they are further processed or manipulated. + +### Tables +Flux structures all data in tables. +When data is streamed from data sources, Flux formats it as annotated +comma-separated values (CSV), representing tables. +Functions then manipulate or process them and output new tables. + +#### Group keys +Every table has a **group key** which describes the contents of the table. +It's a list of columns for which every row in the table will have the same value. +Columns with unique values in each row are **not** part of the group key. + +As functions process and transform data, each modifies the group keys of output tables. +Understanding how tables and group keys are modified by functions is key to properly +shaping your data for the desired output. + +###### Example group key +```js +[_start, _stop, _field, _measurement, host] +``` + +Note that `_time` and `_value` are excluded from the example group key because they +are unique to each row. + +## Tools for working with Flux + +You have multiple [options for writing and running Flux queries](/flux/v0.12/guides/executing-queries), +but as you're getting started, we recommend using the following: + +### 1. Data Explorer +The InfluxDB user interface's (UI) Data Explorer makes it easy to build or write +your first Flux script and visualize the results. + +![Flux in the Data Explorer](/img/flux-data-explorer,png) + +The Data Explorer provides multiple ways to create Flux queries. +Toggle between the two with the button to the left of **Submit** in the Data Explorer. + +![Flux Query Builder and Script Editor Toggle](/img/flux-ui-toggle.png) + +#### Query Builder _(default)_ +The Query Builder is a visual tool for building Flux Queries. +Select the organization and bucket from which you would like to query data. +Filter data by any columns available in the data. +Transform you data using using aggregate functions. + +#### Script Editor +The Script Editor is an in-browser code editor where you can write raw Flux scripts. + +### 2. influx CLI +The [`influx repl` command](/v2.0/reference/cli/influx/repl) opens an interactive +read-eval-print-loop (REPL) for querying data within an organization in InfluxDB with Flux. + +```bash +influx repl --org org-name +``` + + diff --git a/content/v2.0/query-data/flux/get-started/query-influxdb.md b/content/v2.0/query-data/flux/get-started/query-influxdb.md new file mode 100644 index 000000000..b02e515b6 --- /dev/null +++ b/content/v2.0/query-data/flux/get-started/query-influxdb.md @@ -0,0 +1,128 @@ +--- +title: Query InfluxDB with Flux +description: Learn the basics of using Flux to query data from InfluxDB. +menu: + v2_0: + name: Query InfluxDB + parent: get-started + weight: 1 +--- + +This guide walks through the basics of using Flux to query data from InfluxDB. +Every Flux query needs the following: + +1. [A data source](#1-define-your-data-source) +2. [A time range](#2-specify-a-time-range) +3. [Data filters](#3-filter-your-data) + + +## 1. Define your data source +Flux's [`from()`](#) function defines an InfluxDB data source. +It requires a [`bucket`](#) parameter. +The following examples use `example-bucket` as the bucket name. + +```js +from(bucket:"example-bucket") +``` + +## 2. Specify a time range +Flux requires a time range when querying time series data. +"Unbounded" queries are very resource-intensive and as a protective measure, +Flux will not query the database without a specified range. + +Use the pipe-forward operator (`|>`) to pipe data from your data source into the [`range()`](/flux/v0.12/functions/transformations/range) +function, which specifies a time range for your query. +It accepts two properties: `start` and `stop`. +Ranges can be **relative** using negative [durations](/flux/v0.12/language/lexical-elements#duration-literals) +or **absolute** using [timestamps](/flux/v0.12/language/lexical-elements#date-and-time-literals). + +###### Example relative time ranges +```js +// Relative time range with start only. Stop defaults to now. +from(bucket:"example-bucket") + |> range(start: -1h) + +// Relative time range with start and stop +from(bucket:"example-bucket") + |> range(start: -1h, stop: -10m) +``` + +> Relative ranges are relative to "now." + +###### Example absolute time range +```js +from(bucket:"example-bucket") + |> range(start: 2018-11-05T23:30:00Z, stop: 2018-11-06T00:00:00Z) +``` + +#### Use the following: +For this guide, use the relative time range, `-15m`, to limit query results to data from the last 15 minutes: + +```js +from(bucket:"example-bucket") + |> range(start: -15m) +``` + +## 3. Filter your data +Pass your ranged data into the `filter()` function to narrow results based on data attributes or columns. +The `filter()` function has one parameter, `fn`, which expects an anonymous function +with logic that filters data based on columns or attributes. + +Flux's anonymous function syntax is similar to Javascript's. +Records or rows are passed into the `filter()` function as an object (`r`). +The anonymous function takes the object and evaluates it to see if it matches the defined filters. +Use the `and` relational operator to chain multiple filters. + +```js +// Pattern +(r) => (r.objectProperty comparisonOperator comparisonExpression) + +// Example with single filter +(r) => (r._measurement == "cpu") + +// Example with multiple filters +(r) => (r._measurement == "cpu") and (r._field != "usage_system" ) +``` + +#### Use the following: +For this example, filter by the `cpu` measurement, the `usage_system` field, and the `cpu-total` tag value: + +```js +from(bucket:"example-bucket") + |> range(start: -15m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) +``` + +## 4. Yield your queried data +Use Flux's `yield()` function to output the filtered tables as the result of the query. + +```js +from(bucket:"example-bucket") + |> range(start: -15m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) + |> yield() +``` + +{{% note %}} +Flux automatically assume a `yield()` function at +the end of each script in order to output and visualize the data. +`yield()` is only necessary when including multiple queries in the same Flux query. +Each set of returned data needs to be named using the `yield()` function. +{{% /note %}} + +## Congratulations! +You have now queried data from InfluxDB using Flux. +This is a barebones query that can be transformed in other ways. + + diff --git a/content/v2.0/query-data/flux/get-started/syntax-basics.md b/content/v2.0/query-data/flux/get-started/syntax-basics.md new file mode 100644 index 000000000..0fd91cfcc --- /dev/null +++ b/content/v2.0/query-data/flux/get-started/syntax-basics.md @@ -0,0 +1,217 @@ +--- +title: Flux syntax basics +description: An introduction to the basic elements of the Flux syntax with real-world application examples. +menu: + v2_0: + name: Syntax basics + parent: get-started + weight: 3 +--- + + +Flux, at its core, is a scripting language designed specifically for working with data. +This guide walks through a handful of simple expressions and how they are handled in Flux. + +## Use the influx CLI's REPL +Use the `influx repl` command to open the interactive read-eval-print-loop (REPL). +Run the commands provided in this guide in the REPL. + +##### Start in the influx CLI in Flux mode +```bash +influx repl --org org-name +``` + +## Basic Flux syntax +The code blocks below provide commands that illustrate the basic syntax of Flux. +Run these commands in the REPL. + +### Simple expressions +Flux is a scripting language that supports basic expressions. +For example, simple addition: + +```js +> 1 + 1 +2 +``` + +### Variables +Assign an expression to a variable using the assignment operator, `=`. + +```js +> s = "this is a string" +> i = 1 // an integer +> f = 2.0 // a floating point number +``` + +Type the name of a variable to print its value: + +```js +> s +this is a string +> i +1 +> f +2 +``` + +### Objects +Flux also supports objects. Each value in an object can be a different data type. + +```js +> o = {name:"Jim", age: 42} +``` + +Use dot notation to access a properties of an object: + +```js +> o.name +Jim +> o.age +42 +``` + +### Lists +Flux supports lists. List values must be the same type. + +```js +> n = 4 +> l = [1,2,3,n] +> l +[1, 2, 3, 4] +``` + +### Functions +Flux uses functions for most of its heavy lifting. +Below is a simple function that squares a number, `n`. + +```js +> square = (n) => n * n +> square(n:3) +9 +``` + +{{% note %}} +Flux does not support positional arguments or parameters. +Parameters must always be named when calling a function. +{{% /note %}} + +### Pipe-forward operator +Flux uses the pipe-forward operator (`|>`) extensively to chain operations together. +After each function or operation, Flux returns a table or collection of tables containing data. +The pipe-forward operator pipes those tables into the next function where they are further processed or manipulated. + +```js +data |> someFunction() |> anotherFunction() +``` + +## Real-world application of basic syntax +This likely seems familiar if you've already been through through the other +[getting started guides](/v2.0/query-data/flux/get-started). +Flux's syntax is inspired by Javascript and other functional scripting languages. +As you begin to apply these basic principles in real-world use cases such as creating data stream variables, +custom functions, etc., the power of Flux and its ability to query and process data will become apparent. + +The examples below provide both multi-line and single-line versions of each input command. +Carriage returns in Flux aren't necessary, but do help with readability. +Both single- and multi-line commands can be copied and pasted into the `influx` CLI running in Flux mode. + +### Define data stream variables +A common use case for variable assignments in Flux is creating variables for one +or more input data streams. + +{{< code-tabs-wrapper >}} + {{% code-tabs %}} + [Multi-line](#) + [Single-line](#) + {{% /code-tabs %}} +{{% code-tab-content %}} +```js +timeRange = -1h + +cpuUsageUser = + from(bucket:"example-bucket") + |> range(start: timeRange) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_user" and + r.cpu == "cpu-total" + ) + +memUsagePercent = + from(bucket:"example-bucket") + |> range(start: timeRange) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) +``` +{{% /code-tab-content %}} + +{{% code-tab-content %}} +```js +timeRange = -1h +cpuUsageUser = from(bucket:"example-bucket") |> range(start: timeRange) |> filter(fn: (r) => r._measurement == "cpu" and r._field == "usage_user" and r.cpu == "cpu-total") +memUsagePercent = from(bucket:"example-bucket") |> range(start: timeRange) |> filter(fn: (r) => r._measurement == "mem" and r._field == "used_percent") +``` +{{% /code-tab-content %}} +{{< /code-tabs-wrapper>}} + +These variables can be used in other functions, such as `join()`, while keeping the syntax minimal and flexible. + +### Define custom functions +Create a function that returns the `N` number rows in the input stream with the highest `_value`s. +To do this, pass the input stream (`tables`) and the number of results to return (`n`) into a custom function. +Then using Flux's `sort()` and `limit()` functions to find the top `n` results in the data set. + +{{< code-tabs-wrapper >}} + {{% code-tabs %}} + [Multi-line](#) + [Single-line](#) + {{% /code-tabs %}} +{{% code-tab-content %}} +```js +topN = (tables=<-, n) => + tables + |> sort(desc: true) + |> limit(n: n) +``` +{{% /code-tab-content %}} +{{% code-tab-content %}} +```js +topN = (tables=<-, n) => tables |> sort(desc: true) |> limit(n: n) +``` +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} + +_More information about creating custom functions is available in the [Custom functions](/flux/v0.12/functions/custom-functions) documentation._ + +Using the `cpuUsageUser` data stream variable defined above, find the top five data +points with the custom `topN` function and yield the results. + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Multi-line](#) +[Single-line](#) +{{% /code-tabs %}} + +{{% code-tab-content %}} +```js +cpuUsageUser + |> topN(n:5) + |> yield() +``` +{{% /code-tab-content %}} + +{{% code-tab-content %}} +```js +cpuUsageUser |> topN(n:5) |> yield() +``` +{{% /code-tab-content %}} + +{{< /code-tabs-wrapper>}} + +This query will return the five data points with the highest user CPU usage over the last hour. + + diff --git a/content/v2.0/query-data/flux/get-started/transform-data.md b/content/v2.0/query-data/flux/get-started/transform-data.md new file mode 100644 index 000000000..68111916f --- /dev/null +++ b/content/v2.0/query-data/flux/get-started/transform-data.md @@ -0,0 +1,176 @@ +--- +title: Transform data with Flux +description: Learn the basics of using Flux to transform data queried from InfluxDB. +menu: + v2_0: + name: Transform your data + parent: get-started + weight: 2 +--- + +When [querying data from InfluxDB](/v2.0/query-data/flux/get-started/query-influxdb), +you often need to transform that data in some way. +Common examples are aggregating data into averages, downsampling data, etc. + +This guide demonstrates using [Flux functions](/flux/v0.12/functions) to transform your data. +It walks through creating a Flux script that partitions data into windows of time, +averages the `_value`s in each window, and outputs the averages as a new table. + +It's important to understand how the "shape" of your data changes through each of these operations. + +## Query data +Use the query built in the previous [Query data from InfluxDB](/v2.0/query-data/flux/get-started/query-influxdb) +guide, but update the range to pull data from the last hour: + +```js +from(bucket:"example-bucket") + |> range(start: -1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) +``` + +## Flux functions +Flux provides a number of functions that perform specific operations, transformations, and tasks. +You can also [create custom functions](/flux/v0.12/functions/custom-functions) in your Flux queries. +_Functions are covered in detail in the [Flux functions](/flux/v0.12/functions) documentation._ + +A common type of function used when transforming data queried from InfluxDB is an aggregate function. +Aggregate functions take a set of `_value`s in a table, aggregate them, and transform +them into a new value. + +This example uses the [`mean()` function](/flux/v0.12/functions/transformations/aggregates/mean) +to average values within each time window. + +{{% note %}} +The following example walks through the steps required to window and aggregate data, +but there is a [`aggregateWindow()` helper function](#helper-functions) that does it for you. +It's just good to understand the steps in the process. +{{% /note %}} + +## Window your data +Flux's [`window()` function](/flux/v0.12/functions/transformations/window) partitions records based on a time value. +Use the `every` parameter to define a duration of each window. + +For this example, window data in five minute intervals (`5m`). + +```js +from(bucket:"example-bucket") + |> range(start: -1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) + |> window(every: 5m) +``` + +As data is gathered into windows of time, each window is output as its own table. +When visualized, each table is assigned a unique color. + +![Windowed data tables](/img/flux-windowed-data.png) + +## Aggregate windowed data +Flux aggregate functions take the `_value`s in each table and aggregate them in some way. +Use the [`mean()` function](/flux/v0.12/functions/transformations/aggregates/mean) to average the `_value`s of each table. + +```js +from(bucket:"example-bucket") + |> range(start: -1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) + |> window(every: 5m) + |> mean() +``` + +As rows in each window are aggregated, their output table contains only a single row with the aggregate value. +Windowed tables are all still separate and, when visualized, will appear as single, unconnected points. + +![Windowed aggregate data](/img/flux-windowed-aggregates.png) + +## Add times to your aggregates +As values are aggregated, the resulting tables do not have a `_time` column because +the records used for the aggregation all have different timestamps. +Aggregate functions don't infer what time should be used for the aggregate value. +Therefore the `_time` column is dropped. + +A `_time` column is required in the [next operation](#unwindow-aggregate-tables). +To add one, use the [`duplicate()` function](/flux/v0.12/functions/transformations/duplicate) +to duplicate the `_stop` column as the `_time` column for each windowed table. + +```js +from(bucket:"example-bucket") + |> range(start: -1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) + |> window(every: 5m) + |> mean() + |> duplicate(column: "_stop", as: "_time") +``` + +## Unwindow aggregate tables + +Use the `window()` function with the `every: inf` parameter to gather all points +into a single, infinite window. + +```js +from(bucket:"example-bucket") + |> range(start: -1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) + |> window(every: 5m) + |> mean() + |> duplicate(column: "_stop", as: "_time") + |> window(every: inf) +``` + +Once ungrouped and combined into a single table, the aggregate data points will appear connected in your visualization. + +![Unwindowed aggregate data](/img/flux-windowed-aggregates-ungrouped.png) + +## Helper functions +This may seem like a lot of coding just to build a query that aggregates data, however going through the +process helps to understand how data changes "shape" as it is passed through each function. + +Flux provides (and allows you to create) "helper" functions that abstract many of these steps. +The same operation performed in this guide can be accomplished using the +[`aggregateWindow()` function](/flux/v0.12/functions/transformations/aggregates/aggregatewindow). + +```js +from(bucket:"example-bucket") + |> range(start: -1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) + |> aggregateWindow(every: 5m, fn: mean) +``` + +## Congratulations! +You have now constructed a Flux query that uses Flux functions to transform your data. +There are many more ways to manipulate your data using both Flux's primitive functions +and your own custom functions, but this is a good introduction into the basic syntax and query structure. + +--- + +_For a deeper dive into windowing and aggregating data with example data output for each transformation, +view the [Windowing and aggregating data](/flux/v0.12/guides/windowing-aggregating) guide._ + +--- + + diff --git a/static/img/flux-windowed-aggregates-ungrouped.png b/static/img/flux-windowed-aggregates-ungrouped.png new file mode 100644 index 0000000000000000000000000000000000000000..1b5ff82a63999d56999d305b4ac718f97730ea9b GIT binary patch literal 27645 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 literal 0 HcmV?d00001 From ef3d94b27188ea0418d1ca5eff9052a984256981 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 21 Jan 2019 17:36:09 -0700 Subject: [PATCH 03/14] added page navigation buttons --- assets/styles/layouts/_layout-article.scss | 44 +++++++++++++++++++++- assets/styles/themes/_theme-dark.scss | 6 +++ assets/styles/themes/_theme-light.scss | 6 +++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/assets/styles/layouts/_layout-article.scss b/assets/styles/layouts/_layout-article.scss index 5597a47d9..4d7923045 100644 --- a/assets/styles/layouts/_layout-article.scss +++ b/assets/styles/layouts/_layout-article.scss @@ -544,7 +544,7 @@ } } - ///////////////////////////////// Scroll Bars ////////////////////////////////// + //////////////////////////////// Scroll Bars ///////////////////////////////// pre { @include scrollbar($article-code-bg, $article-code-scrollbar); } table { @include scrollbar($article-table-row-alt, $article-code-scrollbar);} @@ -557,6 +557,48 @@ pre { @include scrollbar($article-warn-code-bg, $article-warn-code-scrollbar); } table { @include scrollbar($article-warn-table-row-alt, $article-warn-code-scrollbar); } } + + ////////////////////////// Guides Pagination Buttons ///////////////////////// + + .page-nav-btns { + display: flex; + justify-content: space-between; + margin: 3rem 0 1rem; + + .btn { + display: flex; + max-width: 49%; + color: $article-btn-text; + background: $article-btn; + border-radius: $border-radius; + text-align: center; + align-items: center; + &:hover { + background: $article-btn-hover; + } + + &.prev{ + margin-right: auto; + padding: .75rem 1.25rem .75rem .75rem; + &:before { + content: "\e90a"; + font-family: "icomoon"; + margin-right: .5rem; + vertical-align: middle; + } + } + &.next { + margin-left: auto; + padding: .75rem .75rem .75rem 1.25rem; + &:after { + content: "\e90c"; + font-family: "icomoon"; + margin-left: .5rem; + vertical-align: middle; + } + } + } + } } diff --git a/assets/styles/themes/_theme-dark.scss b/assets/styles/themes/_theme-dark.scss index 2577a5746..227b14417 100644 --- a/assets/styles/themes/_theme-dark.scss +++ b/assets/styles/themes/_theme-dark.scss @@ -116,6 +116,12 @@ $article-tab-code-text-hover: $g20-white !default; $article-tab-code-bg-hover: $b-ocean !default; $article-tab-code-active-text: $g20-white !default; +// Article page buttons +$article-btn: $b-ocean !default; +$article-btn-text: $g20-white !default; +$article-btn-hover: $b-pool !default; +$article-btn-text-hover: $g20-white !default; + // Left Navigation $nav-category: $b-ocean !default; $nav-category-hover: $g20-white !default; diff --git a/assets/styles/themes/_theme-light.scss b/assets/styles/themes/_theme-light.scss index 289f0050b..e352dd786 100644 --- a/assets/styles/themes/_theme-light.scss +++ b/assets/styles/themes/_theme-light.scss @@ -115,6 +115,12 @@ $article-tab-code-text-hover: $g20-white; $article-tab-code-bg-hover: $p-comet; $article-tab-code-active-text: $p-star; +// Article page buttons +$article-btn: $b-pool; +$article-btn-text: $g20-white; +$article-btn-hover: $b-ocean; +$article-btn-text-hover: $g20-white; + // Left Navigation $nav-category: $b-ocean; $nav-category-hover: $gr-viridian; From 9c45700db199afb3282f8b2dbae0a8616c2a137c Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 21 Jan 2019 21:34:28 -0700 Subject: [PATCH 04/14] minor updates to flux getting started docs --- content/v2.0/query-data/flux/_index.md | 4 +++- content/v2.0/query-data/flux/get-started/_index.md | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/content/v2.0/query-data/flux/_index.md b/content/v2.0/query-data/flux/_index.md index ad0ba78f0..f4b47863a 100644 --- a/content/v2.0/query-data/flux/_index.md +++ b/content/v2.0/query-data/flux/_index.md @@ -34,4 +34,6 @@ from(bucket:"example-bucket") ## Get started with Flux The best way to familiarize yourself with Flux is to walk through creating a simple Flux query. -[Get Started with Flux](/v2.0/query-data/flux/get-started) + diff --git a/content/v2.0/query-data/flux/get-started/_index.md b/content/v2.0/query-data/flux/get-started/_index.md index b292174e0..714da0277 100644 --- a/content/v2.0/query-data/flux/get-started/_index.md +++ b/content/v2.0/query-data/flux/get-started/_index.md @@ -83,5 +83,6 @@ influx repl --org org-name ``` From 8e254a3b43fc21ae0242e6b99f9bc253d0bcc978 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 21 Jan 2019 22:01:38 -0700 Subject: [PATCH 05/14] added Flux language spec to reference section --- content/v2.0/reference/flux/_index.md | 12 + .../v2.0/reference/flux/language/_index.md | 47 +++ .../flux/language/assignment-scope.md | 83 ++++ .../v2.0/reference/flux/language/blocks.md | 25 ++ .../flux/language/built-ins/_index.md | 20 + .../language/built-ins/system-built-ins.md | 24 ++ .../flux/language/built-ins/time-constants.md | 56 +++ .../reference/flux/language/data-model.md | 60 +++ .../reference/flux/language/expressions.md | 189 +++++++++ .../flux/language/lexical-elements.md | 359 ++++++++++++++++++ .../v2.0/reference/flux/language/notation.md | 34 ++ .../v2.0/reference/flux/language/operators.md | 105 +++++ .../v2.0/reference/flux/language/options.md | 60 +++ .../v2.0/reference/flux/language/packages.md | 71 ++++ .../reference/flux/language/representation.md | 37 ++ .../reference/flux/language/side-effects.md | 16 + .../reference/flux/language/statements.md | 161 ++++++++ content/v2.0/reference/flux/language/types.md | 102 +++++ .../v2.0/reference/flux/language/variables.md | 13 + 19 files changed, 1474 insertions(+) create mode 100644 content/v2.0/reference/flux/_index.md create mode 100644 content/v2.0/reference/flux/language/_index.md create mode 100644 content/v2.0/reference/flux/language/assignment-scope.md create mode 100644 content/v2.0/reference/flux/language/blocks.md create mode 100644 content/v2.0/reference/flux/language/built-ins/_index.md create mode 100644 content/v2.0/reference/flux/language/built-ins/system-built-ins.md create mode 100644 content/v2.0/reference/flux/language/built-ins/time-constants.md create mode 100644 content/v2.0/reference/flux/language/data-model.md create mode 100644 content/v2.0/reference/flux/language/expressions.md create mode 100644 content/v2.0/reference/flux/language/lexical-elements.md create mode 100644 content/v2.0/reference/flux/language/notation.md create mode 100644 content/v2.0/reference/flux/language/operators.md create mode 100644 content/v2.0/reference/flux/language/options.md create mode 100644 content/v2.0/reference/flux/language/packages.md create mode 100644 content/v2.0/reference/flux/language/representation.md create mode 100644 content/v2.0/reference/flux/language/side-effects.md create mode 100644 content/v2.0/reference/flux/language/statements.md create mode 100644 content/v2.0/reference/flux/language/types.md create mode 100644 content/v2.0/reference/flux/language/variables.md diff --git a/content/v2.0/reference/flux/_index.md b/content/v2.0/reference/flux/_index.md new file mode 100644 index 000000000..c2f8e6cde --- /dev/null +++ b/content/v2.0/reference/flux/_index.md @@ -0,0 +1,12 @@ +--- +title: Flux query language +description: placeholder +menu: + v2_0_ref: + name: Flux query language + weight: 2 +--- + +[Flux functions](/v2.0/reference/flux/functions/) + +[Flux language specification](/v2.0/reference/flux/language/) diff --git a/content/v2.0/reference/flux/language/_index.md b/content/v2.0/reference/flux/language/_index.md new file mode 100644 index 000000000..323ab6a21 --- /dev/null +++ b/content/v2.0/reference/flux/language/_index.md @@ -0,0 +1,47 @@ +--- +title: Flux language specification +description: > + Covers the current and future Flux functional data scripting language, + which is designed for querying, analyzing, and acting on data. +menu: + v2_0_ref: + name: Flux language specification + parent: Flux query language + weight: 5 +--- + +The following document specifies the Flux language and query execution. + +{{% note %}} +This document is a living document and may not represent the current implementation of Flux. +Any section that is not currently implemented is commented with a **[IMPL#XXX]** where +**XXX** is an issue number tracking discussion and progress towards implementation. +{{% /note %}} + +The Flux language is centered on querying and manipulating time series data. + +### Notation +The syntax of the language is specified using [Extended Backus-Naur Form (EBNF)](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form): + +```js +Production = production_name "=" [ Expression ] "." . +Expression = Alternative { "|" Alternative } . +Alternative = Term { Term } . +Term = production_name | token [ "…" token ] | Group | Option | Repetition . +Group = "(" Expression ")" . +Option = "[" Expression "]" . +Repetition = "{" Expression "}" . +``` + +Productions are expressions constructed from terms and the following operators, in increasing precedence: + +``` +| alternation +() grouping +[] option (0 or 1 times) +{} repetition (0 to n times) +``` + +Lower-case production names are used to identify lexical tokens. +Non-terminals are in Camel case. +Lexical tokens are enclosed in double quotes (`""`) or back quotes (``). diff --git a/content/v2.0/reference/flux/language/assignment-scope.md b/content/v2.0/reference/flux/language/assignment-scope.md new file mode 100644 index 000000000..1a572a13a --- /dev/null +++ b/content/v2.0/reference/flux/language/assignment-scope.md @@ -0,0 +1,83 @@ +--- +title: Assignment and scope +description: An assignment binds an identifier to a variable, option, or function. Every identifier in a program must be assigned. +menu: + v2_0_ref: + parent: Flux language specification + name: Assignment and scope + weight: 20 +--- + +{{% note %}} +This document is a living document and may not represent the current implementation of Flux. +Any section that is not currently implemented is commented with a **[IMPL#XXX]** where +**XXX** is an issue number tracking discussion and progress towards implementation. +{{% /note %}} + +An assignment binds an identifier to a variable, option, or function. +Every identifier in a program must be assigned. + +Flux is lexically scoped using blocks: + +1. The scope of a preassigned identifier is in the universe block. +2. The scope of an identifier denoting a variable, option, or function at the top level (outside any function) is the package block. +3. The scope of the name of an imported package is the file block of the file containing the import declaration. +4. The scope of an identifier denoting a function argument is the function body. +5. The scope of an identifier assigned inside a function is the innermost containing block. + +An identifier assigned in a block may be reassigned in an inner block with the exception of option identifiers. +While the identifier of the inner assignment is in scope, it denotes the entity assigned by the inner assignment. + +Note that the package clause is not an assignment. +The package name does not appear in any scope. +Its purpose is to identify the files belonging to the same package and to specify the default package name for import declarations. + +{{% note %}} +To be implemented: [IMPL#247](https://github.com/influxdata/platform/issues/247) Add package/namespace support. +{{% /note %}} + +## Variable assignment +A variable assignment creates a variable bound to an identifier and gives it a type and value. +A variable keeps the same type and value for the remainder of its lifetime. +An identifier assigned to a variable in a block cannot be reassigned in the same block. +An identifier can be reassigned or shadowed in an inner block. + +```js +VariableAssignment = identifier "=" Expression +``` + +##### Examples of variable assignment + +```js +n = 1 +m = 2 +x = 5.4 +f = () => { + n = "a" + m = "b" + return a + b +} +``` + +## Option assignment +```js +OptionAssignment = "option" [ identifier "." ] identifier "=" Expression +``` + +An option assignment creates an option bound to an identifier and gives it a type and a value. +Options may only be assigned in a package block. +An identifier assigned to an option may be reassigned a new value but not a new type. +An option keeps the same type for the remainder of its lifetime. + +###### Examples +```js +// alert package +option severity = ["low", "moderate", "high"] +// foo package +import "alert" +option alert.severity = ["low", "critical"] // qualified option +option n = 1 +option n = 2 +f = (a, b) => a + b + n +x = f(a:1, b:1) // x = 4 +``` diff --git a/content/v2.0/reference/flux/language/blocks.md b/content/v2.0/reference/flux/language/blocks.md new file mode 100644 index 000000000..54cd7a459 --- /dev/null +++ b/content/v2.0/reference/flux/language/blocks.md @@ -0,0 +1,25 @@ +--- +title: Blocks +description: A block is a possibly empty sequence of statements within matching braces ({}). +menu: + v2_0_ref: + parent: Flux language specification + name: Blocks + weight: 30 +--- + +A _block_ is a possibly empty sequence of statements within matching braces (`{}`). + +``` +Block = "{" StatementList "} . +StatementList = { Statement } . +``` + +In addition to _explicit blocks_ in the source code, there are _implicit blocks_: + +1. The _universe block_ encompasses all Flux source text. +2. Each package has a _package block_ containing all Flux source text for that package. +3. Each file has a _file block_ containing all Flux source text in that file. +4. Each function literal has its own _function block_ even if not explicitly declared. + +Blocks nest and influence scoping. diff --git a/content/v2.0/reference/flux/language/built-ins/_index.md b/content/v2.0/reference/flux/language/built-ins/_index.md new file mode 100644 index 000000000..b235eb5d0 --- /dev/null +++ b/content/v2.0/reference/flux/language/built-ins/_index.md @@ -0,0 +1,20 @@ +--- +title: Built-ins +description: > + Flux contains many preassigned values. + These preassigned values are defined in the source files for the various built-in packages. +menu: + v2_0_ref: + name: Built-ins + parent: Flux language specification + weight: 80 +--- + +Flux contains many preassigned values. +These preassigned values are defined in the source files for the various built-in packages. + +## [System built-ins](/flux/v0.x/language/built-ins/system-built-ins) +When a built-in value is not expressible in Flux, its value may be defined by the hosting environment. + +## [Time constants](/flux/v0.x/language/built-ins/time-constants) +When a built-in value is not expressible in Flux, its value may be defined by the hosting environment. diff --git a/content/v2.0/reference/flux/language/built-ins/system-built-ins.md b/content/v2.0/reference/flux/language/built-ins/system-built-ins.md new file mode 100644 index 000000000..9727f6305 --- /dev/null +++ b/content/v2.0/reference/flux/language/built-ins/system-built-ins.md @@ -0,0 +1,24 @@ +--- +title: System built-ins +description: > + When a built-in value is not expressible in Flux, its value may be defined by the hosting environment. + All such values must have a corresponding builtin statement to declare the existence and type of the built-in value. +menu: + v2_0_ref: + name: System built-ins + parent: Built-ins + weight: 80 +--- + +When a built-in value is not expressible in Flux, its value may be defined by the hosting environment. +All such values must have a corresponding builtin statement to declare the existence and type of the built-in value. + +```js +BuiltinStatement = "builtin" identifer ":" TypeExpression +``` + +##### Example + +```js +builtin from : (bucket: string, bucketID: string) -> stream +``` diff --git a/content/v2.0/reference/flux/language/built-ins/time-constants.md b/content/v2.0/reference/flux/language/built-ins/time-constants.md new file mode 100644 index 000000000..835b6c8ad --- /dev/null +++ b/content/v2.0/reference/flux/language/built-ins/time-constants.md @@ -0,0 +1,56 @@ +--- +title: Time constants +description: > + Flux provides built-in time constants for days of the week and months of the year. +menu: + v2_0_ref: + name: Time constants + parent: Built-ins + weight: 80 +--- + +{{% note %}} +This document is a living document and may not represent the current implementation of Flux. +Any section that is not currently implemented is commented with a **[IMPL#XXX]** where +**XXX** is an issue number tracking discussion and progress towards implementation. +{{% /note %}} + +## Days of the week +Days of the week are represented as integers in the range `[0-6]`. +The following builtin values are defined: + +```js +Sunday = 0 +Monday = 1 +Tuesday = 2 +Wednesday = 3 +Thursday = 4 +Friday = 5 +Saturday = 6 +``` + +{{% note %}} +To be implemented: [IMPL#153](https://github.com/influxdata/flux/issues/153) Add Days of the Week constants +{{% /note %}} + +## Months of the year +Months are represented as integers in the range `[1-12]`. +The following builtin values are defined: +```js +January = 1 +February = 2 +March = 3 +April = 4 +May = 5 +June = 6 +July = 7 +August = 8 +September = 9 +October = 10 +November = 11 +December = 12 +``` + +{{% note %}} +To be implemented: [IMPL#154](https://github.com/influxdata/flux/issues/154) Add Months of the Year constants +{{% /note %}} diff --git a/content/v2.0/reference/flux/language/data-model.md b/content/v2.0/reference/flux/language/data-model.md new file mode 100644 index 000000000..b9e1067fb --- /dev/null +++ b/content/v2.0/reference/flux/language/data-model.md @@ -0,0 +1,60 @@ +--- +title: Flux data model +description: Flux employs a basic data model built from basic data types. The data model consists of tables, records, columns and streams. +menu: + v2_0_ref: + name: Data model + parent: Flux language specification + weight: 1 +--- + +{{% note %}} +This document is a living document and may not represent the current implementation of Flux. +Any section that is not currently implemented is commented with a **[IMPL#XXX]** where +**XXX** is an issue number tracking discussion and progress towards implementation. +{{% /note %}} + +Flux employs a basic data model built from basic data types. +The data model consists of tables, records, columns and streams. + +## Record +A **record** is a tuple of named values and is represented using an object type. + +## Column +A **column** has a label and a data type. +The available data types for a column are: + +| Data type | Description | +| --------- |:----------- | +| bool | A boolean value, true or false. | +| uint | An unsigned 64-bit integer. | +| int | A signed 64-bit integer. | +| float | An IEEE-754 64-bit floating-point number. | +| string | A sequence of unicode characters. | +| bytes | A sequence of byte values. | +| time | A nanosecond precision instant in time. | +| duration | A nanosecond precision duration of time. | + +## Table +A **table** is set of records with a common set of columns and a group key. + +The group key is a list of columns. +A table's group key denotes which subset of the entire dataset is assigned to the table. +All records within a table will have the same values for each column that is part of the group key. +These common values are referred to as the "group key value" and can be represented as a set of key value pairs. + +A tables schema consists of its group key and its columns' labels and types. + +## Stream of tables +A **stream** represents a potentially unbounded set of tables. +A stream is grouped into individual tables using their respective group keys. +Tables within a stream each have a unique group key value. + +## Missing values +A record may be missing a value for a specific column. +Missing values are represented with a special `null` value. +The `null` value can be of any data type. + +{{% note %}} +To be implemented: [IMPL#300](https://github.com/influxdata/platform/issues/300) Design how nulls behave +{{% /note %}} diff --git a/content/v2.0/reference/flux/language/expressions.md b/content/v2.0/reference/flux/language/expressions.md new file mode 100644 index 000000000..2ad8a6e1a --- /dev/null +++ b/content/v2.0/reference/flux/language/expressions.md @@ -0,0 +1,189 @@ +--- +title: Expressions +description: An expression specifies the computation of a value by applying the operators and functions to operands. +menu: + v2_0_ref: + parent: Flux language specification + name: Expressions + weight: 40 +--- + +An _expression_ specifies the computation of a value by applying the operators and functions to operands. + +## Operands and primary expressions + +Operands denote the elementary values in an expression. + +Primary expressions are the operands for unary and binary expressions. +A primary expressions may be a literal, an identifier denoting a variable, or a parenthesized expression. + +```js +PrimaryExpression = identifier | Literal | "(" Expression ")" . +``` + +## Literals +Literals construct a value. + +```js +Literal = int_lit + | float_lit + | string_lit + | regex_lit + | duration_lit + | pipe_receive_lit + | ObjectLiteral + | ArrayLiteral + | FunctionLiteral . +``` + +### Object literals +Object literals construct a value with the object type. + +```js +ObjectLiteral = "{" PropertyList "}" . +PropertyList = [ Property { "," Property } ] . +Property = identifier [ ":" Expression ] + | string_lit ":" Expression . +``` + +### Array literals + +Array literals construct a value with the array type. + +```js +ArrayLiteral = "[" ExpressionList "]" . +ExpressionList = [ Expression { "," Expression } ] . +``` + +### Function literals + +A _function literal_ defines a new function with a body and parameters. +The function body may be a block or a single expression. +The function body must have a return statement if it is an explicit block, otherwise the expression is the return value. + +```js +FunctionLiteral = FunctionParameters "=>" FunctionBody . +FunctionParameters = "(" [ ParameterList [ "," ] ] ")" . +ParameterList = Parameter { "," Parameter } . +Parameter = identifier [ "=" Expression ] . +FunctionBody = Expression | Block . +``` + +##### Examples of function literals + +```js +() => 1 // function returns the value 1 +(a, b) => a + b // function returns the sum of a and b +(x=1, y=1) => x * y // function with default values +(a, b, c) => { // function with a block body + d = a + b + return d / c +} + +``` +All function literals are anonymous. +A function may be given a name using a variable assignment. + +``` +add = (a,b) => a + b +mul = (a,b) => a * b +``` + +Function literals are _closures_ and may refer to variables defined in a surrounding block. +Those variables are shared between the function literal and the surrounding block. + +## Call expressions + +A _call expression_ invokes a function with the provided arguments. +Arguments must be specified using the argument name. +Positional arguments are not supported. +Argument order does not matter. +When an argument has a default value, it is not required to be specified. + +```js +CallExpression = "(" PropertyList ")" . +``` + +##### Examples of call expressions + +```js +f(a:1, b:9.6) +float(v:1) +``` + +## Pipe expressions + +A _pipe expression_ is a call expression with an implicit piped argument. +Pipe expressions simplify creating long nested call chains. + +Pipe expressions pass the result of the left hand expression as the _pipe argument_ to the right hand call expression. +Function literals specify which if any argument is the pipe argument using the _pipe literal_ as the argument's default value. +It is an error to use a pipe expression if the function does not declare a pipe argument. + +```js +pipe_receive_lit = "<-" . +``` + +##### Examples of pipe expressions + +```js +foo = () => // function body elided +bar = (x=<-) => // function body elided +baz = (y=<-) => // function body elided +foo() |> bar() |> baz() // equivalent to baz(x:bar(y:foo())) +``` + +## Index expressions +Index expressions access a value from an array based on a numeric index. + +```js +IndexExpression = "[" Expression "]" . +``` + +## Member expressions +Member expressions access a property of an object. +The property being accessed must be either an identifier or a string literal. +In either case the literal value is the name of the property being accessed, the identifier is not evaluated. +It is not possible to access an object's property using an arbitrary expression. + +```js +MemberExpression = DotExpression | MemberBracketExpression +DotExpression = "." identifer +MemberBracketExpression = "[" string_lit "]" . +``` + +### Operators +Operators combine operands into expressions. +Operator precedence is encoded directly into the grammar. + +```js +Expression = LogicalExpression . +LogicalExpression = UnaryLogicalExpression + | LogicalExpression LogicalOperator UnaryLogicalExpression . +LogicalOperator = "and" | "or" . +UnaryLogicalExpression = ComparisonExpression + | UnaryLogicalOperator UnaryLogicalExpression . +UnaryLogicalOperator = "not" . +ComparisonExpression = MultiplicativeExpression + | ComparisonExpression ComparisonOperator MultiplicativeExpression . +ComparisonOperator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "=~" | "!~" . +MultiplicativeExpression = AdditiveExpression + | MultiplicativeExpression MultiplicativeOperator AdditiveExpression . +MultiplicativeOperator = "*" | "/" . +AdditiveExpression = PipeExpression + | AdditiveExpression AdditiveOperator PipeExpression . +AdditiveOperator = "+" | "-" . +PipeExpression = PostfixExpression + | PipeExpression PipeOperator UnaryExpression . +PipeOperator = "|>" . +UnaryExpression = PostfixExpression + | PrefixOperator UnaryExpression . +PrefixOperator = "+" | "-" . +PostfixExpression = PrimaryExpression + | PostfixExpression PostfixOperator . +PostfixOperator = MemberExpression + | CallExpression + | IndexExpression . +``` + +_Also see [Flux Operators](/v2.0/reference/flux/language/operators)._ diff --git a/content/v2.0/reference/flux/language/lexical-elements.md b/content/v2.0/reference/flux/language/lexical-elements.md new file mode 100644 index 000000000..f7d8db05f --- /dev/null +++ b/content/v2.0/reference/flux/language/lexical-elements.md @@ -0,0 +1,359 @@ +--- +title: Lexical elements +description: Descriptions of Flux comments, tokens, identifiers, keywords, and other lexical elements. +menu: + v2_0_ref: + parent: Flux language specification + name: Lexical elements + weight: 50 +--- + +{{% note %}} +This document is a living document and may not represent the current implementation of Flux. +Any section that is not currently implemented is commented with a **[IMPL#XXX]** where +**XXX** is an issue number tracking discussion and progress towards implementation. +{{% /note %}} + +## Comments + +Comment serve as documentation. +Comments begin with the character sequence `//` and stop at the end of the line. + +Comments cannot start inside string or regexp literals. +Comments act like newlines. + +## Tokens + +Flux is built up from tokens. +There are four classes of tokens: + +* _identifiers_ +* _keywords_ +* _operators_ +* _literals_ + + +_White space_ formed from spaces, horizontal tabs, carriage returns, and newlines is ignored except as it separates tokens that would otherwise combine into a single token. +While breaking the input into tokens, the next token is the longest sequence of characters that form a valid token. + +## Identifiers + +Identifiers name entities within a program. +An _identifier_ is a sequence of one or more letters and digits. +An identifier must start with a letter. + +```js + identifier = letter { letter | unicode_digit } . +``` + +##### Examples of identifiers + +``` +a +_x +longIdentifierName +αβ +``` + +## Keywords + +The following keywords are reserved and may not be used as identifiers: + +``` +and import not return option +empty in or package builtin +``` + +{{% note %}} +To be implemented: [IMPL#256](https://github.com/influxdata/platform/issues/256) Add in and empty operator support. + +To be implemented: [IMPL#334](https://github.com/influxdata/platform/issues/334) Add "import" support +{{% /note %}} + + +## Operators + +The following character sequences represent operators: + +``` ++ == != ( ) +- < !~ [ ] +* > =~ { } +/ <= = , : +% >= <- . |> +``` + +## Numeric literals + +Numeric literals may be integers or floating point values. +Literals have arbitrary precision and are coerced to a specific type when used. + +The following coercion rules apply to numeric literals: + +* An integer literal can be coerced to an "int", "uint", or "float" type, +* A float literal can be coerced to a "float" type. +* An error will occur if the coerced type cannot represent the literal value. + +{{% note %}} +To be implemented: [IMPL#255](https://github.com/influxdata/platform/issues/255) Allow numeric literal coercion. +{{% /note %}} + +### Integer literals + +An integer literal is a sequence of digits representing an integer value. +Only decimal integers are supported. + +```js + int_lit = "0" | decimal_lit . + decimal_lit = ( "1" … "9" ) { decimal_digit } . +``` + +##### Examples of integer literals + +``` +0 +42 +317316873 +``` + +## Floating-point literals + +A _floating-point literal_ is a decimal representation of a floating-point value. +It has an integer part, a decimal point, and a fractional part. +The integer and fractional part comprise decimal digits. +One of the integer part or the fractional part may be elided. + +```js +float_lit = decimals "." [ decimals ] + | "." decimals . +decimals = decimal_digit { decimal_digit } . +``` + +##### Examples of floating-point literals + +```js +0. +72.40 +072.40 // == 72.40 +2.71828 +.26 +``` + +{{% note %}} +To be implemented: [IMPL#254](https://github.com/influxdata/platform/issues/254) Parse float literals. +{{% /note %}} + +### Duration literals + +A _duration literal_ is a representation of a length of time. +It has an integer part and a duration unit part. +Multiple durations may be specified together and the resulting duration is the sum of each smaller part. +When several durations are specified together, larger units must appear before smaller ones, and there can be no repeated units. + +```js +duration_lit = { int_lit duration_unit } . +duration_unit = "y" | "mo" | "w" | "d" | "h" | "m" | "s" | "ms" | "us" | "µs" | "ns" . +``` + +| Units | Meaning | +| ----- | ------- | +| y | year (12 months) | +| mo | month | +| w | week (7 days) | +| d | day | +| h | hour (60 minutes) | +| m | minute (60 seconds) | +| s | second | +| ms | milliseconds (1 thousandth of a second) | +| us or µs | microseconds (1 millionth of a second) | +| ns | nanoseconds (1 billionth of a second) | + +Durations represent a length of time. +Lengths of time are dependent on specific instants in time they occur and as such, durations do not represent a fixed amount of time. +No amount of seconds is equal to a day, as days vary in their number of seconds. +No amount of days is equal to a month, as months vary in their number of days. +A duration consists of three basic time units: seconds, days and months. + +Durations can be combined via addition and subtraction. +Durations can be multiplied by an integer value. +These operations are performed on each time unit independently. + +##### Examples of duration literals + +```js +1s +10d +1h15m // 1 hour and 15 minutes +5w +1mo5d // 1 month and 5 days +``` +Durations can be added to date times to produce a new date time. + +Addition and subtraction of durations to date times do not commute and are left associative. +Addition and subtraction of durations to date times applies months, days and seconds in that order. +When months are added to a date times and the resulting date is past the end of the month, the day is rolled back to the last day of the month. + +##### Examples of duration literals + +```js +2018-01-01T00:00:00Z + 1d // 2018-01-02T00:00:00Z +2018-01-01T00:00:00Z + 1mo // 2018-02-01T00:00:00Z +2018-01-01T00:00:00Z + 2mo // 2018-03-01T00:00:00Z +2018-01-31T00:00:00Z + 2mo // 2018-03-31T00:00:00Z +2018-02-28T00:00:00Z + 2mo // 2018-04-28T00:00:00Z +2018-01-31T00:00:00Z + 1mo // 2018-02-28T00:00:00Z, February 31th is rolled back to the last day of the month, February 28th in 2018. + +// Addition and subtraction of durations to date times does not commute +2018-02-28T00:00:00Z + 1mo + 1d // 2018-03-29T00:00:00Z +2018-02-28T00:00:00Z + 1d + 1mo // 2018-04-01T00:00:00Z +2018-01-01T00:00:00Z + 2mo - 1d // 2018-02-28T00:00:00Z +2018-01-01T00:00:00Z - 1d + 3mo // 2018-03-31T00:00:00Z + +// Addition and subtraction of durations to date times applies months, days and seconds in that order. +2018-01-28T00:00:00Z + 1mo + 2d // 2018-03-02T00:00:00Z +2018-01-28T00:00:00Z + 1mo2d // 2018-03-02T00:00:00Z +2018-01-28T00:00:00Z + 2d + 1mo // 2018-02-28T00:00:00Z, explicit left associative add of 2d first changes the result +2018-02-01T00:00:00Z + 2mo2d // 2018-04-03T00:00:00Z +2018-01-01T00:00:00Z + 1mo30d // 2018-03-02T00:00:00Z, Months are applied first to get February 1st, then days are added resulting in March 2 in 2018. +2018-01-31T00:00:00Z + 1mo1d // 2018-03-01T00:00:00Z, Months are applied first to get February 28th, then days are added resulting in March 1 in 2018. +``` + +{{% note %}} +To be implemented: [IMPL#657](https://github.com/influxdata/platform/issues/657) Implement Duration vectors. +{{% /note %}} + +## Date and time literals + +A _date and time literal_ represents a specific moment in time. +It has a date part, a time part and a time offset part. +The format follows the [RFC 3339](https://tools.ietf.org/html/rfc3339) specification. +The time is optional. +When it is omitted, the time is assumed to be midnight for the default location. +The `time_offset` is optional. +When it is omitted, the location option is used to determine the offset. + +```js +date_time_lit = date [ "T" time ] . +date = year_lit "-" month "-" day . +year = decimal_digit decimal_digit decimal_digit decimal_digit . +month = decimal_digit decimal_digit . +day = decimal_digit decimal_digit . +time = hour ":" minute ":" second [ fractional_second ] [ time_offset ] . +hour = decimal_digit decimal_digit . +minute = decimal_digit decimal_digit . +second = decimal_digit decimal_digit . +fractional_second = "." { decimal_digit } . +time_offset = "Z" | ("+" | "-" ) hour ":" minute . +``` + +##### Examples of date and time literals + +```js +1952-01-25T12:35:51Z +2018-08-15T13:36:23-07:00 +2009-10-15T09:00:00 // October 15th 2009 at 9 AM in the default location +2018-01-01 // midnight on January 1st 2018 in the default location +``` + +{{% note %}} +To be implemented: [IMPL#152](https://github.com/influxdata/flux/issues/152) Implement shorthand time literals. +{{% /note %}} + +### String literals + +A _string literal_ represents a sequence of characters enclosed in double quotes. +Within the quotes any character may appear except an unescaped double quote. +String literals support several escape sequences. + +``` +\n U+000A line feed or newline +\r U+000D carriage return +\t U+0009 horizontal tab +\" U+0022 double quote +\\ U+005C backslash +\{ U+007B open curly bracket +\} U+007D close curly bracket +``` + +Additionally, any byte value may be specified via a hex encoding using `\x` as the prefix. + +``` +string_lit = `"` { unicode_value | byte_value | StringExpression | newline } `"` . +byte_value = `\` "x" hex_digit hex_digit . +hex_digit = "0" … "9" | "A" … "F" | "a" … "f" . +unicode_value = unicode_char | escaped_char . +escaped_char = `\` ( "n" | "r" | "t" | `\` | `"` ) . +StringExpression = "{" Expression "}" . +``` + +{{% note %}} +To be added: TODO: With string interpolation `string_lit` is not longer a lexical token as part of a literal, but an entire expression in and of itself. + +To be implemented: [IMPL#252](https://github.com/influxdata/platform/issues/252) Parse string literals. +{{% /note %}} + +##### Examples of string literals + +```js +"abc" +"string with double \" quote" +"string with backslash \\" +"日本語" +"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" // the explicit UTF-8 encoding of the previous line +``` + +String literals are also interpolated for embedded expressions to be evaluated as strings. +Embedded expressions are enclosed in curly brackets (`{}`). +The expressions are evaluated in the scope containing the string literal. +The result of an expression is formatted as a string and replaces the string content between the brackets. +All types are formatted as strings according to their literal representation. +A function `printf` exists to allow more precise control over formatting of various types. +To include the literal curly brackets within a string they must be escaped. + +{{% note %}} +To be implemented: [IMPL#248](https://github.com/influxdata/platform/issues/248) Add printf function. +{{% /note %}} + +##### Example: Interpolation + +```js +n = 42 +"the answer is {n}" // the answer is 42 +"the answer is not {n+1}" // the answer is not 43 +"openinng curly bracket \{" // openinng curly bracket { +"closing curly bracket \}" // closing curly bracket } +``` + +{{% note %}} +To be implemented: [IMPL#251](https://github.com/influxdata/platform/issues/251) Add string interpolation support +{{% /note %}} + +### Regular expression literals + +A _regular expression literal_ represents a regular expression pattern, enclosed in forward slashes. +Within the forward slashes, any unicode character may appear except for an unescaped forward slash. +The `\x` hex byte value representation from string literals may also be present. + +Regular expression literals support only the following escape sequences: + +``` + \/ U+002f forward slash + \\ U+005c backslash +``` + +``` +regexp_lit = "/" { unicode_char | byte_value | regexp_escape_char } "/" . +regexp_escape_char = `\` (`/` | `\`) +``` + +##### Examples of regular expression literals + +```js +/.*/ +/http:\/\/localhost:9999/ +/^\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e(ZZ)?$/ +/^日本語(ZZ)?$/ // the above two lines are equivalent +/\\xZZ/ // this becomes the literal pattern "\xZZ" +``` + +The regular expression syntax is defined by [RE2](https://github.com/google/re2/wiki/Syntax). diff --git a/content/v2.0/reference/flux/language/notation.md b/content/v2.0/reference/flux/language/notation.md new file mode 100644 index 000000000..390eaaa19 --- /dev/null +++ b/content/v2.0/reference/flux/language/notation.md @@ -0,0 +1,34 @@ +--- +title: Notation +description: Notation principles for the Flux functional data scripting language. +menu: + v2_0_ref: + parent: Flux language specification + name: Notation + weight: 60 +--- + +The syntax of the language is specified using [Extended Backus-Naur Form (EBNF)](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form): + +``` +Production = production_name "=" [ Expression ] "." . +Expression = Alternative { "|" Alternative } . +Alternative = Term { Term } . +Term = production_name | token [ "…" token ] | Group | Option | Repetition . +Group = "(" Expression ")" . +Option = "[" Expression "]" . +Repetition = "{" Expression "}" . +``` + +A _production_ is an expression constructed from terms and the following operators, in increasing precedence: + +``` +| alternation +() grouping +[] option (0 or 1 times) +{} repetition (0 to n times) +``` + +Lowercase production names are used to identify lexical tokens. +Non-terminals are in [camel case](https://en.wikipedia.org/wiki/Camel_case). +Lexical tokens are enclosed in double quotes (`""`) or back quotes (``). diff --git a/content/v2.0/reference/flux/language/operators.md b/content/v2.0/reference/flux/language/operators.md new file mode 100644 index 000000000..100afac17 --- /dev/null +++ b/content/v2.0/reference/flux/language/operators.md @@ -0,0 +1,105 @@ +--- +title: Operators in the Flux language +description: Flux supports many types of operators including arithmetic operators, comparison operators, function operators, and others. +menu: + v2_0_ref: + name: Operators + parent: Flux language specification + weight: 130 +--- + +Flux includes the following types of operators: + +- [Arithmetic operators](#arithmetic-operators) +- [Comparison operators](#comparison-operators) +- [Assignment operators](#assignment-operators) +- [Function operators](#function-operators) +- [String Operators](#string-operators) +- [Literal constructors](#literal-constructors) +- [Miscellaneous operators](#miscellaneous-operators) + +## Arithmetic operators +Arithmetic operators take two numerical values (either literals or variables) and +perform a calculation that returns a single numerical value. + +| Operator | Description | Example | Result | +|:--------:| ----------- | ------- | ------ | +| `+` | Addition | `1 + 1` | `2` | +| `-` | Subtraction | `3 - 2` | `1` | +| `*` | Multiplication | `2 * 3` | `6` | +| `/` | Division | `9 / 3` | `3` | +| `%` | Modulus | `10 % 5` | `0` | + +{{% note %}} +In the current version of Flux, values used in arithmetic operations must +be of the same numeric type (integer or float). +Operations with values of different numeric types will result in a type error. +{{% /note %}} + +## Comparison operators +Comparison operators compare expressions and return true or false based on the comparison. + +| Operator | Description | Example | Result | +|:--------:| ----------- | ------- | ------ | +| `==` | Equal to | `"abc" == "abc"` | `true` | +| `!=` | Not equal to | `"abc" != "def"` | `true` | +| `<` | Less than | `1 < 2` | `true` | +| `>` | Greater than | `1 > 2` | `false` | +| `<=` | Less than or equal | `1 <= 2` | `true` | +| `>=` | Greater than or equal | `1 >= 2` | `false` | +| `=~` | Equal to regular expression | `"abc" =~ /[a-z]*/` | `true` | +| `!~` | Not equal to regular expression | `"abc" !~ /[0-9]*/` | `true` | + +{{% note %}} +The `>` and `<` operators also [compare the lexicographic order of strings](#string-operators). +{{% /note %}} + +## Assignment operators +An assignment operator assigns a value to its left operand based on the value of its right operand. + +| Operator | Description | Example | Meaning | +|:--------:| ----------- | ------- | ------- | +| `=` | Assign value of left expression to right expression | `x = y` | x = y | + + +## Function operators +Function operators facilitate the creation of functions and control the flow of data through operations. + +| Operator | Description | Examples | Meaning | +|:--------: | ----------- | -------- | ------- | +| |> | Pipe‑forward | data |> function() | Tables contained in the "data" variable are piped into the function. | +| `<-` | Pipe‑receive | `tables=<-` | The "tables" variable or parameter is assigned to data piped into the operation. _This operator is used for any data type passed into a function; not just table data._ | +| `=>` | Arrow | `(r) => r.tag1 == "tagvalue"` | The arrow passes an object or parameters into function operations. | +| `()` | Function call | `top(n:10)` | Call the `top` function setting the `n` parameter to `10` and perform the associated operations. | + +--- + +_See [Custom functions](#) for examples of function operators is use._ + +--- + +## String Operators +String operators concatenate or compare string values. + +| Operator | Description | Examples | Result | +|:--------:| ----------- | -------- | ------ | +| `+` | Concatenation | `"ab" + "c"` | `"abc"` | +| `<` | Less than in lexicographic order | `"ant" < "bee"` | `true` | +| `>` | Greater than in lexicographic order | `"ant" > "bee"` | `false` | + +## Literal constructors +Literal constructors define fixed values. + +| Operator | Description | +|:--------:| ----------- | +| `[ ]` | List / array | +| `{ }` | Object | +| `""` | String | + +## Miscellaneous operators +| Operator | Description | Example | +|:--------:| ----------- | ------- | +| `( )` | Logical grouping | `r._value / (r._value * 2)` | +| `,` | Sequence delimiter | `item1, item2, item3` | +| `:` | Key-value separator | `{name: "Bob"}` | +| `.` | Dot reference | `r._measurement` | diff --git a/content/v2.0/reference/flux/language/options.md b/content/v2.0/reference/flux/language/options.md new file mode 100644 index 000000000..3908357c8 --- /dev/null +++ b/content/v2.0/reference/flux/language/options.md @@ -0,0 +1,60 @@ +--- +title: Options +description: placeholder +menu: + v2_0_ref: + parent: Flux language specification + name: Options + weight: 110 +--- + +{{% note %}} +This document is a living document and may not represent the current implementation of Flux. +Any section that is not currently implemented is commented with a **[IMPL#XXX]** where +**XXX** is an issue number tracking discussion and progress towards implementation. +{{% /note %}} + +An option represents a storage location for any value of a specified type. +Options are mutable. +An option can hold different values during its lifetime. + +Below is a list of built-in options currently implemented in the Flux language: + +- now +- task +- location + +##### now +The `now` option is a function that returns a time value used as a proxy for the current system time. + +```js +// Query should execute as if the below time is the current system time +option now = () => 2006-01-02T15:04:05-07:00 +``` + +##### task +The `task` option schedules the execution of a Flux query. + +```js +option task = { + name: "foo", // Name is required. + every: 1h, // Task should be run at this interval. + delay: 10m, // Delay scheduling this task by this duration. + cron: "0 2 * * *", // Cron is a more sophisticated way to schedule. 'every' and 'cron' are mutually exclusive. + retry: 5, // Number of times to retry a failed query. +} +``` + +##### location +The `location` option sets the default time zone of all times in the script. +The location maps the UTC offset in use at that location for a given time. +The default value is set using the time zone of the running process. + +```js +option location = fixedZone(offset:-5h) // Set timezone to be 5 hours west of UTC. +option location = loadLocation(name:"America/Denver") // Set location to be America/Denver. +``` + +{{% note %}} +To be implemented: [IMPL#660](https://github.com/influxdata/platform/issues/660) Implement Location option +{{% /note %}} diff --git a/content/v2.0/reference/flux/language/packages.md b/content/v2.0/reference/flux/language/packages.md new file mode 100644 index 000000000..5ed95ef91 --- /dev/null +++ b/content/v2.0/reference/flux/language/packages.md @@ -0,0 +1,71 @@ +--- +title: Packages +description: > + Flux source is organized into packages. + A package consists of one or more source files. + Each source file is parsed individually and composed into a single package. +aliases: + - /flux/v0.x/language/programs +menu: + v2_0_ref: + parent: Flux language specification + name: Packages + weight: 70 +--- + +{{% note %}} +This document is a living document and may not represent the current implementation of Flux. +Any section that is not currently implemented is commented with a **[IMPL#XXX]** where +**XXX** is an issue number tracking discussion and progress towards implementation. +{{% /note %}} + +Flux source is organized into packages. +A package consists of one or more source files. +Each source file is parsed individually and composed into a single package. + +```js +File = [ PackageClause ] [ ImportList ] StatementList . +ImportList = { ImportDeclaration } . +``` + +## Package clause + +```js +PackageClause = "package" identifier . +``` + +A _package clause_ defines the name for the current package. +Package names must be valid Flux identifiers. +The package clause must be at the beginning of any Flux source file. +All files in the same package must declare the same package name. +When a file does not declare a package clause, all identifiers in that +file will belong to the special `main` package. + +{{% note %}} +To be implemented: [IMPL#247](https://github.com/influxdata/platform/issues/247) Add package/namespace support. +{{% /note %}} + +### Package main + +The `main` package is special for a few reasons: + +1. It defines the entry point of a Flux program. +2. It cannot be imported. +3. All statements are marked as producing side effects. + +## Package initialization + +Packages are initialized in the following order: + +1. All imported packages are initialized and assigned to their package identifier. +2. All option declarations are evaluated and assigned regardless of order. An option cannot have dependencies on any other options assigned in the same package block. +3. All variable declarations are evaluated and assigned regardless of order. A variable cannot have a direct or indirect dependency on itself. +4. Any package side effects are evaluated. + +A package will only be initialized once across all file blocks and across all packages blocks regardless of how many times it is imported. + +Initializing imported packages must be deterministic. +Specifically after all imported packages are initialized, each option must be assigned the same value. +Packages imported in the same file block are initialized in declaration order. +Packages imported across different file blocks have no known order. +When a set of imports modify the same option, they must be ordered by placing them in the same file block. diff --git a/content/v2.0/reference/flux/language/representation.md b/content/v2.0/reference/flux/language/representation.md new file mode 100644 index 000000000..91950f1f3 --- /dev/null +++ b/content/v2.0/reference/flux/language/representation.md @@ -0,0 +1,37 @@ +--- +title: Representation +description: Source code is encoded in UTF-8. The text need not be canonicalized. +menu: + v2_0_ref: + parent: Flux language specification + name: Representation + weight: 80 +--- + +Source code is encoded in UTF-8. +The text need not be canonicalized. + +## Characters + +This document will use the term _character_ to refer to a Unicode code point. + +The following terms are used to denote specific Unicode character classes: + +``` +newline = /* the Unicode code point U+000A */ . +unicode_char = /* an arbitrary Unicode code point except newline */ . +unicode_letter = /* a Unicode code point classified as "Letter" */ . +unicode_digit = /* a Unicode code point classified as "Number, decimal digit" */ . +``` + +In The Unicode Standard 8.0, Section 4.5, "General Category" defines a set of character categories. +Flux treats all characters in any of the Letter categories (Lu, Ll, Lt, Lm, or Lo) as Unicode letters, and those in the Number category (Nd) as Unicode digits. + +### Letters and digits + +The underscore character `_` (`U+005F`) is considered a letter. + +``` +letter = unicode_letter | "_" . +decimal_digit = "0" … "9" . +``` diff --git a/content/v2.0/reference/flux/language/side-effects.md b/content/v2.0/reference/flux/language/side-effects.md new file mode 100644 index 000000000..948e0dcd6 --- /dev/null +++ b/content/v2.0/reference/flux/language/side-effects.md @@ -0,0 +1,16 @@ +--- +title: Side effects +description: A summary of side effects in the Flux functional data scripting language. +menu: + v2_0_ref: + parent: Flux language specification + name: Side effects + weight: 90 +--- + +Side effects can occur in one of two ways. + +1. By reassigning built-in options +2. By calling a function that produces side effects + +A function produces side effects when it is explicitly declared to have side effects or when it calls a function that itself produces side effects. diff --git a/content/v2.0/reference/flux/language/statements.md b/content/v2.0/reference/flux/language/statements.md new file mode 100644 index 000000000..7b0bde503 --- /dev/null +++ b/content/v2.0/reference/flux/language/statements.md @@ -0,0 +1,161 @@ +--- +title: Statements +description: Statements control execution in the Flux functional data scripting language. +menu: + v2_0_ref: + parent: Flux language specification + name: Statements + weight: 100 +--- + +{{% note %}} +This document is a living document and may not represent the current implementation of Flux. +Any section that is not currently implemented is commented with a **[IMPL#XXX]** where +**XXX** is an issue number tracking discussion and progress towards implementation. +{{% /note %}} + +A _statement_ controls execution. + +```js +Statement = OptionAssignment + | BuiltinStatement + | VariableAssignment + | ReturnStatement + | ExpressionStatement . +``` + +## Import declaration + +```js +ImportDeclaration = "import" [identifier] string_lit +``` + +A package name and an import path is associated with every package. +The import statement takes a package's import path and brings all of the identifiers +defined in that package into the current scope under a namespace. +The import statement defines the namespace through which to access the imported identifiers. +By default the identifier of this namespace is the package name unless otherwise specified. +For example, given a variable `x` declared in package `foo`, importing `foo` and referencing `x` would look like this: + +```js +import "import/path/to/package/foo" + +foo.x +``` + +Or this: + +```js +import bar "import/path/to/package/foo" + +bar.x +``` + +A package's import path is always absolute. +A package may reassign a new value to an option identifier declared in one of its imported packages. +A package cannot access nor modify the identifiers belonging to the imported packages of its imported packages. +Every statement contained in an imported package is evaluated. + +## Return statements + +A terminating statement prevents execution of all statements that appear after it in the same block. +A return statement is a terminating statement. + +``` +ReturnStatement = "return" Expression . +``` +## Expression statements + +An _expression statement_ is an expression where the computed value is discarded. + +``` +ExpressionStatement = Expression . +``` + +##### Examples of expression statements + +```js +1 + 1 +f() +a +``` + +## Named types + +A named type can be created using a type assignment statement. +A named type is equivalent to the type it describes and may be used interchangeably. + +```js +TypeAssignement = "type" identifier "=" TypeExpression +TypeExpression = identifier + | TypeParameter + | ObjectType + | ArrayType + | GeneratorType + | FunctionType . +TypeParameter = "'" identifier . +ObjectType = "{" PropertyTypeList [";" ObjectUpperBound ] "}" . +ObjectUpperBound = "any" | PropertyTypeList . +PropertyTypeList = PropertyType [ "," PropertyType ] . +PropertyType = identifier ":" TypeExpression + | string_lit ":" TypeExpression . +ArrayType = "[]" TypeExpression . +GeneratorType = "[...]" TypeExpression . +FunctionType = ParameterTypeList "->" TypeExpression +ParameterTypeList = "(" [ ParameterType { "," ParameterType } ] ")" . +ParameterType = identifier ":" [ pipe_receive_lit ] TypeExpression . +``` + +Named types are a separate namespace from values. +It is possible for a value and a type to have the same identifier. +The following named types are built-in. + +```js +bool // boolean +int // integer +uint // unsigned integer +float // floating point number +duration // duration of time +time // time +string // utf-8 encoded string +regexp // regular expression +type // a type that itself describes a type +``` + +When an object's upper bound is not specified, it is assumed to be equal to its lower bound. + +Parameters to function types define whether the parameter is a pipe forward +parameter and whether the parameter has a default value. +The `<-` indicates the parameter is the pipe forward parameter. + +###### Examples +```js + // alias the bool type + type boolean = bool + + // define a person as an object type + type person = { + name: string, + age: int, + } + + // Define addition on ints + type intAdd = (a: int, b: int) -> int + + // Define polymorphic addition + type add = (a: 'a, b: 'a) -> 'a + + // Define funcion with pipe parameter + type bar = (foo: <-string) -> string + + // Define object type with an empty lower bound and an explicit upper bound + type address = { + ; + street: string, + city: string, + state: string, + country: string, + province: string, + zip: int, + } +``` diff --git a/content/v2.0/reference/flux/language/types.md b/content/v2.0/reference/flux/language/types.md new file mode 100644 index 000000000..1b605b501 --- /dev/null +++ b/content/v2.0/reference/flux/language/types.md @@ -0,0 +1,102 @@ +--- +title: Types +description: A type defines the set of values and operations on those values. Types are never explicitly declared as part of the syntax. Types are always inferred from the usage of the value. +menu: + v2_0_ref: + parent: Flux language specification + name: Types + weight: 110 +--- + +{{% note %}} +This document is a living document and may not represent the current implementation of Flux. +Any section that is not currently implemented is commented with a **[IMPL#XXX]** where +**XXX** is an issue number tracking discussion and progress towards implementation. +{{% /note %}} + +A _type_ defines the set of values and operations on those values. +Types are never explicitly declared as part of the syntax. +Types are always inferred from the usage of the value. + +{{% note %}} +To be implemented: [IMPL#249](https://github.com/influxdata/platform/issues/249) Specify type inference rules. +{{% /note %}} + +## Boolean types + +A _boolean type_ represents a truth value, corresponding to the preassigned variables `true` and `false`. +The boolean type name is `bool`. + +## Numeric types + +A _numeric type_ represents sets of integer or floating-point values. + +The following numeric types exist: + +``` +uint the set of all unsigned 64-bit integers +int the set of all signed 64-bit integers +float the set of all IEEE-754 64-bit floating-point numbers +``` + +## Time types + +A _time type_ represents a single point in time with nanosecond precision. +The time type name is `time`. + +## Duration types + +A _duration type_ represents a length of time with nanosecond precision. +The duration type name is `duration`. + +Durations can be added to times to produce a new time. + +##### Examples of duration types + +```js +2018-07-01T00:00:00Z + 1mo // 2018-08-01T00:00:00Z +2018-07-01T00:00:00Z + 2y // 2020-07-01T00:00:00Z +2018-07-01T00:00:00Z + 5h // 2018-07-01T05:00:00Z +``` + +## String types + +A _string type_ represents a possibly empty sequence of characters. +Strings are immutable and cannot be modified once created. +The string type name is `string`. + +The length of a string is its size in bytes, not the number of characters, since a single character may be multiple bytes. + +## Regular expression types + +A _regular expression type_ represents the set of all patterns for regular expressions. +The regular expression type name is `regexp`. + +## Array types + +An _array type_ represents a sequence of values of any other type. +All values in the array must be of the same type. +The length of an array is the number of elements in the array. + +## Object types + +An _object type_ represents a set of unordered key and value pairs. +The key must always be a string. +The value may be any other type, and need not be the same as other values within the object. + +## Function types + +A _function type_ represents a set of all functions with the same argument and result types. + +{{% note %}} +To be implemented: [IMPL#249](https://github.com/influxdata/platform/issues/249) Specify type inference rules. +{{% /note %}} + +## Generator types + +A _generator type_ represents a value that produces an unknown number of other values. +The generated values may be of any other type, but must all be the same type. + +{{% note %}} +To be implemented: [IMPL#658](https://github.com/influxdata/platform/query/issues/658) Implement Generators types. +{{% /note %}} diff --git a/content/v2.0/reference/flux/language/variables.md b/content/v2.0/reference/flux/language/variables.md new file mode 100644 index 000000000..ab9d16314 --- /dev/null +++ b/content/v2.0/reference/flux/language/variables.md @@ -0,0 +1,13 @@ +--- +title: Variables +description: Flux variables hold values. A variable can only hold values defined by its type. +menu: + v2_0_ref: + parent: Flux language specification + name: Variables + weight: 120 +--- + +A **variable** represents a storage location for a single value. +Variables are immutable. +Once a variable is given a value, it holds that value for the remainder of its lifetime. From 8025dd561e38cd9a68214fac4162b16a7a3ee530 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 21 Jan 2019 22:31:27 -0700 Subject: [PATCH 06/14] added flux function docs --- .../query-data/flux/get-started/_index.md | 2 +- .../flux/get-started/query-influxdb.md | 10 +- .../flux/get-started/syntax-basics.md | 2 +- .../flux/get-started/transform-data.md | 18 +- .../v2.0/reference/flux/functions/_index.md | 29 ++++ .../flux/functions/custom-functions.md | 133 +++++++++++++++ .../reference/flux/functions/inputs/_index.md | 14 ++ .../flux/functions/inputs/buckets.md | 22 +++ .../reference/flux/functions/inputs/from.md | 50 ++++++ .../flux/functions/inputs/fromcsv.md | 64 +++++++ .../reference/flux/functions/misc/_index.md | 15 ++ .../flux/functions/misc/intervals.md | 154 +++++++++++++++++ .../flux/functions/misc/linearbins.md | 51 ++++++ .../flux/functions/misc/logarithmicbins.md | 50 ++++++ .../flux/functions/misc/systemtime.md | 23 +++ .../flux/functions/outputs/_index.md | 14 ++ .../reference/flux/functions/outputs/to.md | 158 ++++++++++++++++++ .../reference/flux/functions/outputs/yield.md | 45 +++++ .../reference/flux/functions/tests/_index.md | 14 ++ .../flux/functions/tests/assertequals.md | 67 ++++++++ .../flux/functions/transformations/_index.md | 27 +++ .../transformations/aggregates/_index.md | 47 ++++++ .../aggregates/aggregatewindow.md | 112 +++++++++++++ .../transformations/aggregates/count.md | 45 +++++ .../transformations/aggregates/cov.md | 67 ++++++++ .../transformations/aggregates/covariance.md | 46 +++++ .../transformations/aggregates/derivative.md | 63 +++++++ .../transformations/aggregates/difference.md | 94 +++++++++++ .../aggregates/histogramquantile.md | 83 +++++++++ .../transformations/aggregates/increase.md | 66 ++++++++ .../transformations/aggregates/integral.md | 48 ++++++ .../transformations/aggregates/mean.md | 42 +++++ .../transformations/aggregates/median.md | 97 +++++++++++ .../transformations/aggregates/pearsonr.md | 61 +++++++ .../transformations/aggregates/percentile.md | 97 +++++++++++ .../transformations/aggregates/skew.md | 36 ++++ .../transformations/aggregates/spread.md | 46 +++++ .../transformations/aggregates/stddev.md | 42 +++++ .../transformations/aggregates/sum.md | 42 +++++ .../flux/functions/transformations/columns.md | 57 +++++++ .../transformations/cumulativesum.md | 43 +++++ .../flux/functions/transformations/drop.md | 61 +++++++ .../functions/transformations/duplicate.md | 40 +++++ .../flux/functions/transformations/fill.md | 72 ++++++++ .../flux/functions/transformations/filter.md | 48 ++++++ .../flux/functions/transformations/group.md | 80 +++++++++ .../functions/transformations/histogram.md | 78 +++++++++ .../transformations/influxfieldsascols.md | 40 +++++ .../flux/functions/transformations/join.md | 133 +++++++++++++++ .../flux/functions/transformations/keep.md | 55 ++++++ .../flux/functions/transformations/keys.md | 57 +++++++ .../functions/transformations/keyvalues.md | 75 +++++++++ .../flux/functions/transformations/limit.md | 46 +++++ .../flux/functions/transformations/map.md | 72 ++++++++ .../flux/functions/transformations/pivot.md | 148 ++++++++++++++++ .../flux/functions/transformations/range.md | 70 ++++++++ .../flux/functions/transformations/rename.md | 57 +++++++ .../transformations/selectors/_index.md | 24 +++ .../transformations/selectors/bottom.md | 60 +++++++ .../transformations/selectors/distinct.md | 39 +++++ .../transformations/selectors/first.md | 34 ++++ .../selectors/highestaverage.md | 82 +++++++++ .../selectors/highestcurrent.md | 82 +++++++++ .../transformations/selectors/highestmax.md | 82 +++++++++ .../transformations/selectors/last.md | 34 ++++ .../selectors/lowestaverage.md | 83 +++++++++ .../selectors/lowestcurrent.md | 82 +++++++++ .../transformations/selectors/lowestmin.md | 83 +++++++++ .../transformations/selectors/max.md | 34 ++++ .../transformations/selectors/min.md | 34 ++++ .../transformations/selectors/sample.md | 49 ++++++ .../transformations/selectors/top.md | 54 ++++++ .../transformations/selectors/unique.md | 34 ++++ .../flux/functions/transformations/set.md | 38 +++++ .../flux/functions/transformations/shift.md | 48 ++++++ .../flux/functions/transformations/sort.md | 51 ++++++ .../functions/transformations/statecount.md | 51 ++++++ .../transformations/stateduration.md | 63 +++++++ .../type-conversions/_index.md | 14 ++ .../type-conversions/tobool.md | 35 ++++ .../type-conversions/toduration.md | 35 ++++ .../type-conversions/tofloat.md | 35 ++++ .../transformations/type-conversions/toint.md | 35 ++++ .../type-conversions/tostring.md | 35 ++++ .../type-conversions/totime.md | 35 ++++ .../type-conversions/touint.md | 35 ++++ .../flux/functions/transformations/union.md | 52 ++++++ .../flux/functions/transformations/window.md | 118 +++++++++++++ .../flux/language/built-ins/_index.md | 4 +- .../v2.0/reference/flux/language/packages.md | 2 +- layouts/shortcodes/function-list.html | 33 ++++ 91 files changed, 4918 insertions(+), 19 deletions(-) create mode 100644 content/v2.0/reference/flux/functions/_index.md create mode 100644 content/v2.0/reference/flux/functions/custom-functions.md create mode 100644 content/v2.0/reference/flux/functions/inputs/_index.md create mode 100644 content/v2.0/reference/flux/functions/inputs/buckets.md create mode 100644 content/v2.0/reference/flux/functions/inputs/from.md create mode 100644 content/v2.0/reference/flux/functions/inputs/fromcsv.md create mode 100644 content/v2.0/reference/flux/functions/misc/_index.md create mode 100644 content/v2.0/reference/flux/functions/misc/intervals.md create mode 100644 content/v2.0/reference/flux/functions/misc/linearbins.md create mode 100644 content/v2.0/reference/flux/functions/misc/logarithmicbins.md create mode 100644 content/v2.0/reference/flux/functions/misc/systemtime.md create mode 100644 content/v2.0/reference/flux/functions/outputs/_index.md create mode 100644 content/v2.0/reference/flux/functions/outputs/to.md create mode 100644 content/v2.0/reference/flux/functions/outputs/yield.md create mode 100644 content/v2.0/reference/flux/functions/tests/_index.md create mode 100644 content/v2.0/reference/flux/functions/tests/assertequals.md create mode 100644 content/v2.0/reference/flux/functions/transformations/_index.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/_index.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/aggregatewindow.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/count.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/cov.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/covariance.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/derivative.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/difference.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/histogramquantile.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/increase.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/integral.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/mean.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/median.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/pearsonr.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/percentile.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/skew.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/spread.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/stddev.md create mode 100644 content/v2.0/reference/flux/functions/transformations/aggregates/sum.md create mode 100644 content/v2.0/reference/flux/functions/transformations/columns.md create mode 100644 content/v2.0/reference/flux/functions/transformations/cumulativesum.md create mode 100644 content/v2.0/reference/flux/functions/transformations/drop.md create mode 100644 content/v2.0/reference/flux/functions/transformations/duplicate.md create mode 100644 content/v2.0/reference/flux/functions/transformations/fill.md create mode 100644 content/v2.0/reference/flux/functions/transformations/filter.md create mode 100644 content/v2.0/reference/flux/functions/transformations/group.md create mode 100644 content/v2.0/reference/flux/functions/transformations/histogram.md create mode 100644 content/v2.0/reference/flux/functions/transformations/influxfieldsascols.md create mode 100644 content/v2.0/reference/flux/functions/transformations/join.md create mode 100644 content/v2.0/reference/flux/functions/transformations/keep.md create mode 100644 content/v2.0/reference/flux/functions/transformations/keys.md create mode 100644 content/v2.0/reference/flux/functions/transformations/keyvalues.md create mode 100644 content/v2.0/reference/flux/functions/transformations/limit.md create mode 100644 content/v2.0/reference/flux/functions/transformations/map.md create mode 100644 content/v2.0/reference/flux/functions/transformations/pivot.md create mode 100644 content/v2.0/reference/flux/functions/transformations/range.md create mode 100644 content/v2.0/reference/flux/functions/transformations/rename.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/_index.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/bottom.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/distinct.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/first.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/highestaverage.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/highestcurrent.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/highestmax.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/last.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/lowestaverage.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/lowestcurrent.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/lowestmin.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/max.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/min.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/sample.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/top.md create mode 100644 content/v2.0/reference/flux/functions/transformations/selectors/unique.md create mode 100644 content/v2.0/reference/flux/functions/transformations/set.md create mode 100644 content/v2.0/reference/flux/functions/transformations/shift.md create mode 100644 content/v2.0/reference/flux/functions/transformations/sort.md create mode 100644 content/v2.0/reference/flux/functions/transformations/statecount.md create mode 100644 content/v2.0/reference/flux/functions/transformations/stateduration.md create mode 100644 content/v2.0/reference/flux/functions/transformations/type-conversions/_index.md create mode 100644 content/v2.0/reference/flux/functions/transformations/type-conversions/tobool.md create mode 100644 content/v2.0/reference/flux/functions/transformations/type-conversions/toduration.md create mode 100644 content/v2.0/reference/flux/functions/transformations/type-conversions/tofloat.md create mode 100644 content/v2.0/reference/flux/functions/transformations/type-conversions/toint.md create mode 100644 content/v2.0/reference/flux/functions/transformations/type-conversions/tostring.md create mode 100644 content/v2.0/reference/flux/functions/transformations/type-conversions/totime.md create mode 100644 content/v2.0/reference/flux/functions/transformations/type-conversions/touint.md create mode 100644 content/v2.0/reference/flux/functions/transformations/union.md create mode 100644 content/v2.0/reference/flux/functions/transformations/window.md create mode 100644 layouts/shortcodes/function-list.html diff --git a/content/v2.0/query-data/flux/get-started/_index.md b/content/v2.0/query-data/flux/get-started/_index.md index 714da0277..fafaaf34e 100644 --- a/content/v2.0/query-data/flux/get-started/_index.md +++ b/content/v2.0/query-data/flux/get-started/_index.md @@ -51,7 +51,7 @@ are unique to each row. ## Tools for working with Flux -You have multiple [options for writing and running Flux queries](/flux/v0.12/guides/executing-queries), +You have multiple [options for writing and running Flux queries](/v2.0/reference/flux/guides/executing-queries), but as you're getting started, we recommend using the following: ### 1. Data Explorer diff --git a/content/v2.0/query-data/flux/get-started/query-influxdb.md b/content/v2.0/query-data/flux/get-started/query-influxdb.md index b02e515b6..5f027ed17 100644 --- a/content/v2.0/query-data/flux/get-started/query-influxdb.md +++ b/content/v2.0/query-data/flux/get-started/query-influxdb.md @@ -17,8 +17,8 @@ Every Flux query needs the following: ## 1. Define your data source -Flux's [`from()`](#) function defines an InfluxDB data source. -It requires a [`bucket`](#) parameter. +Flux's [`from()`](/v2.0/reference/flux/functions/inputs/from) function defines an InfluxDB data source. +It requires a [`bucket`](/v2.0/reference/flux/functions/inputs/from#bucket) parameter. The following examples use `example-bucket` as the bucket name. ```js @@ -30,11 +30,11 @@ Flux requires a time range when querying time series data. "Unbounded" queries are very resource-intensive and as a protective measure, Flux will not query the database without a specified range. -Use the pipe-forward operator (`|>`) to pipe data from your data source into the [`range()`](/flux/v0.12/functions/transformations/range) +Use the pipe-forward operator (`|>`) to pipe data from your data source into the [`range()`](/v2.0/reference/flux/functions/transformations/range) function, which specifies a time range for your query. It accepts two properties: `start` and `stop`. -Ranges can be **relative** using negative [durations](/flux/v0.12/language/lexical-elements#duration-literals) -or **absolute** using [timestamps](/flux/v0.12/language/lexical-elements#date-and-time-literals). +Ranges can be **relative** using negative [durations](/v2.0/reference/flux/language/lexical-elements#duration-literals) +or **absolute** using [timestamps](/v2.0/reference/flux/language/lexical-elements#date-and-time-literals). ###### Example relative time ranges ```js diff --git a/content/v2.0/query-data/flux/get-started/syntax-basics.md b/content/v2.0/query-data/flux/get-started/syntax-basics.md index 0fd91cfcc..ad69de95c 100644 --- a/content/v2.0/query-data/flux/get-started/syntax-basics.md +++ b/content/v2.0/query-data/flux/get-started/syntax-basics.md @@ -183,7 +183,7 @@ topN = (tables=<-, n) => tables |> sort(desc: true) |> limit(n: n) {{% /code-tab-content %}} {{< /code-tabs-wrapper >}} -_More information about creating custom functions is available in the [Custom functions](/flux/v0.12/functions/custom-functions) documentation._ +_More information about creating custom functions is available in the [Custom functions](/v2.0/reference/flux/functions/custom-functions) documentation._ Using the `cpuUsageUser` data stream variable defined above, find the top five data points with the custom `topN` function and yield the results. diff --git a/content/v2.0/query-data/flux/get-started/transform-data.md b/content/v2.0/query-data/flux/get-started/transform-data.md index 68111916f..e0e9eba49 100644 --- a/content/v2.0/query-data/flux/get-started/transform-data.md +++ b/content/v2.0/query-data/flux/get-started/transform-data.md @@ -12,7 +12,7 @@ When [querying data from InfluxDB](/v2.0/query-data/flux/get-started/query-influ you often need to transform that data in some way. Common examples are aggregating data into averages, downsampling data, etc. -This guide demonstrates using [Flux functions](/flux/v0.12/functions) to transform your data. +This guide demonstrates using [Flux functions](/v2.0/reference/flux/functions) to transform your data. It walks through creating a Flux script that partitions data into windows of time, averages the `_value`s in each window, and outputs the averages as a new table. @@ -34,14 +34,14 @@ from(bucket:"example-bucket") ## Flux functions Flux provides a number of functions that perform specific operations, transformations, and tasks. -You can also [create custom functions](/flux/v0.12/functions/custom-functions) in your Flux queries. -_Functions are covered in detail in the [Flux functions](/flux/v0.12/functions) documentation._ +You can also [create custom functions](/v2.0/reference/flux/functions/custom-functions) in your Flux queries. +_Functions are covered in detail in the [Flux functions](/v2.0/reference/flux/functions) documentation._ A common type of function used when transforming data queried from InfluxDB is an aggregate function. Aggregate functions take a set of `_value`s in a table, aggregate them, and transform them into a new value. -This example uses the [`mean()` function](/flux/v0.12/functions/transformations/aggregates/mean) +This example uses the [`mean()` function](/v2.0/reference/flux/functions/transformations/aggregates/mean) to average values within each time window. {{% note %}} @@ -51,7 +51,7 @@ It's just good to understand the steps in the process. {{% /note %}} ## Window your data -Flux's [`window()` function](/flux/v0.12/functions/transformations/window) partitions records based on a time value. +Flux's [`window()` function](/v2.0/reference/flux/functions/transformations/window) partitions records based on a time value. Use the `every` parameter to define a duration of each window. For this example, window data in five minute intervals (`5m`). @@ -74,7 +74,7 @@ When visualized, each table is assigned a unique color. ## Aggregate windowed data Flux aggregate functions take the `_value`s in each table and aggregate them in some way. -Use the [`mean()` function](/flux/v0.12/functions/transformations/aggregates/mean) to average the `_value`s of each table. +Use the [`mean()` function](/v2.0/reference/flux/functions/transformations/aggregates/mean) to average the `_value`s of each table. ```js from(bucket:"example-bucket") @@ -100,7 +100,7 @@ Aggregate functions don't infer what time should be used for the aggregate value Therefore the `_time` column is dropped. A `_time` column is required in the [next operation](#unwindow-aggregate-tables). -To add one, use the [`duplicate()` function](/flux/v0.12/functions/transformations/duplicate) +To add one, use the [`duplicate()` function](/v2.0/reference/flux/functions/transformations/duplicate) to duplicate the `_stop` column as the `_time` column for each windowed table. ```js @@ -145,7 +145,7 @@ process helps to understand how data changes "shape" as it is passed through eac Flux provides (and allows you to create) "helper" functions that abstract many of these steps. The same operation performed in this guide can be accomplished using the -[`aggregateWindow()` function](/flux/v0.12/functions/transformations/aggregates/aggregatewindow). +[`aggregateWindow()` function](/v2.0/reference/flux/functions/transformations/aggregates/aggregatewindow). ```js from(bucket:"example-bucket") @@ -166,7 +166,7 @@ and your own custom functions, but this is a good introduction into the basic sy --- _For a deeper dive into windowing and aggregating data with example data output for each transformation, -view the [Windowing and aggregating data](/flux/v0.12/guides/windowing-aggregating) guide._ +view the [Windowing and aggregating data](/v2.0/reference/flux/guides/windowing-aggregating) guide._ --- diff --git a/content/v2.0/reference/flux/functions/_index.md b/content/v2.0/reference/flux/functions/_index.md new file mode 100644 index 000000000..6832835ca --- /dev/null +++ b/content/v2.0/reference/flux/functions/_index.md @@ -0,0 +1,29 @@ +--- +title: Flux functions +description: Flux functions allows you to retrieve, transform, process, and output data easily. +menu: + v2_0_ref: + name: Flux functions + parent: Flux query language + weight: 4 +--- + +Flux's functional syntax allows you to retrieve, transform, process, and output data easily. +There is a large library of built-in functions, but you can also create your own +custom functions to perform operations that suit your needs. + +## [Input functions](/v2.0/reference/flux/functions/inputs) +Input functions define or display information about data sources. + +## [Output functions](/v2.0/reference/flux/functions/outputs) +Output functions yield results or send data to a specified output. + +## [Transformation functions](/v2.0/reference/flux/functions/transformations) +Transformation functions transform or shape your data in specific ways. + +## [Miscellaneous functions](/v2.0/reference/flux/functions/misc) +Functions that serve miscellaneous purposes when writing Flux scripts. + +## [Custom functions](/v2.0/reference/flux/functions/custom-functions) +Flux's functional syntax allows for custom functions. +This guide walks through the basics of creating your own function. diff --git a/content/v2.0/reference/flux/functions/custom-functions.md b/content/v2.0/reference/flux/functions/custom-functions.md new file mode 100644 index 000000000..9365f72d9 --- /dev/null +++ b/content/v2.0/reference/flux/functions/custom-functions.md @@ -0,0 +1,133 @@ +--- +title: Create custom Flux functions +description: Create your own custom Flux functions to transform and manipulate data. +menu: + v2_0_ref: + name: Custom functions + parent: Flux functions + weight: 6 +--- + +Flux's functional syntax allows for custom functions. +This guide walks through the basics of creating your own function. + +## Function definition structure +The basic structure for defining functions in Flux is as follows: + +```js +// Basic function definition structure +functionName = (functionParameters) => functionOperations +``` + +##### `functionName` +The name used to call the function in your Flux script. + +##### `functionParameters` +A comma-separated list of parameters passed into the function and used in its operations. +[Parameter defaults](#define-parameter-defaults) can be defined for each. + +##### `functionOperations` +Operations and functions that manipulate the input into the desired output. + +#### Basic function examples + +###### Example square function +```js +// Function definition +square = (n) => n * n + +// Function usage +> square(n:3) +9 +``` + +###### Example multiply function +```js +// Function definition +multiply = (x, y) => x * y + +// Function usage +> multiply(x:2, y:15) +30 +``` + +## Functions that manipulate pipe-forwarded data +Most Flux functions manipulate data pipe-forwarded into the function. +In order for a custom function to process pipe-forwarded data, one of the function +parameters must capture the input tables using the `<-` pipe-receive expression. + +In the example below, the `tables` parameter is assigned to the `<-` expression, +which represents all data pipe-forwarded into the function. +`tables` is then pipe-forwarded into other operations in the function definition. + +```js +functionName = (tables=<-) => tables |> functionOperations +``` + +#### Pipe-forwardable function example + +###### Multiply row values by x +The example below defines a `multByX` function that multiplies the `_value` column +of each row in the input table by the `x` parameter. +It uses the [`map()` function](/v2.0/reference/flux/functions/transformations/map) to modify each `_value`. + +```js +// Function definition +multByX = (tables=<-, x) => + tables + |> map(fn: (r) => r._value * x) + +// Function usage +from(bucket: "telegraf/autogen") + |> range(start: -1m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> multByX(x:2.0) +``` + +## Define parameter defaults +To define parameters with default values, use the `=` assignment operator to assign +a default in your function definition: + +```js +functionName = (param1=defaultValue1, param2=defaultValue2) => functionOperation +``` + +Defaults are overridden by explicitly defining the parameter in the function call. + +#### Example functions with defaults + +###### Get the winner or the "winner" +The example below defines a `getWinner` function that returns the record with the highest +or lowest `_value` (winner versus "winner") depending on the `noSarcasm` parameter which defaults to `true`. +It uses the [`sort()` function](/v2.0/reference/flux/functions/transformations/sort) to sort records in either descending or ascending order. +It then uses the [`limit()` function](/v2.0/reference/flux/functions/transformations/limit) to return the first record from the sorted table. + +```js +// Function definition +getWinner = (tables=<-, noSarcasm:true) => + tables + |> sort(desc: noSarcasm) + |> limit(n:1) + +// Function usage +// Get the winner +from(bucket: "telegraf/autogen") + |> range(start: -1m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> getWinner() + +// Get the "winner" +from(bucket: "telegraf/autogen") + |> range(start: -1m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> getWinner(noSarcasm: false) +``` diff --git a/content/v2.0/reference/flux/functions/inputs/_index.md b/content/v2.0/reference/flux/functions/inputs/_index.md new file mode 100644 index 000000000..a12eb4d95 --- /dev/null +++ b/content/v2.0/reference/flux/functions/inputs/_index.md @@ -0,0 +1,14 @@ +--- +title: Flux input functions +description: Flux input functions define sources of data or or display information about data sources. +menu: + v2_0_ref: + parent: Flux functions + name: Inputs + weight: 1 +--- + +Flux input functions define sources of data or display information about data sources. +The following input functions are available: + +{{< function-list category="Inputs" menu="v2_0_ref" >}} diff --git a/content/v2.0/reference/flux/functions/inputs/buckets.md b/content/v2.0/reference/flux/functions/inputs/buckets.md new file mode 100644 index 000000000..5c2e180af --- /dev/null +++ b/content/v2.0/reference/flux/functions/inputs/buckets.md @@ -0,0 +1,22 @@ +--- +title: buckets() function +description: The buckets() function returns a list of buckets in the organization. +menu: + v2_0_ref: + name: buckets + parent: Inputs + weight: 1 +--- + +The `buckets()` function returns a list of buckets in the organization. + +_**Function type:** Input_ + +```js +buckets() +``` + +
+ +##### Related InfluxQL functions and statements: +[SHOW DATABASES](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-databases) diff --git a/content/v2.0/reference/flux/functions/inputs/from.md b/content/v2.0/reference/flux/functions/inputs/from.md new file mode 100644 index 000000000..86ad34c7a --- /dev/null +++ b/content/v2.0/reference/flux/functions/inputs/from.md @@ -0,0 +1,50 @@ +--- +title: from() function +description: The from() function retrieves data from an InfluxDB data source. +menu: + v2_0_ref: + name: from + parent: Inputs + weight: 1 +--- + +The `from()` function retrieves data from an InfluxDB data source. +It returns a stream of tables from the specified [bucket](#parameters). +Each unique series is contained within its own table. +Each record in the table represents a single point in the series. + +_**Function type:** Input_ +_**Output data type:** Object_ + +```js +from(bucket: "telegraf/autogen") + +// OR + +from(bucketID: "0261d8287f4d6000") +``` + +## Parameters + +### bucket +The name of the bucket to query. + +_**Data type:** String_ + +### bucketID +The string-encoded ID of the bucket to query. + +_**Data type:** String_ + +## Examples +```js +from(bucket: "telegraf/autogen") +``` +```js +from(bucketID: "0261d8287f4d6000") +``` + +
+ +##### Related InfluxQL functions and statements: +[FROM](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#from-clause) diff --git a/content/v2.0/reference/flux/functions/inputs/fromcsv.md b/content/v2.0/reference/flux/functions/inputs/fromcsv.md new file mode 100644 index 000000000..95e376020 --- /dev/null +++ b/content/v2.0/reference/flux/functions/inputs/fromcsv.md @@ -0,0 +1,64 @@ +--- +title: fromCSV() function +description: The fromCSV() function retrieves data from a CSV data source. +menu: + v2_0_ref: + name: fromCSV + parent: Inputs + weight: 1 +--- + +The `fromCSV()` function retrieves data from a comma-separated value (CSV) data source. +It returns a stream of tables. +Each unique series is contained within its own table. +Each record in the table represents a single point in the series. + +_**Function type:** Input_ +_**Output data type:** Object_ + +```js +from(file: "/path/to/data-file.csv") + +// OR + +from(csv: csvData) +``` + +## Parameters + +### file +The file path of the CSV file to query. +The path can be absolute or relative. +If relative, it is relative to the working directory of the `influxd` process. + +_**Data type:** String_ + +### csv +Raw CSV-formatted text. + +{{% note %}} +CSV data must be in the CSV format produced by the Flux HTTP response standard. +See the [Flux technical specification](https://github.com/influxdata/flux/blob/master/docs/SPEC.md#csv) +for information about this format. +{{% /note %}} + +_**Data type:** String_ + +## Examples + +### Query CSV data from a file +```js +from(file: "/path/to/data-file.csv") +``` + +### Query raw CSV-formatted text +```js +csvData = " +result,table,_start,_stop,_time,region,host,_value +mean,0,2018-05-08T20:50:00Z,2018-05-08T20:51:00Z,2018-05-08T20:50:00Z,east,A,15.43 +mean,0,2018-05-08T20:50:00Z,2018-05-08T20:51:00Z,2018-05-08T20:50:20Z,east,B,59.25 +mean,0,2018-05-08T20:50:00Z,2018-05-08T20:51:00Z,2018-05-08T20:50:40Z,east,C,52.62 +" + +from(csv: csvData) +``` diff --git a/content/v2.0/reference/flux/functions/misc/_index.md b/content/v2.0/reference/flux/functions/misc/_index.md new file mode 100644 index 000000000..cf285784f --- /dev/null +++ b/content/v2.0/reference/flux/functions/misc/_index.md @@ -0,0 +1,15 @@ +--- +title: Flux miscellaneous functions +description: Flux provides miscellaneous functions that serve purposes other than retrieving, transforming, or outputting data. +menu: + v2_0_ref: + parent: Flux functions + name: Miscellaneous + weight: 5 +--- + +Flux functions primarily retrieve, shape and transform, then output data, however +there are functions available that serve other purposes. +The following functions are are available but don't fit within other function categories: + +{{< function-list category="Miscellaneous" menu="v2_0_ref" >}} diff --git a/content/v2.0/reference/flux/functions/misc/intervals.md b/content/v2.0/reference/flux/functions/misc/intervals.md new file mode 100644 index 000000000..db6f039a0 --- /dev/null +++ b/content/v2.0/reference/flux/functions/misc/intervals.md @@ -0,0 +1,154 @@ +--- +title: intervals() function +description: The intervals() function generates a set of time intervals over a range of time. +menu: + v2_0_ref: + name: intervals + parent: Miscellaneous + weight: 1 +--- + +The `intervals()` function generates a set of time intervals over a range of time. + +An interval is an object with `start` and `stop` properties that correspond to the inclusive start and exclusive stop times of the time interval. +The return value of intervals is another function that accepts start and stop time parameters and returns an interval generator. +The generator is then used to produce the set of intervals. +The set of intervals includes all intervals that intersect with the initial range of time. + +{{% note %}} +The `intervals()` function is designed to be used with the intervals parameter of the [`window()` function](/v2.0/reference/flux/functions/transformations/window). +{{% /note %}} + +_**Function type:** Miscellaneous_ +_**Output data type:** Object_ + +```js +intervals() +``` + +## Parameters + +### every +The duration between starts of each of the intervals. +The Nth interval start time is the initial start time plus the offset plus an Nth multiple of the every parameter. +Defaults to the value of the `period` duration. + +_**Data type:** Duration_ + +### period +The length of each interval. +Each interval's stop time is equal to the interval start time plus the period duration. +It can be negative, indicating the start and stop boundaries are reversed. +Defaults to the value of the `every` duration. + +_**Data type:** Duration_ + +### offset +The offset duration relative to the location offset. +It can be negative, indicating that the offset goes backwards in time. +Defaults to `0h`. + +_**Data type:** Duration_ + +### filter +A function that accepts an interval object and returns a boolean value. +Each potential interval is passed to the filter function. +When the function returns false, that interval is excluded from the set of intervals. +Defaults to include all intervals. + +_**Data type:** Function_ + +## Examples + +##### Basic intervals +```js +// 1 hour intervals +intervals(every:1h) + +// 2 hour long intervals every 1 hour +intervals(every:1h, period:2h) + +// 2 hour long intervals every 1 hour starting at 30m past the hour +intervals(every:1h, period:2h, offset:30m) + +// 1 week intervals starting on Monday (by default weeks start on Sunday) +intervals(every:1w, offset:1d) + +// the hour from 11PM - 12AM every night +intervals(every:1d, period:-1h) + +// the last day of each month +intervals(every:1mo, period:-1d) +``` + +##### Using a predicate +```js +// 1 day intervals excluding weekends +intervals( + every:1d, + filter: (interval) => !(weekday(time: interval.start) in [Sunday, Saturday]), +) + +// Work hours from 9AM - 5PM on work days. +intervals( + every:1d, + period:8h, + offset:9h, + filter:(interval) => !(weekday(time: interval.start) in [Sunday, Saturday]), +) +``` + +##### Using known start and stop dates +```js +// Every hour for six hours on Sep 5th. +intervals(every:1h)(start:2018-09-05T00:00:00-07:00, stop: 2018-09-05T06:00:00-07:00) + +// Generates +// [2018-09-05T00:00:00-07:00, 2018-09-05T01:00:00-07:00) +// [2018-09-05T01:00:00-07:00, 2018-09-05T02:00:00-07:00) +// [2018-09-05T02:00:00-07:00, 2018-09-05T03:00:00-07:00) +// [2018-09-05T03:00:00-07:00, 2018-09-05T04:00:00-07:00) +// [2018-09-05T04:00:00-07:00, 2018-09-05T05:00:00-07:00) +// [2018-09-05T05:00:00-07:00, 2018-09-05T06:00:00-07:00) + +// Every hour for six hours with 1h30m periods on Sep 5th +intervals(every:1h, period:1h30m)(start:2018-09-05T00:00:00-07:00, stop: 2018-09-05T06:00:00-07:00) + +// Generates +// [2018-09-05T00:00:00-07:00, 2018-09-05T01:30:00-07:00) +// [2018-09-05T01:00:00-07:00, 2018-09-05T02:30:00-07:00) +// [2018-09-05T02:00:00-07:00, 2018-09-05T03:30:00-07:00) +// [2018-09-05T03:00:00-07:00, 2018-09-05T04:30:00-07:00) +// [2018-09-05T04:00:00-07:00, 2018-09-05T05:30:00-07:00) +// [2018-09-05T05:00:00-07:00, 2018-09-05T06:30:00-07:00) + +// Every hour for six hours using the previous hour on Sep 5th +intervals(every:1h, period:-1h)(start:2018-09-05T12:00:00-07:00, stop: 2018-09-05T18:00:00-07:00) + +// Generates +// [2018-09-05T11:00:00-07:00, 2018-09-05T12:00:00-07:00) +// [2018-09-05T12:00:00-07:00, 2018-09-05T13:00:00-07:00) +// [2018-09-05T13:00:00-07:00, 2018-09-05T14:00:00-07:00) +// [2018-09-05T14:00:00-07:00, 2018-09-05T15:00:00-07:00) +// [2018-09-05T15:00:00-07:00, 2018-09-05T16:00:00-07:00) +// [2018-09-05T16:00:00-07:00, 2018-09-05T17:00:00-07:00) +// [2018-09-05T17:00:00-07:00, 2018-09-05T18:00:00-07:00) + +// Every month for 4 months starting on Jan 1st +intervals(every:1mo)(start:2018-01-01, stop: 2018-05-01) + +// Generates +// [2018-01-01, 2018-02-01) +// [2018-02-01, 2018-03-01) +// [2018-03-01, 2018-04-01) +// [2018-04-01, 2018-05-01) + +// Every month for 4 months starting on Jan 15th +intervals(every:1mo)(start:2018-01-15, stop: 2018-05-15) + +// Generates +// [2018-01-15, 2018-02-15) +// [2018-02-15, 2018-03-15) +// [2018-03-15, 2018-04-15) +// [2018-04-15, 2018-05-15) +``` diff --git a/content/v2.0/reference/flux/functions/misc/linearbins.md b/content/v2.0/reference/flux/functions/misc/linearbins.md new file mode 100644 index 000000000..7b8f22859 --- /dev/null +++ b/content/v2.0/reference/flux/functions/misc/linearbins.md @@ -0,0 +1,51 @@ +--- +title: linearBins() function +description: The linearBins() function generates a list of linearly separated floats. +menu: + v2_0_ref: + name: linearBins + parent: Miscellaneous + weight: 1 +--- + +The `linearBins()` function generates a list of linearly separated floats. +It is a helper function meant to generate bin bounds for the +[`histogram()` function](/v2.0/reference/flux/functions/transformations/histogram). + +_**Function type:** Miscellaneous_ +_**Output data type:** Array of floats_ + +```js +linearBins(start: 0.0, width: 5.0, count: 20, infinity: true) +``` + +## Parameters + +### start +The first value in the returned list. + +_**Data type:** Float_ + +### width +The distance between subsequent bin values. + +_**Data type:** Float_ + +### count +The number of bins to create. + +_**Data type:** Integer_ + +### infinity +When `true`, adds an additional bin with a value of positive infinity. +Defaults to `true`. + +_**Data type:** Boolean_ + +## Examples + +```js +linearBins(start: 0.0, width: 10.0, count: 10) + +// Generated list: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, +Inf] +``` diff --git a/content/v2.0/reference/flux/functions/misc/logarithmicbins.md b/content/v2.0/reference/flux/functions/misc/logarithmicbins.md new file mode 100644 index 000000000..37ebb3034 --- /dev/null +++ b/content/v2.0/reference/flux/functions/misc/logarithmicbins.md @@ -0,0 +1,50 @@ +--- +title: logarithmicBins() function +description: The logarithmicBins() function generates a list of exponentially separated floats. +menu: + v2_0_ref: + name: logarithmicBins + parent: Miscellaneous + weight: 1 +--- + +The `logarithmicBins()` function generates a list of exponentially separated floats. +It is a helper function meant to generate bin bounds for the +[`histogram()` function](/v2.0/reference/flux/functions/transformations/histogram). + +_**Function type:** Miscellaneous_ +_**Output data type:** Array of floats_ + +```js +logarithmicBins(start:1.0, factor: 2.0, count: 10, infinity: true) +``` + +## Parameters + +### start +The first value in the returned bin list. + +_**Data type:** Float_ + +### factor +The multiplier applied to each subsequent bin. + +_**Data type:** Float_ + +### count +The number of bins to create. + +_**Data type:** Integer_ + +### infinity +When `true`, adds an additional bin with a value of positive infinity. +Defaults to `true`. + +_**Data type:** Boolean_ + +## Examples +```js +logarithmicBins(start: 1.0, factor: 2.0, count: 10, infinty: true) + +// Generated list: [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, +Inf] +``` diff --git a/content/v2.0/reference/flux/functions/misc/systemtime.md b/content/v2.0/reference/flux/functions/misc/systemtime.md new file mode 100644 index 000000000..bce87b251 --- /dev/null +++ b/content/v2.0/reference/flux/functions/misc/systemtime.md @@ -0,0 +1,23 @@ +--- +title: systemTime() function +description: The systemTime() function returns the current system time. +menu: + v2_0_ref: + name: systemTime + parent: Miscellaneous + weight: 1 +--- + +The `systemTime()` function returns the current system time. + +_**Function type:** Date/Time_ +_**Output data type:** Timestamp_ + +```js +systemTime() +``` + +## Examples +```js +offsetTime = (offset) => systemTime() |> shift(shift: offset) +``` diff --git a/content/v2.0/reference/flux/functions/outputs/_index.md b/content/v2.0/reference/flux/functions/outputs/_index.md new file mode 100644 index 000000000..96520cf14 --- /dev/null +++ b/content/v2.0/reference/flux/functions/outputs/_index.md @@ -0,0 +1,14 @@ +--- +title: Flux output functions +description: Flux output functions yield results or send data to a specified output destination. +menu: + v2_0_ref: + parent: Flux functions + name: Outputs + weight: 2 +--- + +Flux output functions yield results or send data to a specified output destination. +The following output functions are are available: + +{{< function-list category="Outputs" menu="v2_0_ref" >}} diff --git a/content/v2.0/reference/flux/functions/outputs/to.md b/content/v2.0/reference/flux/functions/outputs/to.md new file mode 100644 index 000000000..b4dc9be12 --- /dev/null +++ b/content/v2.0/reference/flux/functions/outputs/to.md @@ -0,0 +1,158 @@ +--- +title: to() function +description: The to() function writes data to an InfluxDB v2.0 bucket. +menu: + v2_0_ref: + name: to + parent: Outputs + weight: 1 +--- + +The `to()` function writes data to an **InfluxDB v2.0** bucket. + +_**Function type:** Output_ +_**Output data type:** Object_ + +```js +to( + bucket: "my-bucket", + org: "my-org", + host: "http://example.com:8086", + token: "xxxxxx", + timeColumn: "_time", + tagColumns: ["tag1", "tag2", "tag3"], + fieldFn: (r) => ({ [r._field]: r._value }) +) + +// OR + +to( + bucketID: "1234567890", + orgID: "0987654321", + host: "http://example.com:8086", + token: "xxxxxx", + timeColumn: "_time", + tagColumns: ["tag1", "tag2", "tag3"], + fieldFn: (r) => ({ [r._field]: r._value }) +) +``` + +## Parameters +{{% note %}} +`bucket` OR `bucketID` is **required**. +{{% /note %}} + +### bucket +The bucket to which data is written. Mutually exclusive with `bucketID`. + +_**Data type:** String_ + +### bucketID +The ID of the bucket to which data is written. Mutually exclusive with `bucket`. + +_**Data type:** String_ + +### org +The organization name of the specified [`bucket`](#bucket). +Only required when writing to a remote host. +Mutually exclusive with `orgID` + +_**Data type:** String_ + +{{% note %}} +Specify either an `org` or an `orgID`, but not both. +{{% /note %}} + +### orgID +The organization ID of the specified [`bucket`](#bucket). +Only required when writing to a remote host. +Mutually exclusive with `org`. + +_**Data type:** String_ + +### host +The remote InfluxDB host to which to write. +_If specified, a `token` is required._ + +_**Data type:** String_ + +### token +The authorization token to use when writing to a remote host. +_Required when a `host` is specified._ + +_**Data type:** String_ + +### timeColumn +The time column of the output. +Default is `"_time"`. + +_**Data type:** String_ + +### tagColumns +The tag columns of the output. +Defaults to all columns with type `string`, excluding all value columns and the `_field` column if present. + +_**Data type:** Array of strings_ + +### fieldFn +Function that takes a record from the input table and returns an object. +For each record from the input table, `fieldFn` returns an object that maps output the field key to the output value. +Default is `(r) => ({ [r._field]: r._value })` + +_**Data type:** Function_ +_**Output data type:** Object_ + +## Examples + +### Default to() operation +Given the following table: + +| _time | _start | _stop | _measurement | _field | _value | +| ----- | ------ | ----- | ------------ | ------ | ------ | +| 0005 | 0000 | 0009 | "a" | "temp" | 100.1 | +| 0006 | 0000 | 0009 | "a" | "temp" | 99.3 | +| 0007 | 0000 | 0009 | "a" | "temp" | 99.9 | + +The default `to` operation: + +```js +// ... +|> to(bucket:"my-bucket", org:"my-org") +``` + +is equivalent to writing the above data using the following line protocol: + +``` +_measurement=a temp=100.1 0005 +_measurement=a temp=99.3 0006 +_measurement=a temp=99.9 0007 +``` + +### Custom to() operation +The `to()` functions default operation can be overridden. For example, given the following table: + +| _time | _start | _stop | tag1 | tag2 | hum | temp | +| ----- | ------ | ----- | ---- | ---- | ---- | ----- | +| 0005 | 0000 | 0009 | "a" | "b" | 55.3 | 100.1 | +| 0006 | 0000 | 0009 | "a" | "b" | 55.4 | 99.3 | +| 0007 | 0000 | 0009 | "a" | "b" | 55.5 | 99.9 | + +The operation: + +```js +// ... +|> to(bucket:"my-bucket", org:"my-org", tagColumns:["tag1"], fieldFn: (r) => return {"hum": r.hum, "temp": r.temp}) +``` + +is equivalent to writing the above data using the following line protocol: + +``` +_tag1=a hum=55.3,temp=100.1 0005 +_tag1=a hum=55.4,temp=99.3 0006 +_tag1=a hum=55.5,temp=99.9 0007 +``` + +
+ +##### Related InfluxQL functions and statements: +[SELECT INTO](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#the-into-clause) diff --git a/content/v2.0/reference/flux/functions/outputs/yield.md b/content/v2.0/reference/flux/functions/outputs/yield.md new file mode 100644 index 000000000..d9882c3d2 --- /dev/null +++ b/content/v2.0/reference/flux/functions/outputs/yield.md @@ -0,0 +1,45 @@ +--- +title: yield() function +description: The yield() function indicates the input tables received should be delivered as a result of the query. +menu: + v2_0_ref: + name: yield + parent: Outputs + weight: 1 +--- + +The `yield()` function indicates the input tables received should be delivered as a result of the query. +Yield outputs the input stream unmodified. +A query may have multiple results, each identified by the name provided to the `yield()` function. + +_**Function type:** Output_ +_**Output data type:** Object_ + +```js +yield(name: "custom-name") +``` + +{{% note %}} +`yield()` is implicit for queries that do only one thing and are only needed when using multiple sources in a query. +With multiple sources, `yield()` is required to specify what is returned, and what name to give it. +{{% /note %}} + +## Parameters + +### name +A unique name for the yielded results. +Defaults to `"_results"`. + +_**Data type:** String_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> yield(name: "1") +``` + +
+ +##### Related InfluxQL functions and statements: +[SELECT AS](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#the-basic-select-statement) diff --git a/content/v2.0/reference/flux/functions/tests/_index.md b/content/v2.0/reference/flux/functions/tests/_index.md new file mode 100644 index 000000000..1850a247b --- /dev/null +++ b/content/v2.0/reference/flux/functions/tests/_index.md @@ -0,0 +1,14 @@ +--- +title: Flux testing functions +description: Flux testing functions test piped-forward data in specific ways and return errors if the tests fail. +menu: + v2_0_ref: + name: Tests + parent: Flux functions + weight: 5 +--- + +Flux testing functions test piped-forward data in specific ways and return errors if the tests fail. +The following testing functions are available: + +{{< function-list category="Tests" menu="v2_0_ref" >}} diff --git a/content/v2.0/reference/flux/functions/tests/assertequals.md b/content/v2.0/reference/flux/functions/tests/assertequals.md new file mode 100644 index 000000000..96c5d0387 --- /dev/null +++ b/content/v2.0/reference/flux/functions/tests/assertequals.md @@ -0,0 +1,67 @@ +--- +title: assertEquals() function +description: The assertEquals() function tests whether two streams have identical data. +menu: + v2_0_ref: + name: assertEquals + parent: Tests + weight: 1 +--- + +The `assertEquals()` function tests whether two streams have identical data. +If equal, the function outputs the tested data stream unchanged. +If unequal, the function outputs an error. + +_**Function type:** Test_ + +```js +assertEquals( + name: "streamEquality", + got: got, + want: want +) +``` + +_The `assertEquals()` function can be used to perform in-line tests in a query._ + +## Parameters + +## name +Unique name given to the assertion. + +_**Data type:** String_ + +## got +The stream containing data to test. +Defaults to data piped-forward from another function (`<-`). + +_**Data type:** Object_ + +## want +The stream that contains the expected data to test against. + +_**Data type:** Object_ + + +## Examples + +##### Assert of separate streams +```js +want = from(bucket: "backup-telegraf/autogen") + |> range(start: -5m) + +got = from(bucket: "telegraf/autogen") + |> range(start: -5m) + +assertEquals(got: got, want: want) +``` + +##### Inline assertion +```js +want = from(bucket: "backup-telegraf/autogen") + |> range(start: -5m) + +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> assertEquals(want: want) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/_index.md b/content/v2.0/reference/flux/functions/transformations/_index.md new file mode 100644 index 000000000..dd3996c5b --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/_index.md @@ -0,0 +1,27 @@ +--- +title: Flux transformation functions +description: Flux transformation functions transform and shape your data in specific ways. +menu: + v2_0_ref: + parent: Flux functions + name: Transformations + weight: 3 +--- + +Flux transformation functions transform or shape your data in specific ways. +There are different types of transformations categorized below: + +## [Aggregates](/v2.0/reference/flux/functions/transformations/aggregates) +Aggregate functions take values from an input table and aggregate them in some way. +The output table contains is a single row with the aggregated value. + +## [Selectors](/v2.0/reference/flux/functions/transformations/selectors) +Selector functions return one or more records based on function logic. +The output table is different than the input table, but individual row values are not. + +## [Type conversions](/v2.0/reference/flux/functions/transformations/type-conversions) +Type conversion functions convert the `_value` column of the input table into a specific data type. + +## Generic transformations + +{{< function-list category="Transformations" menu="v2_0_ref" >}} diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/_index.md b/content/v2.0/reference/flux/functions/transformations/aggregates/_index.md new file mode 100644 index 000000000..1bde2bc48 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/_index.md @@ -0,0 +1,47 @@ +--- +title: Flux aggregate functions +description: Flux aggregate functions take values from an input table and aggregate them in some way. +menu: + v2_0_ref: + parent: Transformations + name: Aggregates + weight: 1 +--- + +Flux aggregate functions take values from an input table and aggregate them in some way. +The output table contains is a single row with the aggregated value. + +Aggregate operations output a table for every input table they receive. +A list of columns to aggregate must be provided to the operation. +The aggregate function is applied to each column in isolation. +Any output table will have the following properties: + +- It always contains a single record. +- It will have the same group key as the input table. +- It will contain a column for each provided aggregate column. + The column label will be the same as the input table. + The type of the column depends on the specific aggregate operation. + The value of the column will be `null` if the input table is empty or the input column has only `null` values. +- It will not have a `_time` column. + +### aggregateWindow helper function +The [`aggregateWindow()` function](/v2.0/reference/flux/functions/transformations/aggregates/aggregatewindow) +does most of the work needed when aggregating data. +It windows and aggregates the data, then combines windowed tables into a single output table. + +### Aggregate functions +The following aggregate functions are available: + +{{< function-list category="Aggregates" menu="v2_0_ref" >}} + +### Aggregate selectors +The following functions are both aggregates and selectors. +Each returns `n` values after performing an aggregate operation. +They are categorized as selector functions in this documentation: + +- [highestAverage](/v2.0/reference/flux/functions/transformations/selectors/highestaverage) +- [highestCurrent](/v2.0/reference/flux/functions/transformations/selectors/highestcurrent) +- [highestMax](/v2.0/reference/flux/functions/transformations/selectors/highestmax) +- [lowestAverage](/v2.0/reference/flux/functions/transformations/selectors/lowestaverage) +- [lowestCurrent](/v2.0/reference/flux/functions/transformations/selectors/lowestcurrent) +- [lowestMin](/v2.0/reference/flux/functions/transformations/selectors/lowestmin) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/aggregatewindow.md b/content/v2.0/reference/flux/functions/transformations/aggregates/aggregatewindow.md new file mode 100644 index 000000000..840beb6b1 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/aggregatewindow.md @@ -0,0 +1,112 @@ +--- +title: aggregateWindow() function +description: The aggregateWindow() function applies an aggregate function to fixed windows of time. +menu: + v2_0_ref: + name: aggregateWindow + parent: Aggregates + weight: 1 +--- + +The `aggregateWindow()` function applies an aggregate function to fixed windows of time. + +_**Function type:** Aggregate_ + +```js +aggregateWindow( + every: 1m, + fn: mean, + columns: ["_value"], + timeColumn: "_stop", + timeDst: "_time", + createEmpty: true +) +``` + +As data is windowed into separate tables and aggregated, the `_time` column is dropped from each group key. +This helper copies the timestamp from a remaining column into the `_time` column. +View the [function definition](#function-definition). + +## Parameters + +### every +The duration of windows. + +_**Data type:** Duration_ + +### fn +The aggregate function used in the operation. + +_**Data type:** Function_ + +### columns +List of columns on which to operate. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +### timeColumn +The time column from which time is copied for the aggregate record. +Defaults to `"_stop"`. + +_**Data type:** String_ + +### timeDst +The "time destination" column to which time is copied for the aggregate record. +Defaults to `"_time"`. + +_**Data type:** String_ + +### createEmpty +For windows without data, this will create an empty window and fill +it with a `null` aggregate value. +Defaults to `true`. + +_**Data type:** Boolean_ + +## Examples + +###### Using an aggregate function with default parameters +```js +from(bucket: "telegraf/autogen") + |> range(start: 1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent") + |> aggregateWindow( + every: 5m, + fn: mean + ) +``` +####### Specifying parameters of the aggregate function +To use `aggregateWindow()` aggregate functions that don't provide defaults for required parameters, +for the `fn` parameter, define an anonymous function with `columns` and `tables` parameters +that pipe-forwards tables into the aggregate function with all required parameters defined: + +```js +from(bucket: "telegraf/autogen") + |> range(start: 1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent") + |> aggregateWindow( + every: 5m, + fn: (columns, tables=<-) => tables |> percentile(percentile: 0.99, columns:columns) + ) +``` + +## Function definition +```js +aggregateWindow = (every, fn, columns=["_value"], timeColumn="_stop", timeDst="_time", tables=<-) => + tables + |> window(every:every) + |> fn(columns:columns) + |> duplicate(column:timeColumn, as:timeDst) + |> window(every:inf, timeColumn:timeDst) +``` + +
+ +##### Related InfluxQL functions and statements: +[InfluxQL aggregate functions](https://docs.influxdata.com/influxdb/latest/query_language/functions/#aggregations) +[GROUP BY time()](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#the-group-by-clause) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/count.md b/content/v2.0/reference/flux/functions/transformations/aggregates/count.md new file mode 100644 index 000000000..05887f925 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/count.md @@ -0,0 +1,45 @@ +--- +title: count() function +description: The count() function outputs the number of non-null records in each aggregated column. +menu: + v2_0_ref: + name: count + parent: Aggregates + weight: 1 +--- + +The `count()` function outputs the number of records in each aggregated column. +It counts both null and non-null records. + +_**Function type:** Aggregate_ +_**Output data type:** Integer_ + +```js +count(columns: ["_value"]) +``` + +## Parameters + +### columns +A list of columns on which to operate +Defaults to `["_value"]`. + +_**Data type: Array of strings**_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> count() +``` + +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> count(columns: ["_value"]) +``` + +
+ +##### Related InfluxQL functions and statements: +[COUNT()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#count) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/cov.md b/content/v2.0/reference/flux/functions/transformations/aggregates/cov.md new file mode 100644 index 000000000..064e86adf --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/cov.md @@ -0,0 +1,67 @@ +--- +title: cov() function +description: The cov() function computes the covariance between two streams by first joining the streams, then performing the covariance operation. +menu: + v2_0_ref: + name: cov + parent: Aggregates + weight: 1 +--- + +The `cov()` function computes the covariance between two streams by first joining the streams, +then performing the covariance operation. + +_**Function type:** Aggregate +_**Output data type:** Float_ + +```js +cov(x: table1, y: table2, on: ["_time", "_field"], pearsonr: false) +``` + +## Parameters + +### x +One input stream used to calculate the covariance. + +_**Data type:** Object_ + +### y +The other input table used to calculate the covariance. + +_**Data type:** Object_ + +### on +The list of columns on which to join. + +_**Data type:** Array of strings_ + +### pearsonr +Indicates whether the result should be normalized to be the Pearson R coefficient. + +_**Data type:** Boolean_ + + +## Examples + +```js +table1 = from(bucket: "telegraf/autogen") + |> range(start: -15m) + |> filter(fn: (r) => + r._measurement == "measurement_1" + ) + +table2 = from(bucket: "telegraf/autogen") + |> range(start: -15m) + |> filter(fn: (r) => + r._measurement == "measurement_2" + ) + +cov(x: table1, y: table2, on: ["_time", "_field"]) +``` + +## Function definition +```js +cov = (x,y,on,pearsonr=false) => + join( tables:{x:x, y:y}, on:on ) + |> covariance(pearsonr:pearsonr, columns:["_value_x","_value_y"]) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/covariance.md b/content/v2.0/reference/flux/functions/transformations/aggregates/covariance.md new file mode 100644 index 000000000..18124f6ba --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/covariance.md @@ -0,0 +1,46 @@ +--- +title: covariance() function +description: The covariance() function computes the covariance between two columns. +menu: + v2_0_ref: + name: covariance + parent: Aggregates + weight: 1 +--- + +The `covariance()` function computes the covariance between two columns. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +covariance(columns: ["column_x", "column_y"], pearsonr: false, valueDst: "_value") +``` + +## Parameters + +### columns +A list of columns on which to operate. + +_**Data type:** Array of strings_ + +{{% note %}} +Exactly two columns must be provided to the `columns` property. +{{% /note %}} + +### pearsonr +Indicates whether the result should be normalized to be the Pearson R coefficient. + +_**Data type:** Boolean_ + +### valueDst +The column into which the result will be placed. Defaults to `"_value"`. + +_**Data type:** String_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start:-5m) + |> covariance(columns: ["x", "y"]) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/derivative.md b/content/v2.0/reference/flux/functions/transformations/aggregates/derivative.md new file mode 100644 index 000000000..b7be0572d --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/derivative.md @@ -0,0 +1,63 @@ +--- +title: derivative() function +description: The derivative() function computes the rate of change per unit of time between subsequent non-null records. +menu: + v2_0_ref: + name: derivative + parent: Aggregates + weight: 1 +--- + +The `derivative()` function computes the rate of change per [`unit`](#unit) of time between subsequent non-null records. +It assumes rows are ordered by the `_time` column. +The output table schema will be the same as the input table. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +derivative( + unit: 1s, + nonNegative: false, + columns: ["_value"], + timeSrc: "_time" +) +``` + +## Parameters + +### unit +The time duration used when creating the derivative. +Defaults to `1s`. + +_**Data type:** Duration_ + +### nonNegative +Indicates if the derivative is allowed to be negative. +When set to `true`, if a value is less than the previous value, it is assumed the previous value should have been a zero. + +_**Data type:** Boolean_ + +### columns +A list of columns on which to compute the derivative. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +### timeSrc +The column containing time values. +Defaults to `"_time"`. + +_**Data type:** String_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> derivative(unit: 1s, nonNegative: true) +``` + +
+ +##### Related InfluxQL functions and statements: +[DERIVATIVE()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#derivative) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/difference.md b/content/v2.0/reference/flux/functions/transformations/aggregates/difference.md new file mode 100644 index 000000000..bfd2b9bd2 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/difference.md @@ -0,0 +1,94 @@ +--- +title: difference() function +description: The difference() function computes the difference between subsequent non-null records. +menu: + v2_0_ref: + name: difference + parent: Aggregates + weight: 1 +--- + +The `difference()` function computes the difference between subsequent records. +Every user-specified column of numeric type is subtracted while others are kept intact. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +difference(nonNegative: false, columns: ["_value"]) +``` + +## Parameters + +### nonNegative +Indicates if the difference is allowed to be negative. +When set to `true`, if a value is less than the previous value, it is assumed the previous value should have been a zero. + +_**Data type:** Boolean_ + +### columns +A list of columns on which to compute the difference. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +## Subtraction rules for numeric types +- The difference between two non-null values is their algebraic difference; + or `null`, if the result is negative and `nonNegative: true`; +- `null` minus some value is always `null`; +- Some value `v` minus `null` is `v` minus the last non-null value seen before `v`; + or `null` if `v` is the first non-null value seen. + + +## Examples + +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> difference() +``` +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> difference(nonNegative: true) +``` + +### Example data transformation + +###### Input table +| _time | A | B | C | tag | +|:-----:|:----:|:----:|:----:|:---:| +| 0001 | null | 1 | 2 | tv | +| 0002 | 6 | 2 | null | tv | +| 0003 | 4 | 2 | 4 | tv | +| 0004 | 10 | 10 | 2 | tv | +| 0005 | null | null | 1 | tv | + +#### With nonNegative set to false +```js +|> difference(nonNegative: false) +``` +###### Output table +| _time | A | B | C | tag | +|:-----:|:----:|:----:|:----:|:---:| +| 0002 | null | 1 | null | tv | +| 0003 | -2 | 0 | 2 | tv | +| 0004 | 6 | 8 | -2 | tv | +| 0005 | null | null | -1 | tv | + +#### With nonNegative set to true +```js +|> difference(nonNegative: true): +``` +###### Output table +| _time | A | B | C | tag | +|:-----:|:----:|:----:|:----:|:---:| +| 0002 | null | 1 | null | tv | +| 0003 | null | 0 | 2 | tv | +| 0004 | 6 | 8 | null | tv | +| 0005 | null | null | null | tv | + +
+ +##### Related InfluxQL functions and statements: +[DIFFERENCE()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#difference) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/histogramquantile.md b/content/v2.0/reference/flux/functions/transformations/aggregates/histogramquantile.md new file mode 100644 index 000000000..8f38af6d5 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/histogramquantile.md @@ -0,0 +1,83 @@ +--- +title: histogramQuantile() function +description: The `histogramQuantile()` function approximates a quantile given a histogram that approximates the cumulative distribution of the dataset. +menu: + v2_0_ref: + name: histogramQuantile + parent: Aggregates + weight: 1 +--- + +The `histogramQuantile()` function approximates a quantile given a histogram that +approximates the cumulative distribution of the dataset. +Each input table represents a single histogram. +The histogram tables must have two columns – a count column and an upper bound column. + +The count is the number of values that are less than or equal to the upper bound value. +The table can have any number of records, each representing an entry in the histogram. +The counts must be monotonically increasing when sorted by upper bound. +If any values in the count column or upper bound column are `null`, it returns an error. + +Linear interpolation between the two closest bounds is used to compute the quantile. +If the either of the bounds used in interpolation are infinite, +then the other finite bound is used and no interpolation is performed. + +The output table has the same group key as the input table. +Columns not part of the group key are removed and a single value column of type float is added. +The count and upper bound columns must not be part of the group key. +The value column represents the value of the desired quantile from the histogram. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +histogramQuantile(quantile: 0.5, countColumn: "_value", upperBoundColumn: "le", valueColumn: "_value", minValue: 0) +``` + +## Parameters + +### quantile +A value between 0 and 1 indicating the desired quantile to compute. + +_**Data type:** Float_ + +### countColumn +The name of the column containing the histogram counts. +The count column type must be float. +Defaults to `"_value"`. + +_**Data type:** String_ + +### upperBoundColumn +The name of the column containing the histogram upper bounds. +The upper bound column type must be float. +Defaults to `"le"`. + +_**Data type:** String_ + +### valueColumn +The name of the output column which will contain the computed quantile. +Defaults to `"_value"`. + +_**Data type:** String_ + +### minValue +The assumed minimum value of the dataset. +When the quantile falls below the lowest upper bound, interpolation is performed between `minValue` and the lowest upper bound. +When `minValue` is equal to negative infinity, the lowest upper bound is used. +Defaults to `0`. + +_**Data type:** Float_ + +{{% note %}} +When the quantile falls below the lowest upper bound, +interpolation is performed between `minValue` and the lowest upper bound. +When `minValue` is equal to negative infinity, the lowest upper bound is used. +{{% /note %}} + +## Examples + +##### Compute the 90th quantile +```js +histogramQuantile(quantile: 0.9) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/increase.md b/content/v2.0/reference/flux/functions/transformations/aggregates/increase.md new file mode 100644 index 000000000..607790c9e --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/increase.md @@ -0,0 +1,66 @@ +--- +title: increase() function +description: The increase() function calculates the total non-negative difference between values in a table. +menu: + v2_0_ref: + name: increase + parent: Aggregates + weight: 1 +--- + +The `increase()` function calculates the total non-negative difference between values in a table. +A main use case is tracking changes in counter values which may wrap over time +when they hit a threshold or are reset. +In the case of a wrap/reset, we can assume that the absolute delta between two +points will be at least their non-negative difference. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +increase(columns: ["_values"]) +``` + +## Parameters + +### columns +The list of columns for which the increase is calculated. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -24h) + |> filter(fn: (r) => + r._measurement == "system" and + r._field == "n_users" + ) + |> increase() +``` + +Given the following input table: + +| _time | _value | +| ----- | ------ | +| 00001 | 1 | +| 00002 | 5 | +| 00003 | 3 | +| 00004 | 4 | + +`increase()` produces the following table: + +| _time | _value | +| ----- | ------ | +| 00002 | 4 | +| 00003 | 7 | +| 00004 | 8 | + +## Function definition +```js +increase = (tables=<-, columns=["_value"]) => + tables + |> difference(nonNegative: true, columns:columns) + |> cumulativeSum() +``` diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/integral.md b/content/v2.0/reference/flux/functions/transformations/aggregates/integral.md new file mode 100644 index 000000000..3bc7206ab --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/integral.md @@ -0,0 +1,48 @@ +--- +title: integral() function +description: The integral() function computes the area under the curve per unit of time of subsequent non-null records. +menu: + v2_0_ref: + name: integral + parent: Aggregates + weight: 1 +--- + +The `integral()` function computes the area under the curve per [`unit`](#unit) of time of subsequent non-null records. +The curve is defined using `_time` as the domain and record values as the range. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +integral(unit: 10s, columns: ["_value"]) +``` + +## Parameters + +### unit +The time duration used when computing the integral. + +_**Data type:** Duration_ + +### columns +A list of columns on which to operate. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> integral(unit:10s) +``` + +
+ +##### Related InfluxQL functions and statements: +[INTEGRAL()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#integral) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/mean.md b/content/v2.0/reference/flux/functions/transformations/aggregates/mean.md new file mode 100644 index 000000000..6eb3691a3 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/mean.md @@ -0,0 +1,42 @@ +--- +title: mean() function +description: The mean() function computes the mean or average of non-null records in the input table. +menu: + v2_0_ref: + name: mean + parent: Aggregates + weight: 1 +--- + +The `mean()` function computes the mean or average of non-null records in the input table. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +mean(columns: ["_value"]) +``` + +## Parameters + +### columns +A list of columns on which to compute the mean. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent") + |> range(start:-12h) + |> window(every:10m) + |> mean() +``` + +
+ +##### Related InfluxQL functions and statements: +[MEAN()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#mean) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/median.md b/content/v2.0/reference/flux/functions/transformations/aggregates/median.md new file mode 100644 index 000000000..ffc15ceca --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/median.md @@ -0,0 +1,97 @@ +--- +title: median() function +description: The `median()` function returns the median `_value` of an input table or all non-null records in the input table with values that fall within the 50th percentile. +menu: + v2_0_ref: + name: median + parent: Aggregates + weight: 1 +--- + +The `median()` function is a special application of the [`percentile()` function](/v2.0/reference/flux/functions/transformations/aggregates/percentile) +that returns the median `_value` of an input table or all non-null records in the input table +with values that fall within the 50th percentile depending on the [method](#method) used. + +_**Function type:** Selector or Aggregate_ +_**Output data type:** Object_ + + +```js +median(method: "estimate_tdigest", compression: 0.0) +``` + +When using the `estimate_tdigest` or `exact_mean` methods, it outputs non-null +records with values that fall within the 50th percentile. + +When using the `exact_selector` method, it outputs the non-null record with the +value that represents the 50th percentile. + +{{% note %}} +The `median()` function can only be used with float value types. +It is a special application of the [`percentile()` function](/v2.0/reference/flux/functions/transformations/aggregates/percentile) which +uses an approximation implementation that requires floats. +You can convert your value column to a float column using the [`toFloat()` function](/v2.0/reference/flux/functions/transformations/type-conversions/tofloat). +{{% /note %}} + +## Parameters + +### method +Defines the method of computation. Defaults to `"estimate_tdigest"`. + +_**Data type:** String_ + +The available options are: + +##### estimate_tdigest +An aggregate method that uses a [t-digest data structure](https://github.com/tdunning/t-digest) +to compute an accurate percentile estimate on large data sources. + +##### exact_mean +An aggregate method that takes the average of the two points closest to the percentile value. + +##### exact_selector +A selector method that returns the data point for which at least percentile points are less than. + +### compression +Indicates how many centroids to use when compressing the dataset. +A larger number produces a more accurate result at the cost of increased memory requirements. +Defaults to 1000. + +_**Data type:** Float_ + +## Examples + +###### Median as an aggregate +```js +from(bucket: "telegraf/autogen") + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> range(start:-12h) + |> window(every:10m) + |> median() +``` + +###### Median as a selector +```js +from(bucket: "telegraf/autogen") + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> range(start:-12h) + |> window(every:10m) + |> median(method: "exact_selector") +``` + +## Function definition +```js +median = (method="estimate_tdigest", compression=0.0, tables=<-) => + percentile(percentile:0.5, method:method, compression:compression) +``` + +
+ +##### Related InfluxQL functions and statements: +[MEDIAN()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#median) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/pearsonr.md b/content/v2.0/reference/flux/functions/transformations/aggregates/pearsonr.md new file mode 100644 index 000000000..e927d2dea --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/pearsonr.md @@ -0,0 +1,61 @@ +--- +title: pearsonr() function +description: The pearsonr() function computes the Pearson R correlation coefficient between two streams by first joining the streams, then performing the covariance operation normalized to compute R. +menu: + v2_0_ref: + name: pearsonr + parent: Aggregates + weight: 1 +--- + +The `pearsonr()` function computes the Pearson R correlation coefficient between two streams +by first joining the streams, then performing the covariance operation normalized to compute R. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +pearsonr(x: stream1, y: stream2, on: ["_time", "_field"]) +``` + +## Parameters + +### x +First input stream used in the operation. + +_**Data type:** Object_ + +### y +Second input stream used in the operation. + +_**Data type:** Object_ + +### on +The list of columns on which to join. + +_**Data type:** Array of strings_ + +## Examples +```js +stream1 = from(bucket:"telegraf/autogen") + |> range(start:-15m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used" + ) + +stream2 = from(bucket:"telegraf/autogen") + |> range(start:-15m) + |> filter(fn: (r) => r + ._measurement == "mem" and + r._field == "available" + ) + +pearsonr(x: stream1, y: stream2, on: ["_time", "_field"]) +``` + +## Function definition +```js +pearsonr = (x,y,on) => + cov(x:x, y:y, on:on, pearsonr:true) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/percentile.md b/content/v2.0/reference/flux/functions/transformations/aggregates/percentile.md new file mode 100644 index 000000000..c88250fe5 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/percentile.md @@ -0,0 +1,97 @@ +--- +title: percentile() function +description: The percentile() function outputs non-null records with values that fall within the specified percentile or the non-null record with the value that represents the specified percentile. +menu: + v2_0_ref: + name: percentile + parent: Aggregates + weight: 1 +--- + +The `percentile()` function returns records from an input table with `_value`s that fall within +a specified percentile or it returns the record with the `_value` that represents the specified percentile. +Which it returns depends on the [method](#method) used. + +_**Function type:** Aggregate or Selector_ +_**Output data type:** Float or Object_ + +```js +percentile(columns: ["_value"], percentile: 0.99, method: "estimate_tdigest", compression: 1000) +``` + +When using the `estimate_tdigest` or `exact_mean` methods, it outputs non-null +records with values that fall within the specified percentile. + +When using the `exact_selector` method, it outputs the non-null record with the +value that represents the specified percentile. + +## Parameters + +### columns +A list of columns on which to compute the percentile. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +### percentile +A value between 0 and 1 indicating the desired percentile. + +_**Data type:** Float_ + +### method +Defines the method of computation. + +_**Data type:** String_ + +The available options are: + +##### estimate_tdigest +An aggregate method that uses a [t-digest data structure](https://github.com/tdunning/t-digest) +to compute an accurate percentile estimate on large data sources. + +##### exact_mean +An aggregate method that takes the average of the two points closest to the percentile value. + +##### exact_selector +A selector method that returns the data point for which at least percentile points are less than. + +### compression +Indicates how many centroids to use when compressing the dataset. +A larger number produces a more accurate result at the cost of increased memory requirements. +Defaults to 1000. + +_**Data type:** Float_ + +## Examples + +###### Percentile as an aggregate +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system") + |> percentile( + percentile: 0.99, + method: "estimate_tdigest", + compression: 1000 + ) +``` + +###### Percentile as a selector +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system") + |> percentile( + percentile: 0.99, + method: "exact_selector" + ) +``` + +
+ +##### Related InfluxQL functions and statements: +[PERCENTILE()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#percentile) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/skew.md b/content/v2.0/reference/flux/functions/transformations/aggregates/skew.md new file mode 100644 index 000000000..f92ad9b19 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/skew.md @@ -0,0 +1,36 @@ +--- +title: skew() function +description: The skew() function outputs the skew of non-null records as a float. +menu: + v2_0_ref: + name: skew + parent: Aggregates + weight: 1 +--- + +The `skew()` function outputs the skew of non-null records as a float. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +skew(columns: ["_value"]) +``` + +## Parameters + +### columns +Specifies a list of columns on which to operate. Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> skew() +``` diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/spread.md b/content/v2.0/reference/flux/functions/transformations/aggregates/spread.md new file mode 100644 index 000000000..4948198c6 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/spread.md @@ -0,0 +1,46 @@ +--- +title: spread() function +description: The spread() function outputs the difference between the minimum and maximum values in each specified column. +menu: + v2_0_ref: + name: spread + parent: Aggregates + weight: 1 +--- + +The `spread()` function outputs the difference between the minimum and maximum values in each specified column. +Only `uint`, `int`, and `float` column types can be used. +The type of the output column depends on the type of input column: + +- For input columns with type `uint` or `int`, the output is an `int` +- For input columns with type `float` the output is a float. + +_**Function type:** Aggregate_ +_**Output data type:** Integer or Float (inherited from input column type)_ + +```js +spread(columns: ["_value"]) +``` + +## Parameters + +### columns +Specifies a list of columns on which to operate. Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> spread() +``` + +
+ +##### Related InfluxQL functions and statements: +[SPREAD()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#spread) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/stddev.md b/content/v2.0/reference/flux/functions/transformations/aggregates/stddev.md new file mode 100644 index 000000000..4bccc283c --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/stddev.md @@ -0,0 +1,42 @@ +--- +title: stddev() function +description: The stddev() function computes the standard deviation of non-null records in specified columns. +menu: + v2_0_ref: + name: stddev + parent: Aggregates + weight: 1 +--- + +The `stddev()` function computes the standard deviation of non-null records in specified columns. + +_**Function type:** Aggregate_ +_**Output data type:** Float_ + +```js +stddev(columns: ["_value"]) +``` + +## Parameters + +### columns +Specifies a list of columns on which to operate. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> stddev() +``` + +
+ +##### Related InfluxQL functions and statements: +[STDDEV()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#stddev) diff --git a/content/v2.0/reference/flux/functions/transformations/aggregates/sum.md b/content/v2.0/reference/flux/functions/transformations/aggregates/sum.md new file mode 100644 index 000000000..3338bd9f8 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/aggregates/sum.md @@ -0,0 +1,42 @@ +--- +title: sum() function +description: The sum() function computes the sum of non-null records in specified columns. +menu: + v2_0_ref: + name: sum + parent: Aggregates + weight: 1 +--- + +The `sum()` function computes the sum of non-null records in specified columns. + +_**Function type:** Aggregate_ +_**Output data type:** Integer, UInteger, or Float (inherited from column type)_ + +```js +sum(columns: ["_value"]) +``` + +## Parameters + +### columns +Specifies a list of columns on which to operate. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> sum() +``` + +
+ +##### Related InfluxQL functions and statements: +[SUM()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#sum) diff --git a/content/v2.0/reference/flux/functions/transformations/columns.md b/content/v2.0/reference/flux/functions/transformations/columns.md new file mode 100644 index 000000000..e51ddece6 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/columns.md @@ -0,0 +1,57 @@ +--- +title: columns() function +description: > + The columns() function lists the column labels of input tables. + For each input table, it outputs a table with the same group key columns, + plus a new column containing the labels of the input table's columns. +menu: + v2_0_ref: + name: columns + parent: Transformations + weight: 1 +--- + +The `columns()` function lists the column labels of input tables. +For each input table, it outputs a table with the same group key columns, +plus a new column containing the labels of the input table's columns. +Each row in an output table contains the group key value and the label of one column of the input table. +Each output table has the same number of rows as the number of columns of the input table. + +_**Function type:** Transformation_ + +```js +columns(column: "_value") +``` + +## Parameters + +### column +The name of the output column in which to store the column labels. +Defaults to `"_value"`. + +_**Data type:** String_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -30m) + |> columns(column: "labels") +``` + +##### Get every possible column label in a single table +```js +from(bucket: "telegraf/autogen") + |> range(start: -30m) + |> columns() + |> keep(columns: ["_value"]) + |> group() + |> distinct() +``` + +
+ +##### Related InfluxQL functions and statements: +[SHOW MEASUREMENTS](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-measurements) +[SHOW FIELD KEYS](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-field-keys) +[SHOW TAG KEYS](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-tag-keys) +[SHOW SERIES](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-tag-keys) diff --git a/content/v2.0/reference/flux/functions/transformations/cumulativesum.md b/content/v2.0/reference/flux/functions/transformations/cumulativesum.md new file mode 100644 index 000000000..a44bc72a1 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/cumulativesum.md @@ -0,0 +1,43 @@ +--- +title: cumulativeSum() function +description: The cumulativeSum() function computes a running sum for non-null records in the table. +menu: + v2_0_ref: + name: cumulativeSum + parent: Transformations + weight: 1 +--- + +The `cumulativeSum()` function computes a running sum for non-null records in the table. +The output table schema will be the same as the input table. + +_**Function type:** Transformation +_**Output data type:** Float_ + +```js +cumulativeSum(columns: ["_value"]) +``` + +## Parameters + +### columns +A list of columns on which to operate. +Defaults to `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "disk" and + r._field == "used_percent" + ) + |> cumulativeSum(columns: ["_value"]) +``` + +
+ +##### Related InfluxQL functions and statements: +[CUMULATIVE_SUM()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#cumulative-sum) diff --git a/content/v2.0/reference/flux/functions/transformations/drop.md b/content/v2.0/reference/flux/functions/transformations/drop.md new file mode 100644 index 000000000..d7c3b2690 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/drop.md @@ -0,0 +1,61 @@ +--- +title: drop() function +description: The drop() function removes specified columns from a table. +menu: + v2_0_ref: + name: drop + parent: Transformations + weight: 1 +--- + +The `drop()` function removes specified columns from a table. +Columns are specified either through a list or a predicate function. +When a dropped column is part of the group key, it will be removed from the key. +If a specified column is not present in a table, it will return an error. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +drop(columns: ["col1", "col2"]) + +// OR + +drop(fn: (column) => column =~ /usage*/) +``` + +## Parameters + +### columns +Columns to be removed from the table. +Cannot be used with `fn`. + +_**Data type:** Array of strings_ + +### fn +A predicate function which takes a column name as a parameter (`column`) and returns +a boolean indicating whether or not the column should be removed from the table. +Cannot be used with `columns`. + +_**Data type:** Function_ + +## Examples + +##### Drop a list of columns +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> drop(columns: ["host", "_measurement"]) +``` + +##### Drop columns matching a predicate +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> drop(fn: (column) => column =~ /usage*/) +``` + +
+ +##### Related InfluxQL functions and statements: +[DROP MEASUREMENT](https://docs.influxdata.com/influxdb/latest/query_language/database_management/#delete-measurements-with-drop-measurement) diff --git a/content/v2.0/reference/flux/functions/transformations/duplicate.md b/content/v2.0/reference/flux/functions/transformations/duplicate.md new file mode 100644 index 000000000..bb69f165e --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/duplicate.md @@ -0,0 +1,40 @@ +--- +title: duplicate() function +description: The duplicate() function duplicates a specified column in a table. +menu: + v2_0_ref: + name: duplicate + parent: Transformations + weight: 1 +--- + +The `duplicate()` function duplicates a specified column in a table. +If the specified column is part of the group key, it will be duplicated, but will +not be part of the output table's group key. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +duplicate(column: "column-name", as: "duplicate-name") +``` + +## Parameters + +### column +The column to duplicate. + +_**Data type:** String_ + +### as +The name assigned to the duplicate column. + +_**Data type:** String_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start:-5m) + |> filter(fn: (r) => r._measurement == "cpu") + |> duplicate(column: "host", as: "server") +``` diff --git a/content/v2.0/reference/flux/functions/transformations/fill.md b/content/v2.0/reference/flux/functions/transformations/fill.md new file mode 100644 index 000000000..7e221cad7 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/fill.md @@ -0,0 +1,72 @@ +--- +title: fill() function +description: The fill() function filters data based on conditions defined in a predicate function (fn). +menu: + v2_0_ref: + name: fill + parent: Transformations + weight: 1 +--- + +The `filter()` function replaces all null values in an input stream and replace them with a non-null value. +The output stream is the same as the input stream with all null values replaced in the specified column. + +_**Function type:** Transformation_ + +```js +fill(column: "_value", value: 0.0) + +// OR + +fill(column: "_value", usePrevious: true) +``` + +## Parameters + +### column +The column in which to replace null values. Defaults to `"_value"`. + +_**Data type:** String_ + +### value +The constant value to use in place of nulls. +The value type must match the value type of the `column`. + +_**Data type:** Boolean | Integer | UInteger | Float | String | Time | Duration_ + +### usePrevious +When `true`, assigns the value set in the previous non-null row. + +> Cannot be used with `value`. + +_**Data type:** Boolean | Integer | UInteger | Float | String | Time | Duration_ + + +## Examples + +##### Fill null values with a specified non-null value +```js +from(bucket: "telegraf/autogen") + |> range(start: -1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r.cpu == "cpu-total" + ) + |> fill(value: 0.0) +``` + +##### Fill null values with the previous non-null value +```js +from(bucket: "telegraf/autogen") + |> range(start: -1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r.cpu == "cpu-total" + ) + |> fill(usePrevious: true) +``` + +
+ +##### Related InfluxQL functions and statements: +[FILL](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#group-by-time-intervals-and-fill) diff --git a/content/v2.0/reference/flux/functions/transformations/filter.md b/content/v2.0/reference/flux/functions/transformations/filter.md new file mode 100644 index 000000000..b56df7562 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/filter.md @@ -0,0 +1,48 @@ +--- +title: filter() function +description: The filter() function filters data based on conditions defined in a predicate function (fn). +menu: + v2_0_ref: + name: filter + parent: Transformations + weight: 1 +--- + +The `filter()` function filters data based on conditions defined in a predicate function ([`fn`](#fn)). +The output tables have the same schema as the corresponding input tables. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +filter(fn: (r) => r._measurement == "cpu") +``` + +## Parameters + +### fn +A single argument function that evaluates true or false. +Records are passed to the function. +Those that evaluate to true are included in the output tables. + +_**Data type:** Function_ + +{{% note %}} +Objects evaluated in `fn` functions are represented by `r`, short for "record" or "row". +{{% /note %}} + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) +``` + +
+ +##### Related InfluxQL functions and statements: +[SELECT](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#the-basic-select-statement) diff --git a/content/v2.0/reference/flux/functions/transformations/group.md b/content/v2.0/reference/flux/functions/transformations/group.md new file mode 100644 index 000000000..ad7a78045 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/group.md @@ -0,0 +1,80 @@ +--- +title: group() function +description: The group() function groups records based on their values for specific columns. +menu: + v2_0_ref: + name: group + parent: Transformations + weight: 1 +--- + +The `group()` function groups records based on their values for specific columns. +It produces tables with new group keys based on provided properties. + +_**Function type:** Transformation_ + +```js +group(columns: ["host", "_measurement"], mode:"by") + +// OR + +group(columns: ["_time"], mode:"except") + +// OR + +group() +``` + +## Parameters + +### columns +List of columns to use in the grouping operation. +Defaults to `[]`. + +_**Data type:** Array of strings_ + +### mode +The mode used to group columns. + +_**Data type:** String_ + +The following options are available: + +- by +- except + +Defaults to `"by"`. + +#### by +Groups records by columns defined in the [`columns`](#columns) parameter. + +#### except +Groups records by all columns **except** those defined in the [`columns`](#columns) parameter. + +## Examples + +###### Group by host and measurement +```js +from(bucket: "telegraf/autogen") + |> range(start: -30m) + |> group(columns: ["host", "_measurement"]) +``` + +###### Group by everything except time +```js +from(bucket: "telegraf/autogen") + |> range(start: -30m) + |> group(columns: ["_time"], mode: "except") +``` + +###### Remove all grouping +```js +from(bucket: "telegraf/autogen") + |> range(start: -30m) + |> group() +``` + +
+ +##### Related InfluxQL functions and statements: +[GROUP BY](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#the-group-by-clause) _(similar but different)_ diff --git a/content/v2.0/reference/flux/functions/transformations/histogram.md b/content/v2.0/reference/flux/functions/transformations/histogram.md new file mode 100644 index 000000000..73fbb230f --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/histogram.md @@ -0,0 +1,78 @@ +--- +title: histogram() function +description: The histogram() function approximates the cumulative distribution of a dataset by counting data frequencies for a list of bins. +menu: + v2_0_ref: + name: histogram + parent: Transformations + weight: 1 +--- + +The `histogram()` function approximates the cumulative distribution of a dataset by counting data frequencies for a list of bins. +A bin is defined by an upper bound where all data points that are less than or equal to the bound are counted in the bin. +The bin counts are cumulative. + +Each input table is converted into a single output table representing a single histogram. +The output table has a the same group key as the input table. +Columns not part of the group key are removed and an upper bound column and a count column are added. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +histogram(column: "_value", upperBoundColumn: "le", countColumn: "_value", bins: [50.0, 75.0, 90.0], normalize: false) +``` + +## Parameters + +### column +The name of a column containing input data values. +The column type must be float. +Defaults to `"_value"`. + +_**Data type:** String_ + +### upperBoundColumn +The name of the column in which to store the histogram's upper bounds. +Defaults to `"le"`. + +_**Data type:** String_ + +### countColumn +The name of the column in which to store the histogram counts. +Defaults to `"_value"`. + +_**Data type:** String_ + +### bins +A list of upper bounds to use when computing the histogram frequencies. +Bins should contain a bin whose bound is the maximum value of the data set. +This value can be set to positive infinity if no maximum is known. + +_**Data type:** Array of floats_ + +#### Bin helper functions +The following helper functions can be used to generated bins. + +[linearBins()](/v2.0/reference/flux/functions/misc/linearbins) +[logarithmicBins()](/v2.0/reference/flux/functions/misc/logarithmicbins) + +### normalize +When `true`, will convert the counts into frequency values between 0 and 1. +Defaults to `false`. + +_**Data type:** Boolean_ + +{{% note %}} +Normalized histograms cannot be aggregated by summing their counts. +{{% /note %}} + +## Examples + +##### Histogram with dynamically generated bins +```js +// Dynamically generate 10 bins from 0,10,20,...,100 +histogram( + bins: linearBins(start:0.0, width:10.0, count:10) +) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/influxfieldsascols.md b/content/v2.0/reference/flux/functions/transformations/influxfieldsascols.md new file mode 100644 index 000000000..f0a5ec269 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/influxfieldsascols.md @@ -0,0 +1,40 @@ +--- +title: influxFieldsAsCols() function +description: The influxFieldsAsCols() function is pivots a table and automatically aligns fields within each input table that have the same timestamp. +aliases: + - /v2.0/reference/flux/functions/inputs/fromrows +menu: + v2_0_ref: + name: influxFieldsAsCols + parent: Transformations + weight: 1 +--- + +The `influxFieldsAsCols()` function is a special application of the `pivot()` function that +automatically aligns fields within each input table that have the same timestamp. + +_**Function type:** Transformation_ + +```js +influxFieldsAsCols() +``` + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start: -1h) + |> filter(fn: (r) => r._measurement == "cpu") + |> influxFieldsAsCols() + |> keep(columns: ["_time", "cpu", "usage_idle", "usage_user"]) +``` + +## Function definition +```js +influxFieldsAsCols = (tables=<-) => + tables + |> pivot( + rowKey:["_time"], + columnKey: ["_field"], + valueColumn: "_value" + ) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/join.md b/content/v2.0/reference/flux/functions/transformations/join.md new file mode 100644 index 000000000..ceeb95f55 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/join.md @@ -0,0 +1,133 @@ +--- +title: join() function +description: The join() function merges two or more input streams whose values are equal on a set of common columns into a single output stream. +menu: + v2_0_ref: + name: join + parent: Transformations + weight: 1 +--- + +The `join()` function merges two or more input streams whose values are equal on +a set of common columns into a single output stream. +Null values are not considered equal when comparing column values. +The resulting schema is the union of the input schemas. +The resulting group key is the union of the input group keys. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +join(tables: {key1: table1, key2: table2}, on: ["_time", "_field"], method: "inner") +``` + +#### Output schema +The column schema of the output stream is the union of the input schemas. +It is also the same for the output group key. +Columns are renamed using the pattern `_` to prevent ambiguity in joined tables. + +##### Example: +If you have two streams of data, **data_1** and **data_2**, with the following group keys: + +**data_1**: `[_time, _field]` +**data_2**: `[_time, _field]` + +And join them with: + +```js +join(tables: {d1: data_1, d2: data_2}, on: ["_time"]) +``` + +The resulting group keys for all tables will be: `[_time, _field_d1, _field_d2]` + + +## Parameters + +### tables +The map of streams to be joined. Required. + +_**Data type:** Object_ + +> `join()` currently only supports two input streams. + +### on +The list of columns on which to join. Required. + +_**Data type:** Array of strings_ + +### method +The method used to join. Defaults to `"inner"`. + +_**Data type:** String_ + +###### Possible Values: +- `inner` +- `cross` +- `left` +- `right` +- `full` + +{{% note %}} +The `on` parameter and the cross method are mutually exclusive. +{{% /note %}} + +## Examples + +#### Example join with sample data + +Given the following two streams of data: + +##### SF_Temp** + +| _time | _field | _value | +| ------ |:------:| -------:| +| 0001 | "temp" | 70 | +| 0002 | "temp" | 75 | +| 0003 | "temp" | 72 | + +##### NY_Temp** + +| _time | _field | _value | +| ------ |:------:| -------:| +| 0001 | "temp" | 55 | +| 0002 | "temp" | 56 | +| 0003 | "temp" | 55 | + +And the following join query: + +```js +join( + tables: {sf: SF_Temp, ny: NY_Temp}, + on: ["_time", "_field"] +) +``` + +The output will be: + +| _time | _field | _value_ny | _value_sf | +| ----- | ------ | ---------:| ---------:| +| 0001 | "temp" | 55 | 70 | +| 0002 | "temp" | 56 | 75 | +| 0003 | "temp" | 55 | 72 | + +#### Cross-measurement join +```js +data_1 = from(bucket:"telegraf/autogen") + |> range(start:-15m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + +data_2 = from(bucket:"telegraf/autogen") + |> range(start:-15m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + +join( + tables: {d1: data_1, d2: data_2}, + on: ["_time", "host"] +) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/keep.md b/content/v2.0/reference/flux/functions/transformations/keep.md new file mode 100644 index 000000000..4b8f791f6 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/keep.md @@ -0,0 +1,55 @@ +--- +title: keep() function +description: The keep() function returns a table containing only the specified columns. +menu: + v2_0_ref: + name: keep + parent: Transformations + weight: 1 +--- + +The `keep()` function returns a table containing only the specified columns, ignoring all others. +Only columns in the group key that are also specified in the `keep()` function will be kept in the resulting group key. +_It is the inverse of [`drop`](/v2.0/reference/flux/functions/transformations/drop)._ + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +keep(columns: ["col1", "col2"]) + +// OR + +keep(fn: (column) => column =~ /inodes*/) +``` + +## Parameters + +### columns +Columns that should be included in the resulting table. +Cannot be used with `fn`. + +_**Data type:** Array of strings_ + +### fn +A predicate function which takes a column name as a parameter (`column`) and returns +a boolean indicating whether or not the column should be included in the resulting table. +Cannot be used with `columns`. + +_**Data type:** Function_ + +## Examples + +##### Keep a list of columns +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> keep(columns: ["_time", "_value"]) +``` + +##### Keep all columns matching a predicate +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> keep(fn: (column) => column =~ /inodes*/) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/keys.md b/content/v2.0/reference/flux/functions/transformations/keys.md new file mode 100644 index 000000000..ef38e9c94 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/keys.md @@ -0,0 +1,57 @@ +--- +title: keys() function +description: > + The keys() function outputs the group key of input tables. + For each input table, it outputs a table with the same group key columns, plus a + _value column containing the labels of the input table's group key. +menu: + v2_0_ref: + name: keys + parent: Transformations + weight: 1 +--- + +The `keys()` function outputs the group key of input tables. +For each input table, it outputs a table with the same group key columns, plus a +`_value` column containing the labels of the input table's group key. +Each row in an output table contains the group key value and the label of one column in the group key of the input table. +Each output table has the same number of rows as the size of the group key of the input table. + +_**Function type:** Transformation_ + +```js +keys(column: "_value") +``` + +## Parameters + +### column +The name of the output column in which to store the group key labels. +Defaults to `"_value"`. + +_**Data type:** String_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -30m) + |> keys(column: "keys") +``` + +##### Return every possible key in a single table +```js +from(bucket: "telegraf/autogen") + |> range(start: -30m) + |> keys() + |> keep(columns: ["_value"]) + |> group() + |> distinct() +``` + +
+ +##### Related InfluxQL functions and statements: +[SHOW MEASUREMENTS](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-measurements) +[SHOW FIELD KEYS](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-field-keys) +[SHOW TAG KEYS](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-tag-keys) +[SHOW SERIES](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-tag-keys) diff --git a/content/v2.0/reference/flux/functions/transformations/keyvalues.md b/content/v2.0/reference/flux/functions/transformations/keyvalues.md new file mode 100644 index 000000000..8045d5ed4 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/keyvalues.md @@ -0,0 +1,75 @@ +--- +title: keyValues() function +description: The keyValues() function returns a table with the input table's group key plus two columns, _key and _value, that correspond to unique column + value pairs from the input table. +menu: + v2_0_ref: + name: keyValues + parent: Transformations + weight: 1 +--- + +The `keyValues()` function returns a table with the input table's group key plus two columns, +`_key` and `_value`, that correspond to unique column + value pairs from the input table. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +keyValues(keyColumns: ["usage_idle", "usage_user"]) + +// OR + +keyValues(fn: (schema) => schema.columns |> filter(fn: (r) => r.label =~ /usage_.*/)) +``` + +## Parameters + +{{% note %}} +`keyColumns` and `fn` are mutually exclusive. Only one may be used at a time. +{{% /note %}} + +### keyColumns +A list of columns from which values are extracted. +All columns indicated must be of the same type. +Each input table must have all of the columns listed by the `keyColumns` parameter. + +_**Data type:** Array of strings_ + +### fn +Function used to identify a set of columns. +All columns indicated must be of the same type. + +_**Data type:** Function_ + +## Additional requirements + +- Only one of `keyColumns` or `fn` may be used in a single call. +- All columns indicated must be of the same type. +- Each input table must have all of the columns listed by the `keyColumns` parameter. + +## Examples + +##### Get key values from explicitly defined columns +```js +from(bucket: "telegraf/autogen") + |> range(start: -30m) + |> filter(fn: (r) => r._measurement == "cpu") + |> keyValues(keyColumns: ["usage_idle", "usage_user"]) +``` + +##### Get key values from columns matching a regular expression +```js +from(bucket: "telegraf/autogen") + |> range(start: -30m) + |> filter(fn: (r) => r._measurement == "cpu") + |> keyValues(fn: (schema) => schema.columns |> filter(fn: (r) => r.label =~ /usage_.*/)) +``` + +
+ +##### Related InfluxQL functions and statements: +[SHOW MEASUREMENTS](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-measurements) +[SHOW FIELD KEYS](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-field-keys) +[SHOW TAG KEYS](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-tag-keys) +[SHOW TAG VALUES](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-tag-values) +[SHOW SERIES](https://docs.influxdata.com/influxdb/latest/query_language/schema_exploration/#show-series) diff --git a/content/v2.0/reference/flux/functions/transformations/limit.md b/content/v2.0/reference/flux/functions/transformations/limit.md new file mode 100644 index 000000000..25561ef04 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/limit.md @@ -0,0 +1,46 @@ +--- +title: limit() function +description: The limit() function limits the number of records in output tables to a fixed number (n). +menu: + v2_0_ref: + name: limit + parent: Transformations + weight: 1 +--- + +The `limit()` function limits the number of records in output tables to a fixed number ([`n`](#n)). +One output table is produced for each input table. +Each output table contains the first `n` records after the first `offset` records of the input table. +If the input table has less than `offset + n` records, all records except the first `offset` ones are output. + +_**Function type:** Filter_ +_**Output data type:** Object_ + +```js +limit(n:10, offset: 0) +``` + +## Parameters + +### n +The maximum number of records to output. + +_**Data type:** Integer_ + +### offset +The number of records to skip per table before limiting to `n`. +Defaults to `0`. + +_**Data type:** Integer_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> limit(n:10, offset: 1) +``` + +
+ +##### Related InfluxQL functions and statements: +[LIMIT](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#the-limit-and-slimit-clauses) diff --git a/content/v2.0/reference/flux/functions/transformations/map.md b/content/v2.0/reference/flux/functions/transformations/map.md new file mode 100644 index 000000000..a9a8188e2 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/map.md @@ -0,0 +1,72 @@ +--- +title: map() function +description: The map() function applies a function to each record in the input tables. +menu: + v2_0_ref: + name: map + parent: Transformations + weight: 1 +--- + +The `map()` function applies a function to each record in the input tables. +The modified records are assigned to new tables based on the group key of the input table. +The output tables are the result of applying the map function to each record of the input tables. + +When the output record contains a different value for the group key, the record is regrouped into the appropriate table. +When the output record drops a column that was part of the group key, that column is removed from the group key. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +map(fn: (r) => r._value * r._value), mergeKey: true) +``` + +## Parameters + +### fn +A single argument function that to apply to each record. +The return value must be an object. + +_**Data type:** Function_ + +{{% note %}} +Objects evaluated in `fn` functions are represented by `r`, short for "record" or "row". +{{% /note %}} + +### mergeKey +Indicates if the record returned from `fn` should be merged with the group key. +When merging, all columns on the group key will be added to the record giving precedence to any columns already present on the record. +When not merging, only columns defined on the returned record will be present on the output records. +Defaults to `true`. + +_**Data type:** Boolean_ + +## Examples + +###### Square the value of each record +```js +from(bucket:"telegraf/autogen") + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" and + r.cpu == "cpu-total" + ) + |> range(start:-12h) + |> map(fn: (r) => r._value * r._value) +``` + +###### Create a new table with new format +```js +from(bucket:"telegraf/autogen") + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> range(start:-12h) + // create a new table by copying each row into a new format + |> map(fn: (r) => ({ + _time: r._time, + app_server: r.host + })) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/pivot.md b/content/v2.0/reference/flux/functions/transformations/pivot.md new file mode 100644 index 000000000..84438b888 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/pivot.md @@ -0,0 +1,148 @@ +--- +title: pivot() function +description: The pivot() function collects values stored vertically (column-wise) in a table and aligns them horizontally (row-wise) into logical sets. +menu: + v2_0_ref: + name: pivot + parent: Transformations + weight: 1 +--- + +The `pivot()` function collects values stored vertically (column-wise) in a table +and aligns them horizontally (row-wise) into logical sets. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value") +``` + +The group key of the resulting table is the same as the input tables, excluding columns found in the [`columnKey`](#columnkey) and [`valueColumn`](#valuecolumn) parameters. +This is because these columns are not part of the resulting output table. + +Every input row should have a 1:1 mapping to a particular row + column in the output table, determined by its values for the [`rowKey`](#rowkey) and [`columnKey`](#columnkey) parameters. +In cases where more than one value is identified for the same row + column pair, the last value +encountered in the set of table rows is used as the result. + +Every input row should have a 1:1 mapping to a particular row/column pair in the output table, +determined by its values for the `rowKey` and `columnKey`. +In cases where more than one value is identified for the same row/column pair in the output, +the last value encountered in the set of table rows is used as the result. + +The output is constructed as follows: + +- The set of columns for the new table is the `rowKey` unioned with the group key, + but excluding the columns indicated by the `columnKey` and the `valueColumn`. +- A new column is added to the set of columns for each unique value identified + in the input by the `columnKey` parameter. +- The label of a new column is the concatenation of the values of `columnKey` using `_` as a separator. + If the value is `null`, `"null"` is used. +- A new row is created for each unique value identified in the input by the `rowKey` parameter. +- For each new row, values for group key columns stay the same, while values for new columns are + determined from the input tables by the value in `valueColumn` at the row identified by the + `rowKey` values and the new column's label. + If no value is found, the value is set to `null`. + + +## Parameters + +### rowKey +List of columns used to uniquely identify a row for the output. + +_**Data type:** Array of strings_ + +### columnKey +List of columns used to pivot values onto each row identified by the rowKey. + +_**Data type:** Array of strings_ + +### valueColumn +The single column that contains the value to be moved around the pivot. + +_**Data type:** String_ + +## Examples + +### Align fields within each measurement that have the same timestamp + +```js +from(bucket:"test") + |> range(start: 1970-01-01T00:00:00.000000000Z) + |> pivot( + rowKey:["_time"], + columnKey: ["_field"], + valueColumn: "_value" + ) +``` + +###### Input +| _time | _value | _measurement | _field | +|:------------------------------:|:------:|:------------:|:------:| +| 1970-01-01T00:00:00.000000001Z | 1.0 | "m1" | "f1" | +| 1970-01-01T00:00:00.000000001Z | 2.0 | "m1" | "f2" | +| 1970-01-01T00:00:00.000000001Z | null | "m1" | "f3" | +| 1970-01-01T00:00:00.000000001Z | 3.0 | "m1" | null | +| 1970-01-01T00:00:00.000000002Z | 4.0 | "m1" | "f1" | +| 1970-01-01T00:00:00.000000002Z | 5.0 | "m1" | "f2" | +| null | 6.0 | "m1" | "f2" | +| 1970-01-01T00:00:00.000000002Z | null | "m1" | "f3" | +| 1970-01-01T00:00:00.000000003Z | null | "m1" | "f1" | +| 1970-01-01T00:00:00.000000003Z | 7.0 | "m1" | null | +| 1970-01-01T00:00:00.000000004Z | 8.0 | "m1" | "f3" | + +###### Output +| _time | _measurement | f1 | f2 | f3 | null | +|:------------------------------:|:------------:|:----:|:----:|:----:|:----:| +| 1970-01-01T00:00:00.000000001Z | "m1" | 1.0 | 2.0 | null | 3.0 | +| 1970-01-01T00:00:00.000000002Z | "m1" | 4.0 | 5.0 | null | null | +| null | "m1" | null | 6.0 | null | null | +| 1970-01-01T00:00:00.000000003Z | "m1" | null | null | null | 7.0 | +| 1970-01-01T00:00:00.000000004Z | "m1" | null | null | 8.0 | null | + +### Align fields and measurements that have the same timestamp + +{{% note %}} +Note the effects of: + +- Having null values in some `columnKey` value; +- Having more values for the same `rowKey` and `columnKey` value + (the 11th row overrides the 10th, and so does the 15th with the 14th). +{{% /note %}} + +```js +from(bucket:"test") + |> range(start: 1970-01-01T00:00:00.000000000Z) + |> pivot( + rowKey:["_time"], + columnKey: ["_measurement", "_field"], + valueColumn: "_value" + ) +``` + +###### Input +| _time | _value | _measurement | _field | +|:------------------------------:|:------:|:------------:|:------:| +| 1970-01-01T00:00:00.000000001Z | 1.0 | "m1" | "f1" | +| 1970-01-01T00:00:00.000000001Z | 2.0 | "m1" | "f2" | +| 1970-01-01T00:00:00.000000001Z | 3.0 | null | "f3" | +| 1970-01-01T00:00:00.000000001Z | 4.0 | null | null | +| 1970-01-01T00:00:00.000000002Z | 5.0 | "m1" | "f1" | +| 1970-01-01T00:00:00.000000002Z | 6.0 | "m1" | "f2" | +| 1970-01-01T00:00:00.000000002Z | 7.0 | "m1" | "f3" | +| 1970-01-01T00:00:00.000000002Z | 8.0 | null | null | +| null | 9.0 | "m1" | "f3" | +| 1970-01-01T00:00:00.000000003Z | 10.0 | "m1" | null | +| 1970-01-01T00:00:00.000000003Z | 11.0 | "m1" | null | +| 1970-01-01T00:00:00.000000003Z | 12.0 | "m1" | "f3" | +| 1970-01-01T00:00:00.000000003Z | 13.0 | null | null | +| null | 14.0 | "m1" | null | +| null | 15.0 | "m1" | null | + +###### Output +| _time | m1_f1 | m1_f2 | null_f3 | null_null | m1_f3 | m1_null | +|:------------------------------:|:-----:|:-----:|:---------:|:---------:|:-----:|:-------:| +| 1970-01-01T00:00:00.000000001Z | 1.0 | 2.0 | 3.0 | 4.0 | null | null | +| 1970-01-01T00:00:00.000000002Z | 5.0 | 6.0 | null | 8.0 | 7.0 | null | +| null | null | null | null | null | 9.0 | 15.0 | +| 1970-01-01T00:00:00.000000003Z | null | null | null | 13.0 | 12.0 | 11.0 | diff --git a/content/v2.0/reference/flux/functions/transformations/range.md b/content/v2.0/reference/flux/functions/transformations/range.md new file mode 100644 index 000000000..ee2574b85 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/range.md @@ -0,0 +1,70 @@ +--- +title: range() function +description: The range() function filters records based on time bounds. +menu: + v2_0_ref: + name: range + parent: Transformations + weight: 1 +--- + +The `range()` function filters records based on time bounds. +Each input table's records are filtered to contain only records that exist within the time bounds. +Records with a `null` value for their time are filtered. +Each input table's group key value is modified to fit within the time bounds. +Tables where all records exists outside the time bounds are filtered entirely. + +_**Function type:** Transformation_ +_**Output data type:* Object_ + +```js +range(start: -15m, stop: now) +``` + +## Parameters + +### start +Specifies the oldest time to be included in the results. + +Relative start times are defined using negative durations. +Negative durations are relative to now. +Absolute start times are defined using timestamps. + +_**Data type:** Duration or Timestamp_ + +### stop +Specifies the exclusive newest time to be included in the results. Defaults to `now`. + +Relative stop times are defined using negative durations. +Negative durations are relative to now. +Absolute stop times are defined using timestamps. + +_**Data type:** Duration or Timestamp_ + +## Examples + +###### Time range relative to now +```js +from(bucket:"telegraf/autogen") + |> range(start:-12h) + // ... +``` + +###### Relative time range +```js +from(bucket:"telegraf/autogen") + |> range(start:-12h, stop: -15m) + // ... +``` + +###### Absolute time range +```js +from(bucket:"telegraf/autogen") + |> range(start:2018-05-22T23:30:00Z, stop: 2018-05-23T00:00:00Z) + // ... +``` + +
+ +##### Related InfluxQL functions and statements: +[WHERE](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#the-where-clause) diff --git a/content/v2.0/reference/flux/functions/transformations/rename.md b/content/v2.0/reference/flux/functions/transformations/rename.md new file mode 100644 index 000000000..972d21615 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/rename.md @@ -0,0 +1,57 @@ +--- +title: rename() function +description: The rename() function renames specified columns in a table. +menu: + v2_0_ref: + name: rename + parent: Transformations + weight: 1 +--- + +The `rename()` function renames specified columns in a table. +If a column is renamed and is part of the group key, the column name in the group key will be updated. + +There are two variants: + +- one which maps old column names to new column names +- one which takes a mapping function. + +_**Function type:** Transformation_ + +```js +rename(columns: {host: "server", facility: "datacenter"}) + +// OR + +rename(fn: (column) => "{column}_new") +``` + +## Parameters + +### columns +A map of columns to rename and their corresponding new names. +Cannot be used with `fn`. + +_**Data type:** Object_ + +### fn +A function mapping between old and new column names. +Cannot be used with `columns`. + +_**Data type:** Function_ + +## Examples + +##### Rename a single column +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> rename(columns: {host: "server"}) +``` + +##### Rename all columns using a function +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> rename(fn: (column) => column + "_new") +``` diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/_index.md b/content/v2.0/reference/flux/functions/transformations/selectors/_index.md new file mode 100644 index 000000000..c3f75b850 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/_index.md @@ -0,0 +1,24 @@ +--- +title: Flux selector functions +description: Flux selector functions return one or more records based on function logic. +menu: + v2_0_ref: + parent: Transformations + name: Selectors + weight: 1 +--- + +Flux selector functions return one or more records based on function logic. +The output table is different than the input table, but individual row values are not. + +The following selector functions are available: + +{{< function-list category="Selectors" menu="v2_0_ref" >}} + + +### Selectors and aggregates +The following functions can be used as both selectors or aggregates, but they are +categorized as aggregate functions in this documentation: + +- [median](/v2.0/reference/flux/functions/transformations/aggregates/median) +- [percentile](/v2.0/reference/flux/functions/transformations/aggregates/percentile) diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/bottom.md b/content/v2.0/reference/flux/functions/transformations/selectors/bottom.md new file mode 100644 index 000000000..0bb331126 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/bottom.md @@ -0,0 +1,60 @@ +--- +title: bottom() function +description: The bottom() function sorts a table by columns and keeps only the bottom n records. +menu: + v2_0_ref: + name: bottom + parent: Selectors + weight: 1 +--- + +The `bottom()` function sorts a table by columns and keeps only the bottom `n` records. + +_**Function type:** Selector_ +_**Output data type:** Object_ + +```js +bottom(n:10, columns: ["_value"]) +``` + +## Parameters + +### n +Number of records to return. + +_**Data type:** Integer_ + +### columns +List of columns by which to sort. +Sort precedence is determined by list order (left to right). +Default is `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> bottom(n:10) +``` + +## Function definition +```js +// _sortLimit is a helper function, which sorts and limits a table. +_sortLimit = (n, desc, columns=["_value"], tables=<-) => + tables + |> sort(columns:columns, desc:desc) + |> limit(n:n) + +bottom = (n, columns=["_value"], tables=<-) => + _sortLimit(n:n, columns:columns, desc:false) +``` + +
+ +##### Related InfluxQL functions and statements: +[BOTTOM()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#bottom) diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/distinct.md b/content/v2.0/reference/flux/functions/transformations/selectors/distinct.md new file mode 100644 index 000000000..2accafb58 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/distinct.md @@ -0,0 +1,39 @@ +--- +title: distinct() function +description: The distinct() function returns the unique values for a given column. +menu: + v2_0_ref: + name: distinct + parent: Selectors + weight: 1 +--- + +The `distinct()` function returns the unique values for a given column. +`null` is considered its own distinct value if it is present. + +_**Function type:** Selector_ +_**Output data type:** Object_ + +```js +distinct(column: "host") +``` + +## Parameters + +### column +Column on which to track unique values. + +_**Data type:** string_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => r._measurement == "cpu") + |> distinct(column: "host") +``` + +
+ +##### Related InfluxQL functions and statements: +[DISTINCT()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#distinct) diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/first.md b/content/v2.0/reference/flux/functions/transformations/selectors/first.md new file mode 100644 index 000000000..8cc9453bd --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/first.md @@ -0,0 +1,34 @@ +--- +title: first() function +description: The first() function selects the first non-null record from an input table. +menu: + v2_0_ref: + name: first + parent: Selectors + weight: 1 +--- + +The `first()` function selects the first non-null record from an input table. + +_**Function type:** Selector_ +_**Output data type:** Object_ + +```js +first() +``` + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> first() +``` + +
+ +##### Related InfluxQL functions and statements: +[FIRST()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#first) diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/highestaverage.md b/content/v2.0/reference/flux/functions/transformations/selectors/highestaverage.md new file mode 100644 index 000000000..556ff4788 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/highestaverage.md @@ -0,0 +1,82 @@ +--- +title: highestAverage() function +description: The highestAverage() function returns the top 'n' records from all groups using the average of each group. +menu: + v2_0_ref: + name: highestAverage + parent: Selectors + weight: 1 +--- + +The `highestAverage()` function returns the top `n` records from all groups using the average of each group. + +_**Function type:** Selector, Aggregate_ + +```js +highestAverage( + n:10, + columns: ["_value"], + groupColumns: [] +) +``` + +## Parameters + +### n +Number of records to return. + +_**Data type:** Integer_ + +### columns +List of columns by which to sort. +Sort precedence is determined by list order (left to right). +Default is `["_value"]`. + +_**Data type:** Array of strings_ + +### groupColumns +The columns on which to group before performing the aggregation. +Default is `[]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> highestAverage(n:10, groupColumns: ["host"]) +``` + +## Function definition +```js +// _sortLimit is a helper function, which sorts and limits a table. +_sortLimit = (n, desc, columns=["_value"], tables=<-) => + tables + |> sort(columns:columns, desc:desc) + |> limit(n:n) + +// _highestOrLowest is a helper function which reduces all groups into a single +// group by specific tags and a reducer function. It then selects the highest or +// lowest records based on the columns and the _sortLimit function. +// The default reducer assumes no reducing needs to be performed. +_highestOrLowest = (n, _sortLimit, reducer, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> group(columns:groupColumns) + |> reducer() + |> group(columns:[]) + |> _sortLimit(n:n, columns:columns) + +highestAverage = (n, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> _highestOrLowest( + n:n, + columns:columns, + groupColumns:groupColumns, + reducer: (tables=<-) => tables |> mean(columns:[columns[0]]), + _sortLimit: top, + ) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/highestcurrent.md b/content/v2.0/reference/flux/functions/transformations/selectors/highestcurrent.md new file mode 100644 index 000000000..ba92d92b7 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/highestcurrent.md @@ -0,0 +1,82 @@ +--- +title: highestCurrent() function +description: The highestCurrent() function returns the top 'n' records from all groups using the last value of each group. +menu: + v2_0_ref: + name: highestCurrent + parent: Selectors + weight: 1 +--- + +The `highestCurrent()` function returns the top `n` records from all groups using the last value of each group. + +_**Function type:** Selector, Aggregate_ + +```js +highestCurrent( + n:10, + columns: ["_value"], + groupColumns: [] +) +``` + +## Parameters + +### n +Number of records to return. + +_**Data type:** Integer_ + +### columns +List of columns by which to sort. +Sort precedence is determined by list order (left to right). +Default is `["_value"]`. + +_**Data type:** Array of strings_ + +### groupColumns +The columns on which to group before performing the aggregation. +Default is `[]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> highestCurrent(n:10, groupColumns: ["host"]) +``` + +## Function definition +```js +// _sortLimit is a helper function, which sorts and limits a table. +_sortLimit = (n, desc, columns=["_value"], tables=<-) => + tables + |> sort(columns:columns, desc:desc) + |> limit(n:n) + +// _highestOrLowest is a helper function which reduces all groups into a single +// group by specific tags and a reducer function. It then selects the highest or +// lowest records based on the columns and the _sortLimit function. +// The default reducer assumes no reducing needs to be performed. +_highestOrLowest = (n, _sortLimit, reducer, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> group(columns:groupColumns) + |> reducer() + |> group(columns:[]) + |> _sortLimit(n:n, columns:columns) + +highestCurrent = (n, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> _highestOrLowest( + n:n, + columns:columns, + groupColumns:groupColumns, + reducer: (tables=<-) => tables |> last(column:columns[0]), + _sortLimit: top, + ) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/highestmax.md b/content/v2.0/reference/flux/functions/transformations/selectors/highestmax.md new file mode 100644 index 000000000..c25cb1c2a --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/highestmax.md @@ -0,0 +1,82 @@ +--- +title: highestMax() function +description: The highestMax() function returns the top 'n' records from all groups using the maximum of each group. +menu: + v2_0_ref: + name: highestMax + parent: Selectors + weight: 1 +--- + +The `highestMax()` function returns the top `n` records from all groups using the maximum of each group. + +_**Function type:** Selector, Aggregate_ + +```js +highestMax( + n:10, + columns: ["_value"], + groupColumns: [] +) +``` + +## Parameters + +### n +Number of records to return. + +_**Data type:** Integer_ + +### columns +List of columns by which to sort. +Sort precedence is determined by list order (left to right). +Default is `["_value"]`. + +_**Data type:** Array of strings_ + +### groupColumns +The columns on which to group before performing the aggregation. +Default is `[]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> highestMax(n:10, groupColumns: ["host"]) +``` + +## Function definition +```js +// _sortLimit is a helper function, which sorts and limits a table. +_sortLimit = (n, desc, columns=["_value"], tables=<-) => + tables + |> sort(columns:columns, desc:desc) + |> limit(n:n) + +// _highestOrLowest is a helper function which reduces all groups into a single +// group by specific tags and a reducer function. It then selects the highest or +// lowest records based on the columns and the _sortLimit function. +// The default reducer assumes no reducing needs to be performed. +_highestOrLowest = (n, _sortLimit, reducer, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> group(columns:groupColumns) + |> reducer() + |> group(columns:[]) + |> _sortLimit(n:n, columns:columns) + +highestMax = (n, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> _highestOrLowest( + n:n, + columns:columns, + groupColumns:groupColumns, + reducer: (tables=<-) => tables |> max(column:columns[0]), + _sortLimit: top + ) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/last.md b/content/v2.0/reference/flux/functions/transformations/selectors/last.md new file mode 100644 index 000000000..2b754b1ed --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/last.md @@ -0,0 +1,34 @@ +--- +title: last() function +description: The last() function selects the last non-null record from an input table. +menu: + v2_0_ref: + name: last + parent: Selectors + weight: 1 +--- + +The `last()` function selects the last non-null record from an input table. + +_**Function type:** Selector_ +_**Output data type:** Object_ + +```js +last() +``` + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> last() +``` + +
+ +##### Related InfluxQL functions and statements: +[LAST()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#last) diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/lowestaverage.md b/content/v2.0/reference/flux/functions/transformations/selectors/lowestaverage.md new file mode 100644 index 000000000..433c3af76 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/lowestaverage.md @@ -0,0 +1,83 @@ +--- +title: lowestAverage() function +description: The lowestAverage() function returns the bottom 'n' records from all groups using the average of each group. +menu: + v2_0_ref: + name: lowestAverage + parent: Selectors + weight: 1 +--- + +The `lowestAverage()` function returns the bottom `n` records from all groups using the average of each group. + +_**Function type:** Selector, Aggregate_ + +```js +lowestAverage( + n:10, + columns: ["_value"], + groupColumns: [] +) +``` + +## Parameters + +### n +Number of records to return. + +_**Data type:** Integer_ + +### columns +List of columns by which to sort. +Sort precedence is determined by list order (left to right). +Default is `["_value"]`. + +_**Data type:** Array of strings_ + +### groupColumns +The columns on which to group before performing the aggregation. +Default is `[]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> lowestAverage(n:10, groupColumns: ["host"]) +``` + +## Function definition +```js +// _sortLimit is a helper function, which sorts and limits a table. +_sortLimit = (n, desc, columns=["_value"], tables=<-) => + tables + |> sort(columns:columns, desc:desc) + |> limit(n:n) + +// _highestOrLowest is a helper function which reduces all groups into a single +// group by specific tags and a reducer function. It then selects the highest or +// lowest records based on the columns and the _sortLimit function. +// The default reducer assumes no reducing needs to be performed. +_highestOrLowest = (n, _sortLimit, reducer, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> group(columns:groupColumns) + |> reducer() + |> group(columns:[]) + |> _sortLimit(n:n, columns:columns) + +lowestAverage = (n, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> _highestOrLowest( + n:n, + columns:columns, + groupColumns:groupColumns, + reducer: (tables=<-) => tables |> mean(columns:[columns[0]]), + _sortLimit: bottom, + ) + +``` diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/lowestcurrent.md b/content/v2.0/reference/flux/functions/transformations/selectors/lowestcurrent.md new file mode 100644 index 000000000..207ae46b0 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/lowestcurrent.md @@ -0,0 +1,82 @@ +--- +title: lowestCurrent() function +description: The lowestCurrent() function returns the bottom 'n' records from all groups using the last value of each group. +menu: + v2_0_ref: + name: lowestCurrent + parent: Selectors + weight: 1 +--- + +The `lowestCurrent()` function returns the bottom `n` records from all groups using the last value of each group. + +_**Function type:** Selector, Aggregate_ + +```js +lowestCurrent( + n:10, + columns: ["_value"], + groupColumns: [] +) +``` + +## Parameters + +### n +Number of records to return. + +_**Data type:** Integer_ + +### columns +List of columns by which to sort. +Sort precedence is determined by list order (left to right). +Default is `["_value"]`. + +_**Data type:** Array of strings_ + +### groupColumns +The columns on which to group before performing the aggregation. +Default is `[]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> lowestCurrent(n:10, groupColumns: ["host"]) +``` + +## Function definition +```js +// _sortLimit is a helper function, which sorts and limits a table. +_sortLimit = (n, desc, columns=["_value"], tables=<-) => + tables + |> sort(columns:columns, desc:desc) + |> limit(n:n) + +// _highestOrLowest is a helper function which reduces all groups into a single +// group by specific tags and a reducer function. It then selects the highest or +// lowest records based on the columns and the _sortLimit function. +// The default reducer assumes no reducing needs to be performed. +_highestOrLowest = (n, _sortLimit, reducer, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> group(columns:groupColumns) + |> reducer() + |> group(columns:[]) + |> _sortLimit(n:n, columns:columns) + +lowestCurrent = (n, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> _highestOrLowest( + n:n, + columns:columns, + groupColumns:groupColumns, + reducer: (tables=<-) => tables |> last(column:columns[0]), + _sortLimit: bottom, + ) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/lowestmin.md b/content/v2.0/reference/flux/functions/transformations/selectors/lowestmin.md new file mode 100644 index 000000000..fc81656d1 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/lowestmin.md @@ -0,0 +1,83 @@ +--- +title: lowestMin() function +description: The lowestMin() function returns the bottom 'n' records from all groups using the minimum of each group. +menu: + v2_0_ref: + name: lowestMin + parent: Selectors + weight: 1 +--- + +The `lowestMin()` function returns the bottom `n` records from all groups using the minimum of each group. + +_**Function type:** Selector, Aggregate_ + +```js +lowestMin( + n:10, + columns: ["_value"], + groupColumns: [] +) +``` + +## Parameters + +### n +Number of records to return. + +_**Data type:** Integer_ + +### columns +List of columns by which to sort. +Sort precedence is determined by list order (left to right). +Default is `["_value"]`. + +_**Data type:** Array of strings_ + +### groupColumns +The columns on which to group before performing the aggregation. +Default is `[]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> lowestMin(n:10, groupColumns: ["host"]) +``` + +## Function definition +```js +// _sortLimit is a helper function, which sorts and limits a table. +_sortLimit = (n, desc, columns=["_value"], tables=<-) => + tables + |> sort(columns:columns, desc:desc) + |> limit(n:n) + +// _highestOrLowest is a helper function which reduces all groups into a single +// group by specific tags and a reducer function. It then selects the highest or +// lowest records based on the columns and the _sortLimit function. +// The default reducer assumes no reducing needs to be performed. +_highestOrLowest = (n, _sortLimit, reducer, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> group(columns:groupColumns) + |> reducer() + |> group(columns:[]) + |> _sortLimit(n:n, columns:columns) + +lowestMin = (n, columns=["_value"], groupColumns=[], tables=<-) => + tables + |> _highestOrLowest( + n:n, + columns:columns, + groupColumns:groupColumns, + // TODO(nathanielc): Once max/min support selecting based on multiple columns change this to pass all columns. + reducer: (tables=<-) => tables |> min(column:columns[0]), + _sortLimit: bottom, + ) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/max.md b/content/v2.0/reference/flux/functions/transformations/selectors/max.md new file mode 100644 index 000000000..347c87da1 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/max.md @@ -0,0 +1,34 @@ +--- +title: max() function +description: The max() function selects record with the highest _value from the input table. +menu: + v2_0_ref: + name: max + parent: Selectors + weight: 1 +--- + +The `max()` function selects record with the highest `_value` from the input table. + +_**Function type:** Selector_ +_**Output data type:** Object_ + +```js +max() +``` + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> max() +``` + +
+ +##### Related InfluxQL functions and statements: +[MAX()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#max) diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/min.md b/content/v2.0/reference/flux/functions/transformations/selectors/min.md new file mode 100644 index 000000000..fa683d403 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/min.md @@ -0,0 +1,34 @@ +--- +title: min() function +description: The min() function selects record with the lowest _value from the input table. +menu: + v2_0_ref: + name: min + parent: Selectors + weight: 1 +--- + +The `min()` function selects record with the lowest `_value` from the input table. + +_**Function type:** Selector_ +_**Output data type:** Object_ + +```js +min() +``` + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> min() +``` + +
+ +##### Related InfluxQL functions and statements: +[MIN()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#min) diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/sample.md b/content/v2.0/reference/flux/functions/transformations/selectors/sample.md new file mode 100644 index 000000000..b291c34f8 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/sample.md @@ -0,0 +1,49 @@ +--- +title: sample() function +description: The sample() function selects a subset of the records from the input table. +menu: + v2_0_ref: + name: sample + parent: Selectors + weight: 1 +--- + +The `sample()` function selects a subset of the records from the input table. + +_**Function type:** Selector_ +_**Output data type:** Object_ + +```js +sample(n:5, pos: -1) +``` + +## Parameters + +### n +Sample every Nth element. + +_**Data type:** Integer_ + +### pos +The position offset from the start of results where sampling begins. +`pos` must be less than `n`. +If `pos` is less than 0, a random offset is used. +Defaults to `-1` (random offset). + +_**Data type:** Integer_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1d) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_system" + ) + |> sample(n: 5, pos: 1) +``` + +
+ +##### Related InfluxQL functions and statements: +[SAMPLE()](https://docs.influxdata.com/influxdb/latest/query_language/functions/#sample) diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/top.md b/content/v2.0/reference/flux/functions/transformations/selectors/top.md new file mode 100644 index 000000000..eae546760 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/top.md @@ -0,0 +1,54 @@ +--- +title: top() function +description: The top() function sorts a table by columns and keeps only the top n records. +menu: + v2_0_ref: + name: top + parent: Selectors + weight: 1 +--- + +The `top()` function sorts a table by columns and keeps only the top `n` records. + +_**Function type:** Selector_ +_**Output data type:** Object_ + +```js +top(n:10, columns: ["_value"]) +``` + +## Parameters + +### n +Number of records to return. + +_**Data type:** Integer_ + +### columns +List of columns by which to sort. +Sort precedence is determined by list order (left to right). +Default is `["_value"]`. + +_**Data type:** Array of strings_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> top(n:10) +``` + +## Function definition +```js +// _sortLimit is a helper function, which sorts and limits a table. +_sortLimit = (n, desc, columns=["_value"], tables=<-) => + tables + |> sort(columns:columns, desc:desc) + |> limit(n:n) + +top = (n, columns=["_value"], tables=<-) => _sortLimit(n:n, columns:columns, desc:true) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/selectors/unique.md b/content/v2.0/reference/flux/functions/transformations/selectors/unique.md new file mode 100644 index 000000000..67f7a78fe --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/selectors/unique.md @@ -0,0 +1,34 @@ +--- +title: unique() function +description: The unique() function returns all records containing unique values in a specified column. +menu: + v2_0_ref: + name: unique + parent: Selectors + weight: 1 +--- + +The `unique()` function returns all records containing unique values in a specified column. + +_**Function type:** Selector_ +_**Output data type:** Object_ + +```js +unique(column: "_value") +``` + +## Parameters + +### column +The column searched for unique values. +Defaults to `"_value"`. + +_**Data type:** String_ + +## Examples +```js +from("telegraf/autogen") + |> range(start: -15m) + |> filter(fn: (r) => r._measurement == "syslog") + |> unique(column: "message") +``` diff --git a/content/v2.0/reference/flux/functions/transformations/set.md b/content/v2.0/reference/flux/functions/transformations/set.md new file mode 100644 index 000000000..ccc90dd12 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/set.md @@ -0,0 +1,38 @@ +--- +title: set() function +description: The set() function assigns a static value to each record in the input table. +menu: + v2_0_ref: + name: set + parent: Transformations + weight: 1 +--- + +The `set()` function assigns a static value to each record in the input table. +The key may modify an existing column or add a new column to the tables. +If the modified column is part of the group key, the output tables are regrouped as needed. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +set(key: "myKey",value: "myValue") +``` + +## Parameters + +### key +The label of the column to modify or set. + +_**Data type:** String_ + +### value +The string value to set. + +_**Data type:** String_ + +## Examples +```js +from(bucket: "telegraf/autogen") + |> set(key: "host", value: "prod-node-1") +``` diff --git a/content/v2.0/reference/flux/functions/transformations/shift.md b/content/v2.0/reference/flux/functions/transformations/shift.md new file mode 100644 index 000000000..b00a76346 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/shift.md @@ -0,0 +1,48 @@ +--- +title: shift() function +description: The shift() function adds a fixed duration to time columns. +menu: + v2_0_ref: + name: shift + parent: Transformations + weight: 1 +--- + +The `shift()` function adds a fixed duration to time columns. +The output table schema is the same as the input table. +If the time is `null`, the time will continue to be `null`. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +shift(shift: 10h, columns: ["_start", "_stop", "_time"]) +``` + +## Parameters + +### shift +The amount of time to add to each time value. The shift may be a negative duration. + +_**Data type:** Duration_ + +### columns +The list of all columns to be shifted. Defaults to `["_start", "_stop", "_time"]`. + +_**Data type:** Array of strings_ + +## Examples + +###### Shift forward in time +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> shift(shift: 12h) +``` + +###### Shift backward in time +```js +from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> shift(shift: -12h) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/sort.md b/content/v2.0/reference/flux/functions/transformations/sort.md new file mode 100644 index 000000000..8b5361423 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/sort.md @@ -0,0 +1,51 @@ +--- +title: sort() function +description: The sort() function orders the records within each table. +menu: + v2_0_ref: + name: sort + parent: Transformations + weight: 1 +--- + +The `sort()` function orders the records within each table. +One output table is produced for each input table. +The output tables will have the same schema as their corresponding input tables. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +#### Sorting with null values +When sorting, `null` values will always be first. +When `desc: false`, nulls are less than every other value. +When `desc: true`, nulls are greater than every value. + +```js +sort(columns: ["_value"], desc: false) +``` + +## Parameters + +### columns +List of columns by which to sort. +Sort precedence is determined by list order (left to right). +Default is `["_value"]`. + +_**Data type:** Array of strings_ + +### desc +Sort results in descending order. +Default is `false`. + +_**Data type:** Boolean_ + +## Examples +```js +from(bucket:"telegraf/autogen") + |> range(start:-12h) + |> filter(fn: (r) => + r._measurement == "system" and + r._field == "uptime" + ) + |> sort(columns:["region", "host", "_value"]) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/statecount.md b/content/v2.0/reference/flux/functions/transformations/statecount.md new file mode 100644 index 000000000..73b727d79 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/statecount.md @@ -0,0 +1,51 @@ +--- +title: stateCount() function +description: The stateCount() function computes the number of consecutive records in a given state. +menu: + v2_0_ref: + name: stateCount + parent: Transformations + weight: 1 +--- + +The `stateCount()` function computes the number of consecutive records in a given state. +The state is defined via the function `fn`. +For each consecutive point that evaluates as `true`, the state count will be incremented. +When a point evaluates as `false`, the state count is reset. +The state count is added as an additional column to each record. + +_**Function type:** Transformation_ +_**Output data type:** Integer_ + +```js +stateCount(fn: (r) => r._field == "state", column: "stateCount") +``` + +_If the expression generates an error during evaluation, the point is discarded +and does not affect the state count._ + +## Parameters + +### fn +A single argument function that evaluates true or false to identify the state of the record. +Records are passed to the function. +Those that evaluate to `true` increment the state count. +Those that evaluate to `false` reset the state count. + +_**Data type:** Function_ + +### column +The name of the column added to each record that contains the incremented state count. + +_**Data type:** String_ + +## Examples +```js +from("monitor/autogen") + |> range(start: -1h) + |> filter(fn: (r) => r._measurement == "http") + |> stateCount( + fn: (r) => r.http_response_code == "500", + column: "server_error_count" + ) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/stateduration.md b/content/v2.0/reference/flux/functions/transformations/stateduration.md new file mode 100644 index 000000000..fc08d283b --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/stateduration.md @@ -0,0 +1,63 @@ +--- +title: stateDuration() function +description: The stateDuration() function computes the duration of a given state. +menu: + v2_0_ref: + name: stateDuration + parent: Transformations + weight: 1 +--- + +The `stateDuration()` function computes the duration of a given state. +The state is defined via the function `fn`. +For each consecutive point for that evaluates as `true`, the state duration will be +incremented by the duration between points. +When a point evaluates as `false`, the state duration is reset. +The state duration is added as an additional column to each record. + +_**Function type:** Transformation_ +_**Output data type:** Duration_ + +{{% note %}} +As the first point in the given state has no previous point, its +state duration will be 0. +{{% /note %}} + +```js +stateDuration(fn: (r) => r._measurement == "state", column: "stateDuration", unit: 1s) +``` + +_If the expression generates an error during evaluation, the point is discarded, +and does not affect the state duration._ + +## Parameters + +### fn +A single argument function that evaluates true or false to identify the state of the record. +Records are passed to the function. +Those that evaluate to `true` increment the state duration. +Those that evaluate to `false` reset the state duration. + +_**Data type:** Function_ + +### column +The name of the column added to each record that contains the state duration. + +_**Data type:** String_ + +### unit +The unit of time in which the state duration is incremented. +For example: `1s`, `1m`, `1h`, etc. + +_**Data type:** Duration_ + +## Examples +```js +from("monitor/autogen") + |> range(start: -1h) + |> filter(fn: (r) => r._measurement == "http") + |> stateDuration( + fn: (r) => r.http_response_code == "500", + column: "server_error_duration" + ) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/type-conversions/_index.md b/content/v2.0/reference/flux/functions/transformations/type-conversions/_index.md new file mode 100644 index 000000000..5b6e04c44 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/type-conversions/_index.md @@ -0,0 +1,14 @@ +--- +title: Flux type conversion functions +description: Flux type conversion functions convert columns of the input table into a specific data type. +menu: + v2_0_ref: + parent: Transformations + name: Type conversions + weight: 1 +--- + +Flux type conversion functions convert columns of the input table into a specific data type. +The following type conversion functions are available: + +{{< function-list category="Type conversions" menu="v2_0_ref" >}} diff --git a/content/v2.0/reference/flux/functions/transformations/type-conversions/tobool.md b/content/v2.0/reference/flux/functions/transformations/type-conversions/tobool.md new file mode 100644 index 000000000..3dc1e308a --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/type-conversions/tobool.md @@ -0,0 +1,35 @@ +--- +title: toBool() function +description: The toBool() function converts a value to a boolean. +menu: + v2_0_ref: + name: toBool + parent: Type conversions + weight: 1 +--- + +The `toBool()` function converts a value to a boolean. + +_**Function type:** Type conversion_ +_**Output data type:** Boolean_ + +```js +toBool() +``` + +## Examples +```js +from(bucket: "telegraf") + |> filter(fn:(r) => + r._measurement == "mem" and + r._field == "used" + ) + |> toBool() +``` + +## Function definition +```js +toBool = (tables=<-) => + tables + |> map(fn:(r) => bool(v: r._value)) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/type-conversions/toduration.md b/content/v2.0/reference/flux/functions/transformations/type-conversions/toduration.md new file mode 100644 index 000000000..b329dbaeb --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/type-conversions/toduration.md @@ -0,0 +1,35 @@ +--- +title: toDuration() function +description: The toDuration() function converts a value to a duration. +menu: + v2_0_ref: + name: toDuration + parent: Type conversions + weight: 1 +--- + +The `toDuration()` function converts a value to a duration. + +_**Function type:** Type conversion_ +_**Output data type:** Duration_ + +```js +toDuration() +``` + +## Examples +```js +from(bucket: "telegraf") + |> filter(fn:(r) => + r._measurement == "mem" and + r._field == "used" + ) + |> toDuration() +``` + +## Function definition +```js +toDuration = (tables=<-) => + tables + |> map(fn:(r) => duration(v: r._value)) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/type-conversions/tofloat.md b/content/v2.0/reference/flux/functions/transformations/type-conversions/tofloat.md new file mode 100644 index 000000000..66fba14d8 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/type-conversions/tofloat.md @@ -0,0 +1,35 @@ +--- +title: toFloat() function +description: The toFloat() function converts a value to a float. +menu: + v2_0_ref: + name: toFloat + parent: Type conversions + weight: 1 +--- + +The `toFloat()` function converts a value to a float. + +_**Function type:** Type conversion_ +_**Output data type:** Float_ + +```js +toFloat() +``` + +## Examples +```js +from(bucket: "telegraf") + |> filter(fn:(r) => + r._measurement == "mem" and + r._field == "used" + ) + |> toFloat() +``` + +## Function definition +```js +toFloat = (tables=<-) => + tables + |> map(fn:(r) => float(v: r._value)) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/type-conversions/toint.md b/content/v2.0/reference/flux/functions/transformations/type-conversions/toint.md new file mode 100644 index 000000000..565920f94 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/type-conversions/toint.md @@ -0,0 +1,35 @@ +--- +title: toInt() function +description: The toInt() function converts a value to an integer. +menu: + v2_0_ref: + name: toInt + parent: Type conversions + weight: 1 +--- + +The `toInt()` function converts a value to an integer. + +_**Function type:** Type conversion_ +_**Output data type:** Integer_ + +```js +toInt() +``` + +## Examples +```js +from(bucket: "telegraf") + |> filter(fn:(r) => + r._measurement == "mem" and + r._field == "used" + ) + |> toInt() +``` + +## Function definition +```js +toInt = (tables=<-) => + tables + |> map(fn:(r) => int(v: r._value)) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/type-conversions/tostring.md b/content/v2.0/reference/flux/functions/transformations/type-conversions/tostring.md new file mode 100644 index 000000000..89505f6b3 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/type-conversions/tostring.md @@ -0,0 +1,35 @@ +--- +title: toString() function +description: The toString() function converts a value to a string. +menu: + v2_0_ref: + name: toString + parent: Type conversions + weight: 1 +--- + +The `toString()` function converts a value to a string. + +_**Function type:** Type conversion_ +_**Output data type:** String_ + +```js +toString() +``` + +## Examples +```js +from(bucket: "telegraf") + |> filter(fn:(r) => + r._measurement == "mem" and + r._field == "used" + ) + |> toString() +``` + +## Function definition +```js +toString = (tables=<-) => + tables + |> map(fn:(r) => string(v: r._value)) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/type-conversions/totime.md b/content/v2.0/reference/flux/functions/transformations/type-conversions/totime.md new file mode 100644 index 000000000..0d8fe53a0 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/type-conversions/totime.md @@ -0,0 +1,35 @@ +--- +title: toTime() function +description: The toTime() function converts a value to a time. +menu: + v2_0_ref: + name: toTime + parent: Type conversions + weight: 1 +--- + +The `toTime()` function converts a value to a time. + +_**Function type:** Type conversion_ +_**Output data type:** Time_ + +```js +toTime() +``` + +## Examples +```js +from(bucket: "telegraf") + |> filter(fn:(r) => + r._measurement == "mem" and + r._field == "used" + ) + |> toTime() +``` + +## Function definition +```js +toTime = (tables=<-) => + tables + |> map(fn:(r) => time(v:r._value)) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/type-conversions/touint.md b/content/v2.0/reference/flux/functions/transformations/type-conversions/touint.md new file mode 100644 index 000000000..ea0368cd2 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/type-conversions/touint.md @@ -0,0 +1,35 @@ +--- +title: toUInt() function +description: The toUInt() function converts a value to an uinteger. +menu: + v2_0_ref: + name: toUInt + parent: Type conversions + weight: 1 +--- + +The `toUInt()` function converts a value to an UInteger. + +_**Function type:** Type conversion_ +_**Output data type:** UInteger_ + +```js +toUInt() +``` + +## Examples +```js +from(bucket: "telegraf") + |> filter(fn:(r) => + r._measurement == "mem" and + r._field == "used" + ) + |> toUInt() +``` + +## Function definition +```js +toUInt = (tables=<-) => + tables + |> map(fn:(r) => uint(v:r._value)) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/union.md b/content/v2.0/reference/flux/functions/transformations/union.md new file mode 100644 index 000000000..0de8683ae --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/union.md @@ -0,0 +1,52 @@ +--- +title: union() function +description: The union() function concatenates two or more input streams into a single output stream. +menu: + v2_0_ref: + name: union + parent: Transformations + weight: 1 +--- + +The `union()` function concatenates two or more input streams into a single output stream. +In tables that have identical schemas and group keys, contents of the tables will be concatenated in the output stream. +The output schemas of the `union()` function is the union of all input schemas. + +`union()` does not preserve the sort order of the rows within tables. +A sort operation may be added if a specific sort order is needed. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +union(tables: ["table1", "table2"]) +``` + +## Parameters + +### tables +Specifies the streams to union together. +There must be at least two streams. + +_**Data type:** Array of streams_ + +## Examples +```js +left = from(bucket: "test") + |> range(start: 2018-05-22T19:53:00Z, stop: 2018-05-22T19:53:50Z) + |> filter(fn: (r) => + r._field == "usage_guest" or + r._field == "usage_guest_nice" + ) + |> drop(columns: ["_start", "_stop"]) + +right = from(bucket: "test") + |> range(start: 2018-05-22T19:53:50Z, stop: 2018-05-22T19:54:20Z) + |> filter(fn: (r) => + r._field == "usage_guest" or + r._field == "usage_idle" + ) + |> drop(columns: ["_start", "_stop"]) + +union(tables: [left, right]) +``` diff --git a/content/v2.0/reference/flux/functions/transformations/window.md b/content/v2.0/reference/flux/functions/transformations/window.md new file mode 100644 index 000000000..e2331f508 --- /dev/null +++ b/content/v2.0/reference/flux/functions/transformations/window.md @@ -0,0 +1,118 @@ +--- +title: window() function +description: The window() function groups records based on a time value. +menu: + v2_0_ref: + name: window + parent: Transformations + weight: 1 +--- + +The `window()` function groups records based on a time value. +New columns are added to uniquely identify each window. +Those columns are added to the group key of the output tables. + +A single input record will be placed into zero or more output tables, depending on the specific windowing function. + +_**Function type:** Transformation_ +_**Output data type:** Object_ + +```js +window( + every: 5m, + period: 5m, + start: 12h, + timeColumn: "_time", + startColumn: "_start", + stopColumn: "_stop" +) + +// OR + +window( + intervals: intervals(every: 5m, period: 5m, offset: 12h), + timeColumn: "_time", + startColumn: "_start", + stopColumn: "_stop" +) +``` + +## Parameters + +{{% note %}} +`every`,`period` or `intervals` is required. +{{% /note %}} + +### every +Duration of time between windows. +Defaults to `period` value. + +_**Data type:** Duration_ + +### period +Duration of the window. +Period is the length of each interval. +It can be negative, indicating the start and stop boundaries are reversed. +Defaults to `every` value. + +_**Data type:** Duration_ + +### start +The start window time relative to the [`location`](/v2.0/reference/flux/language/options/#location) offset. +It can be negative, indicating that the start goes backwards in time. +The default aligns the window boundaries with `now`. + +_**Data type:** Duration_ + +### intervals +A function that returns an interval generator, a set of intervals used as windows. + +_**Data type:** Function_ + +###### Example interval generator function +```js +intervals(every:1d, period:8h, offset:9h) +``` + +> When `intervals` is used, `every`, `period`, and `start` cannot be used or need to be set to 0. + +### timeColumn +The column containing time. +Defaults to `"_time"`. + +_**Data type:** String_ + +### startColumn +The column containing the window start time. +Defaults to `"_start"`. + +_**Data type:** String_ + +### stopColumn +The column containing the window stop time. +Defaults to `"_stop"`. + +_**Data type:** String_ + +## Examples + +#### Window data into 10 minute intervals +```js +from(bucket:"telegraf/autogen") + |> range(start:-12h) + |> window(every:10m) + // ... +``` + +#### Window data using intervals function +The following windows data into 8 hour intervals starting at 9AM every day. +```js +from(bucket:"telegraf/autogen") + |> range(start:-12h) + |> window(intervals: intervals(every:1d, period:8h, offset:9h)) +``` + +
+ +##### Related InfluxQL functions and statements: +[GROUP BY time()](https://docs.influxdata.com/influxdb/latest/query_language/data_exploration/#the-group-by-clause) diff --git a/content/v2.0/reference/flux/language/built-ins/_index.md b/content/v2.0/reference/flux/language/built-ins/_index.md index b235eb5d0..28f44d1b0 100644 --- a/content/v2.0/reference/flux/language/built-ins/_index.md +++ b/content/v2.0/reference/flux/language/built-ins/_index.md @@ -13,8 +13,8 @@ menu: Flux contains many preassigned values. These preassigned values are defined in the source files for the various built-in packages. -## [System built-ins](/flux/v0.x/language/built-ins/system-built-ins) +## [System built-ins](/v2.0/reference/flux/language/built-ins/system-built-ins) When a built-in value is not expressible in Flux, its value may be defined by the hosting environment. -## [Time constants](/flux/v0.x/language/built-ins/time-constants) +## [Time constants](/v2.0/reference/flux/language/built-ins/time-constants) When a built-in value is not expressible in Flux, its value may be defined by the hosting environment. diff --git a/content/v2.0/reference/flux/language/packages.md b/content/v2.0/reference/flux/language/packages.md index 5ed95ef91..6dabf4004 100644 --- a/content/v2.0/reference/flux/language/packages.md +++ b/content/v2.0/reference/flux/language/packages.md @@ -5,7 +5,7 @@ description: > A package consists of one or more source files. Each source file is parsed individually and composed into a single package. aliases: - - /flux/v0.x/language/programs + - /v2.0/reference/flux/language/programs menu: v2_0_ref: parent: Flux language specification diff --git a/layouts/shortcodes/function-list.html b/layouts/shortcodes/function-list.html new file mode 100644 index 000000000..34a256068 --- /dev/null +++ b/layouts/shortcodes/function-list.html @@ -0,0 +1,33 @@ +{{ $category := (.Get "category") }} +{{ $menu := (.Get "menu")}} +
    +{{ range (index .Site.Menus $menu) }} + {{ if .HasChildren}} + {{ range .Children }} + {{ if eq .Parent $category }} + {{ if not .HasChildren }} +
  • {{ .Name }}
  • + {{ end }} + {{ end }} + {{ if .HasChildren}} + {{ range .Children }} + {{ if eq .Parent $category }} + {{ if not .HasChildren }} +
  • {{ .Name }}
  • + {{ end }} + {{ end }} + {{ if .HasChildren}} + {{ range .Children }} + {{ if eq .Parent $category }} + {{ if not .HasChildren }} +
  • {{ .Name }}
  • + {{ end }} + {{ end }} + {{ end }} + {{ end }} + {{ end }} + {{ end }} + {{ end }} + {{ end }} +{{ end }} +
From 11f4402511a82c46fefac53671c5e3fa999c13d9 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Mon, 21 Jan 2019 23:12:12 -0700 Subject: [PATCH 07/14] create flux guides --- .../query-data/flux/get-started/_index.md | 2 +- .../flux/get-started/transform-data.md | 2 +- content/v2.0/query-data/flux/guides/_index.md | 28 + .../query-data/flux/guides/execute-queries.md | 85 +++ .../v2.0/query-data/flux/guides/group-data.md | 671 ++++++++++++++++++ .../v2.0/query-data/flux/guides/histograms.md | 139 ++++ content/v2.0/query-data/flux/guides/join.md | 300 ++++++++ .../flux/guides/regular-expressions.md | 85 +++ .../v2.0/query-data/flux/guides/sort-limit.md | 47 ++ .../flux/guides/window-aggregate.md | 331 +++++++++ 10 files changed, 1688 insertions(+), 2 deletions(-) create mode 100644 content/v2.0/query-data/flux/guides/_index.md create mode 100644 content/v2.0/query-data/flux/guides/execute-queries.md create mode 100644 content/v2.0/query-data/flux/guides/group-data.md create mode 100644 content/v2.0/query-data/flux/guides/histograms.md create mode 100644 content/v2.0/query-data/flux/guides/join.md create mode 100644 content/v2.0/query-data/flux/guides/regular-expressions.md create mode 100644 content/v2.0/query-data/flux/guides/sort-limit.md create mode 100644 content/v2.0/query-data/flux/guides/window-aggregate.md diff --git a/content/v2.0/query-data/flux/get-started/_index.md b/content/v2.0/query-data/flux/get-started/_index.md index fafaaf34e..afe2148cf 100644 --- a/content/v2.0/query-data/flux/get-started/_index.md +++ b/content/v2.0/query-data/flux/get-started/_index.md @@ -51,7 +51,7 @@ are unique to each row. ## Tools for working with Flux -You have multiple [options for writing and running Flux queries](/v2.0/reference/flux/guides/executing-queries), +You have multiple [options for writing and running Flux queries](/v2.0/reference/flux/guides/execute-queries), but as you're getting started, we recommend using the following: ### 1. Data Explorer diff --git a/content/v2.0/query-data/flux/get-started/transform-data.md b/content/v2.0/query-data/flux/get-started/transform-data.md index e0e9eba49..b075ca8d7 100644 --- a/content/v2.0/query-data/flux/get-started/transform-data.md +++ b/content/v2.0/query-data/flux/get-started/transform-data.md @@ -166,7 +166,7 @@ and your own custom functions, but this is a good introduction into the basic sy --- _For a deeper dive into windowing and aggregating data with example data output for each transformation, -view the [Windowing and aggregating data](/v2.0/reference/flux/guides/windowing-aggregating) guide._ +view the [Windowing and aggregating data](/v2.0/reference/flux/guides/window-aggregate) guide._ --- diff --git a/content/v2.0/query-data/flux/guides/_index.md b/content/v2.0/query-data/flux/guides/_index.md new file mode 100644 index 000000000..77be5ea8d --- /dev/null +++ b/content/v2.0/query-data/flux/guides/_index.md @@ -0,0 +1,28 @@ +--- +title: Flux how-to guides +description: Helpful guides that walk through both common and complex tasks and use cases for Flux. +menu: + v2_0: + name: How-to guides + parent: Flux + weight: 3 +--- + +## [Different ways to execute Flux queries](/v2.0/query-data/flux/guides/execute-queries) +A guide that covers the different options for executing Flux queries with InfluxDB and Chronograf v1.7+. + +## [How to window and aggregate data with Flux](/v2.0/query-data/flux/guides/window-aggregate) +This guide walks through windowing and aggregating data with Flux and demonstrates +how data is shaped in the process. + +## [How to group data with Flux](/v2.0/query-data/flux/guides/group-data) +This guide walks through grouping data in Flux with examples of how data is shaped in the process. + +## [How to join data with Flux](/v2.0/query-data/flux/guides/join) +This guide walks through joining data with Flux, illustrating how joined data is output and how it can be used. + +## [How to sort and limit data with Flux](/v2.0/query-data/flux/guides/sort-limit) +This guide walks through sorting and limiting data with Flux. + +## [Regular expressions in Flux](/v2.0/query-data/flux/guides/regular-expressions) +This guide walks through using regular expressions in evaluation logic in Flux functions. diff --git a/content/v2.0/query-data/flux/guides/execute-queries.md b/content/v2.0/query-data/flux/guides/execute-queries.md new file mode 100644 index 000000000..1e94bb6f1 --- /dev/null +++ b/content/v2.0/query-data/flux/guides/execute-queries.md @@ -0,0 +1,85 @@ +--- +title: Execute Flux queries +seotitle: Different ways to execute Flux queries +description: There are multiple ways to execute Flux queries include the InfluxDB user interface, CLI, and API. +menu: + v2_0: + name: Execute Flux queries + parent: How-to guides + weight: 1 +--- + +There are multiple ways to execute Flux queries with InfluxDB and Chronograf v1.7+. +This guide covers the different options: + +1. [Data Explorer](#data-explorer) +2. [Influx REPL](#influx-repl) +3. [Influx query command](#influx-query-command) +5. [InfluxDB API](#influxdb-api) + +## Data Explorer +Flux queries can be built, executed, and visualized in InfluxDB UI's Data Explorer. + +![Data Explorer with Flux](/img/flux-data-explorer.png) + +## Influx REPL +The [`influx repl` command](/v2.0/reference/cli/influx/repl) starts an interactive +read-eval-print-loop (REPL) where you can write and execute Flux queries. + +```bash +influx repl --org org-name +``` + +## Influx query command +You can pass Flux queries to the [`influx query` command](/v2.0/reference/cli/influx/query) +as either a file or raw Flux via stdin. + +###### Run a Flux query from a file +```bash +influx query @/path/to/query.flux +``` + +###### Pass raw Flux via stdin pipe +```bash +influx query - # Return to open the pipe + +data = from(bucket: "example-bucket") |> range(start: -10m) # ... +# ctrl-d to close the pipe and submit the query +``` + +## InfluxDB API +Flux can be used to query InfluxDB through InfluxDB's `/api/v2/query` endpoint. +Queried data is returned in annotated CSV format. + +In your request, set the following: + +- `accept` header to `application/csv` +- `content-type` header to `application/vnd.flux` + +This allows you to POST the Flux query in plain text and receive the annotated CSV response. + +Below is an example `curl` command that queries InfluxDB using Flux: + +{{< code-tabs-wrapper >}} +{{% code-tabs %}} +[Multi-line](#) +[Single-line](#) +{{% /code-tabs %}} + +{{% code-tab-content %}} +```bash +curl localhost:8086/api/v2/query -XPOST -sS \ +-H 'accept:application/csv' \ +-H 'content-type:application/vnd.flux' \ +-d 'from(bucket:"telegraf") + |> range(start:-5m) + |> filter(fn:(r) => r._measurement == "cpu")' +``` +{{% /code-tab-content %}} + +{{% code-tab-content %}} +```bash +curl localhost:8086/api/v2/query -XPOST -sS -H 'accept:application/csv' -H 'content-type:application/vnd.flux' -d 'from(bucket:"telegraf") |> range(start:-5m) |> filter(fn:(r) => r._measurement == "cpu")' +``` +{{% /code-tab-content %}} +{{< /code-tabs-wrapper >}} diff --git a/content/v2.0/query-data/flux/guides/group-data.md b/content/v2.0/query-data/flux/guides/group-data.md new file mode 100644 index 000000000..1b6e6191f --- /dev/null +++ b/content/v2.0/query-data/flux/guides/group-data.md @@ -0,0 +1,671 @@ +--- +title: Group data with Flux +seotitle: How to group data with Flux +description: > + This guide walks through grouping data with Flux by providing examples and + illustrating how data is shaped throughout the process. +menu: + v2_0: + name: Group data + parent: How-to guides + weight: 3 +--- + +With Flux, data can be grouped by any column in your queried data set. +"Grouping" is accomplished by partitioning data into tables where each row shares a common value for specified columns. +This guide walks through grouping data in Flux with examples of how data is shaped in the process. + +## Group keys +Every table has a **group key** – a list of columns which for which every row in the table has the same value. + +###### Example group key +```js +[_start, _stop, _field, _measurement, host] +``` + +Grouping data in Flux is essentially defining the group key of output tables. +Understanding how modifying group keys shapes output data is key to successfully +grouping and transforming data into your desired output. + +## group() Function +Flux's [`group()` function](/v2.0/reference/flux/functions/transformations/group) defines the +group key for output tables, i.e. grouping records based on values for specific columns. + +###### group() example +```js +dataStream + |> group(columns: ["cpu", "host"]) +``` + +###### Resulting group key +```js +[cpu, host] +``` + +The `group()` function has the following parameters: + +### by +An explicit method for defining the group key with an array of strings. +Only columns specified are included in the output group key. + +### except +An implicit method for defining the group key with an array of strings. +All columns **except** those specified are included in the output group key. + +### none +A boolean that removes all grouping and outputs everything as a single table. + +--- + +_For more information, see the [`group()` function](/v2.0/reference/flux/functions/transformations/group)._ + +--- + +## Example grouping operations +To illustrate how grouping works, define a `dataSet` variable that queries System +CPU usage from the `telegraf/autogen` bucket. +Filter the `cpu` tag so it only returns results for each numbered CPU core. + +### Data set +CPU used by system operations for all numbered CPU cores. +It uses a regular expression to filter only numbered cores. + +```js +dataSet = from(bucket: "telegraf/autogen") + |> range(start: -2m) + |> filter(fn: (r) => + r._field == "usage_system" and + r.cpu =~ /cpu[0-9*]/ + ) + |> drop(columns: ["host"]) +``` + +> This example drops the `host` column from the returned data since the CPU data +> is only tracked for a single host and it simplifies the output tables. +> Don't drop the `host` column if monitoring multiple hosts. + +{{% truncate %}} +``` +Table: keys: [_start, _stop, _field, _measurement, cpu] + _start:time _stop:time _field:string _measurement:string cpu:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:34:00.000000000Z 7.892107892107892 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:34:10.000000000Z 7.2 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:34:20.000000000Z 7.4 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:34:30.000000000Z 5.5 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:34:40.000000000Z 7.4 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:34:50.000000000Z 7.5 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:35:00.000000000Z 10.3 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:35:10.000000000Z 9.2 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:35:20.000000000Z 8.4 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:35:30.000000000Z 8.5 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:35:40.000000000Z 8.6 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:35:50.000000000Z 10.2 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu0 2018-11-05T21:36:00.000000000Z 10.6 + +Table: keys: [_start, _stop, _field, _measurement, cpu] + _start:time _stop:time _field:string _measurement:string cpu:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:34:00.000000000Z 0.7992007992007992 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:34:10.000000000Z 0.7 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:34:20.000000000Z 0.7 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:34:30.000000000Z 0.4 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:34:40.000000000Z 0.7 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:34:50.000000000Z 0.7 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:35:00.000000000Z 1.4 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:35:10.000000000Z 1.2 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:35:20.000000000Z 0.8 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:35:30.000000000Z 0.8991008991008991 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:35:40.000000000Z 0.8008008008008008 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:35:50.000000000Z 0.999000999000999 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu1 2018-11-05T21:36:00.000000000Z 1.1022044088176353 + +Table: keys: [_start, _stop, _field, _measurement, cpu] + _start:time _stop:time _field:string _measurement:string cpu:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:34:00.000000000Z 4.1 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:34:10.000000000Z 3.6 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:34:20.000000000Z 3.5 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:34:30.000000000Z 2.6 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:34:40.000000000Z 4.5 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:34:50.000000000Z 4.895104895104895 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:35:00.000000000Z 6.906906906906907 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:35:10.000000000Z 5.7 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:35:20.000000000Z 5.1 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:35:30.000000000Z 4.7 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:35:40.000000000Z 5.1 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:35:50.000000000Z 5.9 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu2 2018-11-05T21:36:00.000000000Z 6.4935064935064934 + +Table: keys: [_start, _stop, _field, _measurement, cpu] + _start:time _stop:time _field:string _measurement:string cpu:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:34:00.000000000Z 0.5005005005005005 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:34:10.000000000Z 0.5 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:34:20.000000000Z 0.5 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:34:30.000000000Z 0.3 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:34:40.000000000Z 0.6 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:34:50.000000000Z 0.6 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:35:00.000000000Z 1.3986013986013985 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:35:10.000000000Z 0.9 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:35:20.000000000Z 0.5005005005005005 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:35:30.000000000Z 0.7 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:35:40.000000000Z 0.6 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:35:50.000000000Z 0.8 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z usage_system cpu cpu3 2018-11-05T21:36:00.000000000Z 0.9 +``` +{{% /truncate %}} + +**Note that the group key is output with each table: `Table: keys: `.** + +![Group example data set](/img/flux/grouping-data-set.png) + +### Group by CPU +Group the `dataSet` stream by the `cpu` column. + +```js +dataSet + |> group(columns: ["cpu"]) +``` + +This won't actually change the structure of the data since it already has `cpu` in the group key and is therefore grouped by `cpu`. +However, notice that it does change the group key: + +{{% truncate %}} +###### Group by CPU output tables +``` +Table: keys: [cpu] + cpu:string _stop:time _time:time _value:float _field:string _measurement:string _start:time +---------------------- ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:00.000000000Z 7.892107892107892 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:10.000000000Z 7.2 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:20.000000000Z 7.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:30.000000000Z 5.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:40.000000000Z 7.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:50.000000000Z 7.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:00.000000000Z 10.3 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:10.000000000Z 9.2 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:20.000000000Z 8.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:30.000000000Z 8.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:40.000000000Z 8.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:50.000000000Z 10.2 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu0 2018-11-05T21:36:00.000000000Z 2018-11-05T21:36:00.000000000Z 10.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [cpu] + cpu:string _stop:time _time:time _value:float _field:string _measurement:string _start:time +---------------------- ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:00.000000000Z 0.7992007992007992 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:10.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:20.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:30.000000000Z 0.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:40.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:50.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:00.000000000Z 1.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:10.000000000Z 1.2 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:20.000000000Z 0.8 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:30.000000000Z 0.8991008991008991 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:40.000000000Z 0.8008008008008008 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:50.000000000Z 0.999000999000999 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu1 2018-11-05T21:36:00.000000000Z 2018-11-05T21:36:00.000000000Z 1.1022044088176353 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [cpu] + cpu:string _stop:time _time:time _value:float _field:string _measurement:string _start:time +---------------------- ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:00.000000000Z 4.1 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:10.000000000Z 3.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:20.000000000Z 3.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:30.000000000Z 2.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:40.000000000Z 4.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:50.000000000Z 4.895104895104895 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:00.000000000Z 6.906906906906907 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:10.000000000Z 5.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:20.000000000Z 5.1 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:30.000000000Z 4.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:40.000000000Z 5.1 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:50.000000000Z 5.9 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu2 2018-11-05T21:36:00.000000000Z 2018-11-05T21:36:00.000000000Z 6.4935064935064934 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [cpu] + cpu:string _stop:time _time:time _value:float _field:string _measurement:string _start:time +---------------------- ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:00.000000000Z 0.5005005005005005 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:10.000000000Z 0.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:20.000000000Z 0.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:30.000000000Z 0.3 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:40.000000000Z 0.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:50.000000000Z 0.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:00.000000000Z 1.3986013986013985 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:10.000000000Z 0.9 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:20.000000000Z 0.5005005005005005 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:30.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:40.000000000Z 0.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:35:50.000000000Z 0.8 usage_system cpu 2018-11-05T21:34:00.000000000Z + cpu3 2018-11-05T21:36:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.9 usage_system cpu 2018-11-05T21:34:00.000000000Z +``` +{{% /truncate %}} + +The visualization remains the same. + +![Group by CPU](/img/flux/grouping-data-set.png) + +### Group by time +Grouping data by the `_time` column is a good illustration of how grouping changes the structure of your data. + +```js +dataSet + |> group(columns: ["_time"]) +``` + +When grouping by `_time`, all records that share a common `_time` value are grouped into individual tables. +So each output table represents a single point in time. + +{{% truncate %}} +###### Group by time output tables +``` +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:34:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 7.892107892107892 usage_system cpu cpu0 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.7992007992007992 usage_system cpu cpu1 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 4.1 usage_system cpu cpu2 +2018-11-05T21:34:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.5005005005005005 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:34:10.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 7.2 usage_system cpu cpu0 +2018-11-05T21:34:10.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu cpu1 +2018-11-05T21:34:10.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 3.6 usage_system cpu cpu2 +2018-11-05T21:34:10.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.5 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:34:20.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 7.4 usage_system cpu cpu0 +2018-11-05T21:34:20.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu cpu1 +2018-11-05T21:34:20.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 3.5 usage_system cpu cpu2 +2018-11-05T21:34:20.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.5 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:34:30.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 5.5 usage_system cpu cpu0 +2018-11-05T21:34:30.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.4 usage_system cpu cpu1 +2018-11-05T21:34:30.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 2.6 usage_system cpu cpu2 +2018-11-05T21:34:30.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.3 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:34:40.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 7.4 usage_system cpu cpu0 +2018-11-05T21:34:40.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu cpu1 +2018-11-05T21:34:40.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 4.5 usage_system cpu cpu2 +2018-11-05T21:34:40.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.6 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:34:50.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 7.5 usage_system cpu cpu0 +2018-11-05T21:34:50.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu cpu1 +2018-11-05T21:34:50.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 4.895104895104895 usage_system cpu cpu2 +2018-11-05T21:34:50.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.6 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:35:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 10.3 usage_system cpu cpu0 +2018-11-05T21:35:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 1.4 usage_system cpu cpu1 +2018-11-05T21:35:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 6.906906906906907 usage_system cpu cpu2 +2018-11-05T21:35:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 1.3986013986013985 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:35:10.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 9.2 usage_system cpu cpu0 +2018-11-05T21:35:10.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 1.2 usage_system cpu cpu1 +2018-11-05T21:35:10.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 5.7 usage_system cpu cpu2 +2018-11-05T21:35:10.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.9 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:35:20.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 8.4 usage_system cpu cpu0 +2018-11-05T21:35:20.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.8 usage_system cpu cpu1 +2018-11-05T21:35:20.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 5.1 usage_system cpu cpu2 +2018-11-05T21:35:20.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.5005005005005005 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:35:30.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 8.5 usage_system cpu cpu0 +2018-11-05T21:35:30.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.8991008991008991 usage_system cpu cpu1 +2018-11-05T21:35:30.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 4.7 usage_system cpu cpu2 +2018-11-05T21:35:30.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:35:40.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 8.6 usage_system cpu cpu0 +2018-11-05T21:35:40.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.8008008008008008 usage_system cpu cpu1 +2018-11-05T21:35:40.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 5.1 usage_system cpu cpu2 +2018-11-05T21:35:40.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.6 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:35:50.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 10.2 usage_system cpu cpu0 +2018-11-05T21:35:50.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.999000999000999 usage_system cpu cpu1 +2018-11-05T21:35:50.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 5.9 usage_system cpu cpu2 +2018-11-05T21:35:50.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.8 usage_system cpu cpu3 + +Table: keys: [_time] + _time:time _start:time _stop:time _value:float _field:string _measurement:string cpu:string +------------------------------ ------------------------------ ------------------------------ ---------------------------- ---------------------- ---------------------- ---------------------- +2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 10.6 usage_system cpu cpu0 +2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 1.1022044088176353 usage_system cpu cpu1 +2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 6.4935064935064934 usage_system cpu cpu2 +2018-11-05T21:36:00.000000000Z 2018-11-05T21:34:00.000000000Z 2018-11-05T21:36:00.000000000Z 0.9 usage_system cpu cpu3 +``` +{{% /truncate %}} + +Because each timestamp is a structured as a separate table, when visualized, they appear as individual, unconnected points. +Even though there are multiple records per timestamp, it will only visualize the last record of the table. + +![Group by time](/img/flux/grouping-by-time.png) + +> With some further processing, you could calculate the average CPU usage across all CPUs per point +> of time and group them into a single table, but we won't cover that in this example. +> If you're interested in running and visualizing this yourself, here's what the query would look like: +> +```js +dataSet + |> group(columns: ["_time"]) + |> mean() + |> group(columns: ["_value", "_time"], mode: "except") +``` + +## Group by CPU and time +Group by the `cpu` and `_time` columns. + +```js +dataSet + |> group(columns: ["cpu", "_time"]) +``` + +This outputs a table for every unique `cpu` and `_time` combination: + +{{% truncate %}} +###### Group by CPU and time output tables +``` +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:00.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 7.892107892107892 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:00.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.7992007992007992 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:00.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 4.1 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:00.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.5005005005005005 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:10.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 7.2 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:10.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:10.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 3.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:10.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:20.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 7.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:20.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:20.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 3.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:20.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:30.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 5.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:30.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:30.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 2.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:30.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.3 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:40.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 7.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:40.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:40.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 4.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:40.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:50.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 7.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:50.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:50.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 4.895104895104895 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:34:50.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:00.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 10.3 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:00.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 1.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:00.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 6.906906906906907 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:00.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 1.3986013986013985 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:10.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 9.2 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:10.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 1.2 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:10.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 5.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:10.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.9 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:20.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 8.4 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:20.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.8 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:20.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 5.1 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:20.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.5005005005005005 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:30.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 8.5 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:30.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.8991008991008991 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:30.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 4.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:30.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.7 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:40.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 8.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:40.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.8008008008008008 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:40.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 5.1 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:40.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:50.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 10.2 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:50.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 0.999000999000999 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:50.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 5.9 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:35:50.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.8 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:36:00.000000000Z cpu0 2018-11-05T21:36:00.000000000Z 10.6 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:36:00.000000000Z cpu1 2018-11-05T21:36:00.000000000Z 1.1022044088176353 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:36:00.000000000Z cpu2 2018-11-05T21:36:00.000000000Z 6.4935064935064934 usage_system cpu 2018-11-05T21:34:00.000000000Z + +Table: keys: [_time, cpu] + _time:time cpu:string _stop:time _value:float _field:string _measurement:string _start:time +------------------------------ ---------------------- ------------------------------ ---------------------------- ---------------------- ---------------------- ------------------------------ +2018-11-05T21:36:00.000000000Z cpu3 2018-11-05T21:36:00.000000000Z 0.9 usage_system cpu 2018-11-05T21:34:00.000000000Z +``` +{{% /truncate %}} + +When visualized, tables appear as individual, unconnected points. + +![Group by CPU and time](/img/flux/grouping-by-cpu-time.png) + +Grouping by `cpu` and `_time` is a good illustration of how grouping works. + +## In conclusion +Grouping is a powerful way to shape your data into your desired output format. +It modifies the group keys of output tables, grouping records into tables that +all share common values within specified columns. diff --git a/content/v2.0/query-data/flux/guides/histograms.md b/content/v2.0/query-data/flux/guides/histograms.md new file mode 100644 index 000000000..ff6f46543 --- /dev/null +++ b/content/v2.0/query-data/flux/guides/histograms.md @@ -0,0 +1,139 @@ +--- +title: Create histograms with Flux +seotitle: How to create histograms with Flux +description: This guide walks through using the histogram() function to create cumulative histograms with Flux. +menu: + v2_0: + name: Create histograms + parent: How-to guides + weight: 7 +--- + + +Histograms provide valuable insight into the distribution of your data. +This guide walks through using Flux's `histogram()` function to transform your data into a **cumulative histogram**. + +## histgram() function +The [`histogram()` function](/v2.0/reference/flux/functions/transformations/histogram) approximates the +cumulative distribution of a dataset by counting data frequencies for a list of "bins." +A **bin** is simply a range in which a data point falls. +All data points that are less than or equal to the bound are counted in the bin. +In the histogram output, a column is added (le) that represents the upper bounds of of each bin. +Bin counts are cumulative. + +```js +from(bucket:"telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> histogram(bins: [0.0, 10.0, 20.0, 30.0]) +``` + +> Values output by the `histogram` function represent points of data aggregated over time. +> Since values do not represent single points in time, there is no `_time` column in the output table. + +## Bin helper functions +Flux provides two helper functions for generating histogram bins. +Each generates and outputs an array of floats designed to be used in the `histogram()` function's `bins` parameter. + +### linearBins() +The [`linearBins()` function](/v2.0/reference/flux/functions/misc/linearbins) generates a list of linearly separated floats. + +```js +linearBins(start: 0.0, width: 10.0, count: 10) + +// Generated list: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, +Inf] +``` + +### logarithmicBins() +The [`logarithmicBins()` function](/v2.0/reference/flux/functions/misc/logarithmicbins) generates a list of exponentially separated floats. + +```js +logarithmicBins(start: 1.0, factor: 2.0, count: 10, infinty: true) + +// Generated list: [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, +Inf] +``` + +## Examples + +### Generating a histogram with linear bins +```js +from(bucket:"telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> histogram( + bins: linearBins( + start:65.5, + width: 0.5, + count: 20, + infinity:false + ) + ) +``` + +###### Output table +``` +Table: keys: [_start, _stop, _field, _measurement, host] + _start:time _stop:time _field:string _measurement:string host:string le:float _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------ ---------------------------- ---------------------------- +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 65.5 5 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 66 6 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 66.5 8 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 67 9 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 67.5 9 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 68 10 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 68.5 12 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 69 12 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 69.5 15 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 70 23 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 70.5 30 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 71 30 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 71.5 30 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 72 30 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 72.5 30 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 73 30 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 73.5 30 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 74 30 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 74.5 30 +2018-11-07T22:19:58.423358000Z 2018-11-07T22:24:58.423358000Z used_percent mem Scotts-MacBook-Pro.local 75 30 +``` + +### Generating a histogram with logarithmic bins +```js +from(bucket:"telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> histogram( + bins: logarithmicBins( + start:0.5, + factor: 2.0, + count: 10, + infinity:false + ) + ) +``` + +###### Output table +``` +Table: keys: [_start, _stop, _field, _measurement, host] + _start:time _stop:time _field:string _measurement:string host:string le:float _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------ ---------------------------- ---------------------------- +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 0.5 0 +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 1 0 +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 2 0 +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 4 0 +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 8 0 +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 16 0 +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 32 0 +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 64 2 +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 128 30 +2018-11-07T22:23:36.860664000Z 2018-11-07T22:28:36.860664000Z used_percent mem Scotts-MacBook-Pro.local 256 30 +``` diff --git a/content/v2.0/query-data/flux/guides/join.md b/content/v2.0/query-data/flux/guides/join.md new file mode 100644 index 000000000..521bf799b --- /dev/null +++ b/content/v2.0/query-data/flux/guides/join.md @@ -0,0 +1,300 @@ +--- +title: Join data with Flux +seotitle: How to join data with Flux +description: This guide walks through joining data with Flux and outlines how it shapes your data in the process. +menu: + v2_0: + name: Join data + parent: How-to guides + weight: 5 +--- + +The [`join()` function](/v2.0/reference/flux/functions/transformations/join) merges two or more +input streams whose values are equal on a set of common columns into a single output stream. +Flux allows you to join on any columns common between two data streams and opens the door +for operations such as cross-measurement joins and math across measurements. + +To illustrate a join operation, use data captured by Telegraf and and stored in +InfluxDB with a default TICK stack installation - memory usage and processes. + +In this guide, we'll join two data streams, one representing memory usage and the other representing the +total number of running processes, then calculate the average memory usage per running process. + +## Define stream variables +In order to perform a join, you must have two streams of data. +Assign a variable to each data stream. + +### Memory used variable +Define a `memUsed` variable that filters on the `mem` measurement and the `used` field. +This returns the amount of memory (in bytes) used. + +###### memUsed stream definition +```js +memUsed = from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used" + ) +``` + +{{% truncate %}} +###### memUsed data output +``` +Table: keys: [_start, _stop, _field, _measurement, host] + _start:time _stop:time _field:string _measurement:string host:string _time:time _value:int +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------ ------------------------------ -------------------------- +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:50:00.000000000Z 10956333056 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:50:10.000000000Z 11014008832 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:50:20.000000000Z 11373428736 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:50:30.000000000Z 11001421824 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:50:40.000000000Z 10985852928 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:50:50.000000000Z 10992279552 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:51:00.000000000Z 11053568000 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:51:10.000000000Z 11092242432 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:51:20.000000000Z 11612774400 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:51:30.000000000Z 11131961344 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:51:40.000000000Z 11124805632 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:51:50.000000000Z 11332464640 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:52:00.000000000Z 11176923136 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:52:10.000000000Z 11181068288 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:52:20.000000000Z 11182579712 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:52:30.000000000Z 11238862848 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:52:40.000000000Z 11275296768 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:52:50.000000000Z 11225411584 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:53:00.000000000Z 11252690944 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:53:10.000000000Z 11227029504 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:53:20.000000000Z 11201646592 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:53:30.000000000Z 11227897856 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:53:40.000000000Z 11330428928 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:53:50.000000000Z 11347976192 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:54:00.000000000Z 11368271872 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:54:10.000000000Z 11269623808 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:54:20.000000000Z 11295637504 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:54:30.000000000Z 11354423296 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:54:40.000000000Z 11379687424 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:54:50.000000000Z 11248926720 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z used mem host1.local 2018-11-06T05:55:00.000000000Z 11292524544 +``` +{{% /truncate %}} + +### Total processes variable +Define a `procTotal` variable that filters on the `processes` measurement and the `total` field. +This returns the number of running processes. + +###### procTotal stream definition +```js +procTotal = from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "processes" and + r._field == "total" + ) +``` + +{{% truncate %}} +###### procTotal data output +``` +Table: keys: [_start, _stop, _field, _measurement, host] + _start:time _stop:time _field:string _measurement:string host:string _time:time _value:int +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------ ------------------------------ -------------------------- +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:50:00.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:50:10.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:50:20.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:50:30.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:50:40.000000000Z 469 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:50:50.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:51:00.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:51:10.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:51:20.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:51:30.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:51:40.000000000Z 469 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:51:50.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:52:00.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:52:10.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:52:20.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:52:30.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:52:40.000000000Z 472 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:52:50.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:53:00.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:53:10.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:53:20.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:53:30.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:53:40.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:53:50.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:54:00.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:54:10.000000000Z 470 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:54:20.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:54:30.000000000Z 473 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:54:40.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:54:50.000000000Z 471 +2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z total processes host1.local 2018-11-06T05:55:00.000000000Z 471 +``` +{{% /truncate %}} + +## Join the two data streams +With the two data streams defined, use the `join()` function to join them together. +`join()` requires two parameters: + +##### `tables` +A map of tables to join with keys by which they will be aliased. +In the example below, `mem` is the alias for `memUsed` and `proc` is the alias for `procTotal`. + +##### `on` +An array of strings defining the columns on which the tables will be joined. +_**Both tables must have all columns defined in this list.**_ + +```js +join( + tables: {mem:memUsed, proc:procTotal}, + on: ["_time", "_stop", "_start", "host"] +) +``` + +{{% truncate %}} +###### Joined output table +``` +Table: keys: [_field_mem, _field_proc, _measurement_mem, _measurement_proc, _start, _stop, host] + _field_mem:string _field_proc:string _measurement_mem:string _measurement_proc:string _start:time _stop:time host:string _time:time _value_mem:int _value_proc:int +---------------------- ---------------------- ----------------------- ------------------------ ------------------------------ ------------------------------ ------------------------ ------------------------------ -------------------------- -------------------------- + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:00.000000000Z 10956333056 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:10.000000000Z 11014008832 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:20.000000000Z 11373428736 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:30.000000000Z 11001421824 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:40.000000000Z 10985852928 469 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:50.000000000Z 10992279552 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:00.000000000Z 11053568000 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:10.000000000Z 11092242432 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:20.000000000Z 11612774400 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:30.000000000Z 11131961344 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:40.000000000Z 11124805632 469 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:50.000000000Z 11332464640 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:00.000000000Z 11176923136 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:10.000000000Z 11181068288 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:20.000000000Z 11182579712 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:30.000000000Z 11238862848 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:40.000000000Z 11275296768 472 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:50.000000000Z 11225411584 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:00.000000000Z 11252690944 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:10.000000000Z 11227029504 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:20.000000000Z 11201646592 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:30.000000000Z 11227897856 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:40.000000000Z 11330428928 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:50.000000000Z 11347976192 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:00.000000000Z 11368271872 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:10.000000000Z 11269623808 470 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:20.000000000Z 11295637504 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:30.000000000Z 11354423296 473 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:40.000000000Z 11379687424 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:50.000000000Z 11248926720 471 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:55:00.000000000Z 11292524544 471 +``` +{{% /truncate %}} + +Notice the output table includes the following columns: + +- `_field_mem` +- `_field_proc` +- `_measurement_mem` +- `_measurement_proc` +- `_value_mem` +- `_value_proc` + +These represent the columns with values unique to the two input tables. + +## Calculate and create a new table +With the two streams of data joined into a single table, use the [`map()` function](/v2.0/reference/flux/functions/transformations/map) +to build a new table by mapping the existing `_time` column to a new `_time` column and dividing `_value_mem` by `_value_proc` +and mapping it to a new `_value` column. + +```js +join(tables: {mem:memUsed, proc:procTotal}, on: ["_time", "_stop", "_start", "host"]) + |> map(fn: (r) => ({ + _time: r._time, + _value: r._value_mem / r._value_proc + })) +``` + +{{% truncate %}} +###### Mapped table +``` +Table: keys: [_field_mem, _field_proc, _measurement_mem, _measurement_proc, _start, _stop, host] + _field_mem:string _field_proc:string _measurement_mem:string _measurement_proc:string _start:time _stop:time host:string _time:time _value:int +---------------------- ---------------------- ----------------------- ------------------------ ------------------------------ ------------------------------ ------------------------ ------------------------------ -------------------------- + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:00.000000000Z 23311346 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:10.000000000Z 23434061 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:20.000000000Z 24147407 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:30.000000000Z 23407280 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:40.000000000Z 23423993 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:50:50.000000000Z 23338173 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:00.000000000Z 23518229 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:10.000000000Z 23600515 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:20.000000000Z 24708030 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:30.000000000Z 23685024 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:40.000000000Z 23720267 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:51:50.000000000Z 24060434 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:00.000000000Z 23730197 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:10.000000000Z 23789506 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:20.000000000Z 23792722 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:30.000000000Z 23861704 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:40.000000000Z 23888340 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:52:50.000000000Z 23833145 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:00.000000000Z 23941895 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:10.000000000Z 23887296 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:20.000000000Z 23833290 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:30.000000000Z 23838424 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:40.000000000Z 24056112 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:53:50.000000000Z 24093367 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:00.000000000Z 24136458 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:10.000000000Z 23977922 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:20.000000000Z 23982245 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:30.000000000Z 24005123 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:40.000000000Z 24160695 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:54:50.000000000Z 23883071 + used total mem processes 2018-11-06T05:50:00.000000000Z 2018-11-06T05:55:00.000000000Z Scotts-MacBook-Pro.local 2018-11-06T05:55:00.000000000Z 23975635 +``` +{{% /truncate %}} + +This table represents the average amount of memory in bytes per running process. + + +## Real world example +The following function calculates the batch sizes written to an InfluxDB cluster by joining +fields from `httpd` and `write` measurements in order to compare `pointReq` and `writeReq`. +The results are grouped by cluster ID so you can make comparisons across clusters. + +```js +batchSize = (cluster_id, start=-1m, interval=10s) => { + httpd = from(bucket:"telegraf") + |> range(start:start) + |> filter(fn:(r) => + r._measurement == "influxdb_httpd" and + r._field == "writeReq" and + r.cluster_id == cluster_id + ) + |> aggregateWindow(every: interval, fn: mean) + |> derivative(nonNegative:true,unit:60s) + + write = from(bucket:"telegraf") + |> range(start:start) + |> filter(fn:(r) => + r._measurement == "influxdb_write" and + r._field == "pointReq" and + r.cluster_id == cluster_id + ) + |> aggregateWindow(every: interval, fn: max) + |> derivative(nonNegative:true,unit:60s) + + return join( + tables:{httpd:httpd, write:write}, + on:["_time","_stop","_start","host"] + ) + |> map(fn:(r) => ({ + _time: r._time, + _value: r._value_httpd / r._value_write, + })) + |> group(columns: cluster_id) +} + +batchSize(cluster_id: "enter cluster id here") +``` diff --git a/content/v2.0/query-data/flux/guides/regular-expressions.md b/content/v2.0/query-data/flux/guides/regular-expressions.md new file mode 100644 index 000000000..de0862fd2 --- /dev/null +++ b/content/v2.0/query-data/flux/guides/regular-expressions.md @@ -0,0 +1,85 @@ +--- +title: Use regular expressions in Flux +seotitle: How to use regular expressions in Flux +description: This guide walks through using regular expressions in evaluation logic in Flux functions. +menu: + v2_0: + name: Regular expressions + parent: How-to guides + weight: 7 +--- + +Regular expressions (regexes) are incredibly powerful when matching patterns in large collections of data. +With Flux, regular expressions are primarily used for evaluation logic in operations such as filtering rows, +dropping and keeping columns, state detection, etc. +This guide shows how to use regular expressions in your Flux scripts. + +## Go regular expression syntax +Flux uses Go's [regexp package](https://golang.org/pkg/regexp/) for regular expression search. +The links [below](#helpful-links) provide information about Go's regular expression syntax. + +## Regular expression operators +Flux provides two comparison operators for use with regular expressions. + +#### `=~` +When the expression on the left **MATCHES** the regular expression on the right, this evaluates to `true`. + +#### `!~` +When the expression on the left **DOES NOT MATCH** the regular expression on the right, this evaluates to `true`. + +## Regular expressions in Flux +When using regex matching in your Flux scripts, enclose your regular expressions with `/`. +The following is the basic regex comparison syntax: + +###### Basic regex comparison syntax +```js +expression =~ /regex/ +expression !~ /regex/ +``` +## Examples + +### Use a regex to filter by tag value +The following example filters records by the `cpu` tag. +It only keeps records for which the `cpu` is either `cpu0`, `cpu1`, or `cpu2`. + +```js +from(bucket: "telegraf/autogen") + |> range(start: -15m) + |> filter(fn: (r) => + r._measurement == "cpu" and + r._field == "usage_user" and + r.cpu =~ /cpu[0-2]/ + ) +``` + +### Use a regex to filter by field key +The following example excludes records that do not have `_percent` in a field key. + +```js +from(bucket: "telegraf/autogen") + |> range(start: -15m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field =~ /_percent/ + ) +``` + +### Drop columns matching a regex +The following example drops columns whose names do not being with `_`. + +```js +from(bucket: "telegraf/autogen") + |> range(start: -15m) + |> filter(fn: (r) => r._measurement == "mem") + |> drop(fn: (col) => col !~ /_.*/) +``` + +## Helpful links + +##### Syntax documentation +[regexp Syntax GoDoc](https://godoc.org/regexp/syntax) +[RE2 Syntax Overview](https://github.com/google/re2/wiki/Syntax) + +##### Go regex testers +[Regex Tester - Golang](https://regex-golang.appspot.com/assets/html/index.html) +[Regex101](https://regex101.com/) diff --git a/content/v2.0/query-data/flux/guides/sort-limit.md b/content/v2.0/query-data/flux/guides/sort-limit.md new file mode 100644 index 000000000..c0da4e5ff --- /dev/null +++ b/content/v2.0/query-data/flux/guides/sort-limit.md @@ -0,0 +1,47 @@ +--- +title: Sort and limit data with Flux +seotitle: How to sort and limit data with Flux +description: > + This guide walks through sorting and limiting data with Flux and outlines how + it shapes your data in the process. +menu: + v2_0: + name: Sort and limit data + parent: How-to guides + weight: 6 +--- + +The [`sort()`function](/v2.0/reference/flux/functions/transformations/sort) orders the records within each table. The following example orders system uptime first by region, then host, then value. + +```js +from(bucket:"telegraf/autogen") + |> range(start:-12h) + |> filter(fn: (r) => + r._measurement == "system" and + r._field == "uptime" + ) + |> sort(columns:["region", "host", "_value"]) +``` + +The [`limit()` function](/v2.0/reference/flux/functions/transformations/limit) limit the number of records in output tables to a fixed number (n). The following example shows up to 10 records from the past hour. + +```js +from(bucket:"telegraf/autogen") + |> range(start:-1h) + |> limit(n:10) +``` + +You can use `sort()` and `limit()` together to show the top N records. The example below returns the 10 top system uptime values sorted first by region, then host, then value. + +```js +from(bucket:"telegraf/autogen") + |> range(start:-12h) + |> filter(fn: (r) => + r._measurement == "system" and + r._field == "uptime" + ) + |> sort(columns:["region", "host", "_value"]) + |> limit(n:10) +``` + +You now have created a Flux query that sorts and limits data. Flux also provides the [`top()`](/v2.0/reference/flux/functions/transformations/selectors/top) and [`bottom()`](/v2.0/reference/flux/functions/transformations/selectors/bottom) functions to perform both of these functions at the same time. diff --git a/content/v2.0/query-data/flux/guides/window-aggregate.md b/content/v2.0/query-data/flux/guides/window-aggregate.md new file mode 100644 index 000000000..c0c3fda8e --- /dev/null +++ b/content/v2.0/query-data/flux/guides/window-aggregate.md @@ -0,0 +1,331 @@ +--- +title: Window and aggregate data with Flux +seotitle: How to window and aggregate data with Flux +description: > + This guide walks through windowing and aggregating data with Flux and outlines + how it shapes your data in the process. +menu: + v2_0: + name: Window and aggregate data + parent: How-to guides + weight: 2 +--- + +A common operation performed with time series data is grouping data into windows of time, +or "windowing" data, then aggregating windowed values into a new value. +This guide walks through windowing and aggregating data with Flux and demonstrates +how data is shaped in the process. + +> The following example is an in-depth walk through of the steps required to window and aggregate data. +> The [`aggregateWindow()` function](#summing-up) performs these operations for you, but understanding +> how data is shaped in the process helps to successfully create your desired output. + +## Data set +For the purposes of this guide, define a variable that represents your base data set. +The following example queries the memory usage of the host machine. + +```js +dataSet = from(bucket: "telegraf/autogen") + |> range(start: -5m) + |> filter(fn: (r) => + r._measurement == "mem" and + r._field == "used_percent" + ) + |> drop(columns: ["host"]) +``` + +> This example drops the `host` column from the returned data since the memory data +> is only tracked for a single host and it simplifies the output tables. +> Dropping the `host` column is column is optional and not recommended if monitoring memory +> on multiple hosts. + +`dataSet` can now be used to represent your base data, which will look similar to the following: + +{{% truncate %}} +``` +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:50:00.000000000Z 71.11611366271973 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:50:10.000000000Z 67.39630699157715 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:50:20.000000000Z 64.16666507720947 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:50:30.000000000Z 64.19951915740967 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:50:40.000000000Z 64.2122745513916 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:50:50.000000000Z 64.22209739685059 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:51:00.000000000Z 64.6336555480957 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:51:10.000000000Z 64.16516304016113 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:51:20.000000000Z 64.18349742889404 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:51:30.000000000Z 64.20474052429199 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:51:40.000000000Z 68.65062713623047 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:51:50.000000000Z 67.20139980316162 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:52:00.000000000Z 70.9143877029419 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:52:10.000000000Z 64.14549350738525 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:52:20.000000000Z 64.15379047393799 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:52:30.000000000Z 64.1592264175415 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:52:40.000000000Z 64.18190002441406 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:52:50.000000000Z 64.28837776184082 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:53:00.000000000Z 64.29731845855713 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:53:10.000000000Z 64.36963081359863 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:53:20.000000000Z 64.37397003173828 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:53:30.000000000Z 64.44413661956787 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:53:40.000000000Z 64.42906856536865 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:53:50.000000000Z 64.44573402404785 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:00.000000000Z 64.48912620544434 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:10.000000000Z 64.49522972106934 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:20.000000000Z 64.48652744293213 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:30.000000000Z 64.49949741363525 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:40.000000000Z 64.4949197769165 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:50.000000000Z 64.49787616729736 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:55:00.000000000Z 64.49816226959229 +``` +{{% /truncate %}} + +## Windowing data +Use the [`window()` function](/v2.0/reference/flux/functions/transformations/window) to group your data based on time bounds. +The most common parameter passed with the `window()` is `every` which defines the duration of time between windows. +Other parameters are available, but for this example, window the base data set into one minute windows. + +```js +dataSet + |> window(every: 1m) +``` + +Each window of time is output in its own table containing all records that fall within the window. + +{{% truncate %}} +###### window() output tables +``` +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:50:00.000000000Z 2018-11-03T17:51:00.000000000Z used_percent mem 2018-11-03T17:50:00.000000000Z 71.11611366271973 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:51:00.000000000Z used_percent mem 2018-11-03T17:50:10.000000000Z 67.39630699157715 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:51:00.000000000Z used_percent mem 2018-11-03T17:50:20.000000000Z 64.16666507720947 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:51:00.000000000Z used_percent mem 2018-11-03T17:50:30.000000000Z 64.19951915740967 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:51:00.000000000Z used_percent mem 2018-11-03T17:50:40.000000000Z 64.2122745513916 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:51:00.000000000Z used_percent mem 2018-11-03T17:50:50.000000000Z 64.22209739685059 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:51:00.000000000Z 2018-11-03T17:52:00.000000000Z used_percent mem 2018-11-03T17:51:00.000000000Z 64.6336555480957 +2018-11-03T17:51:00.000000000Z 2018-11-03T17:52:00.000000000Z used_percent mem 2018-11-03T17:51:10.000000000Z 64.16516304016113 +2018-11-03T17:51:00.000000000Z 2018-11-03T17:52:00.000000000Z used_percent mem 2018-11-03T17:51:20.000000000Z 64.18349742889404 +2018-11-03T17:51:00.000000000Z 2018-11-03T17:52:00.000000000Z used_percent mem 2018-11-03T17:51:30.000000000Z 64.20474052429199 +2018-11-03T17:51:00.000000000Z 2018-11-03T17:52:00.000000000Z used_percent mem 2018-11-03T17:51:40.000000000Z 68.65062713623047 +2018-11-03T17:51:00.000000000Z 2018-11-03T17:52:00.000000000Z used_percent mem 2018-11-03T17:51:50.000000000Z 67.20139980316162 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:52:00.000000000Z 2018-11-03T17:53:00.000000000Z used_percent mem 2018-11-03T17:52:00.000000000Z 70.9143877029419 +2018-11-03T17:52:00.000000000Z 2018-11-03T17:53:00.000000000Z used_percent mem 2018-11-03T17:52:10.000000000Z 64.14549350738525 +2018-11-03T17:52:00.000000000Z 2018-11-03T17:53:00.000000000Z used_percent mem 2018-11-03T17:52:20.000000000Z 64.15379047393799 +2018-11-03T17:52:00.000000000Z 2018-11-03T17:53:00.000000000Z used_percent mem 2018-11-03T17:52:30.000000000Z 64.1592264175415 +2018-11-03T17:52:00.000000000Z 2018-11-03T17:53:00.000000000Z used_percent mem 2018-11-03T17:52:40.000000000Z 64.18190002441406 +2018-11-03T17:52:00.000000000Z 2018-11-03T17:53:00.000000000Z used_percent mem 2018-11-03T17:52:50.000000000Z 64.28837776184082 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:53:00.000000000Z 2018-11-03T17:54:00.000000000Z used_percent mem 2018-11-03T17:53:00.000000000Z 64.29731845855713 +2018-11-03T17:53:00.000000000Z 2018-11-03T17:54:00.000000000Z used_percent mem 2018-11-03T17:53:10.000000000Z 64.36963081359863 +2018-11-03T17:53:00.000000000Z 2018-11-03T17:54:00.000000000Z used_percent mem 2018-11-03T17:53:20.000000000Z 64.37397003173828 +2018-11-03T17:53:00.000000000Z 2018-11-03T17:54:00.000000000Z used_percent mem 2018-11-03T17:53:30.000000000Z 64.44413661956787 +2018-11-03T17:53:00.000000000Z 2018-11-03T17:54:00.000000000Z used_percent mem 2018-11-03T17:53:40.000000000Z 64.42906856536865 +2018-11-03T17:53:00.000000000Z 2018-11-03T17:54:00.000000000Z used_percent mem 2018-11-03T17:53:50.000000000Z 64.44573402404785 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:54:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:00.000000000Z 64.48912620544434 +2018-11-03T17:54:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:10.000000000Z 64.49522972106934 +2018-11-03T17:54:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:20.000000000Z 64.48652744293213 +2018-11-03T17:54:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:30.000000000Z 64.49949741363525 +2018-11-03T17:54:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:40.000000000Z 64.4949197769165 +2018-11-03T17:54:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:50.000000000Z 64.49787616729736 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:55:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:55:00.000000000Z 64.49816226959229 +``` +{{% /truncate %}} + +When visualized in [Chronograf](/chronograf/latest/), each window table is displayed in a different color. + +![Windowed data](/img/flux/simple-windowed-data.png) + +## Aggregate data +[Aggregate functions](/v2.0/reference/flux/functions/transformations/aggregates) take the values +of all rows in a table and use them to perform an aggregate operation. +The result is output as a new value in a single-row table. + +Since windowed data is split into separate tables, aggregate operations run against +each table separately and output new tables containing only the aggregated value. + +For this example, use the [`mean()` function](/v2.0/reference/flux/functions/transformations/aggregates/mean) +to output the average of each window: + +```js +dataSet + |> window(every: 1m) + |> mean() +``` + +{{% truncate %}} +###### mean() output tables +``` +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------------- +2018-11-03T17:50:00.000000000Z 2018-11-03T17:51:00.000000000Z used_percent mem 65.88549613952637 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------------- +2018-11-03T17:51:00.000000000Z 2018-11-03T17:52:00.000000000Z used_percent mem 65.50651391347249 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------------- +2018-11-03T17:52:00.000000000Z 2018-11-03T17:53:00.000000000Z used_percent mem 65.30719598134358 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------------- +2018-11-03T17:53:00.000000000Z 2018-11-03T17:54:00.000000000Z used_percent mem 64.39330975214641 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------------- +2018-11-03T17:54:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 64.49386278788249 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ---------------------------- +2018-11-03T17:55:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 64.49816226959229 +``` +{{% /truncate %}} + +Because each data point is contained in its own table, when visualized, +they appear as single, unconnected points. + +![Aggregated windowed data](/img/flux/simple-windowed-aggregate-data.png) + +### Recreate the time column +**Notice the `_time` column is not in the [aggregated output tables](#mean-output-tables).** +Because records in each table are aggregated together, their timestamps no longer +apply and the column is removed from the group key and table. + +Also notice the `_start` and `_stop` columns still exist. +These represent the lower and upper bounds of the time window. + +Many Flux functions rely on the `_time` column. +To further process your data after an aggregate function, you need to add `_time` back in. +Use the [`duplicate()` function](/v2.0/reference/flux/functions/transformations/duplicate) to +duplicate either the `_start` or `_stop` column as a new `_time` column. + +```js +dataSet + |> window(every: 1m) + |> mean() + |> duplicate(column: "_stop", as: "_time") +``` + +{{% truncate %}} +###### duplicate() output tables +``` +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:50:00.000000000Z 2018-11-03T17:51:00.000000000Z used_percent mem 2018-11-03T17:51:00.000000000Z 65.88549613952637 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:51:00.000000000Z 2018-11-03T17:52:00.000000000Z used_percent mem 2018-11-03T17:52:00.000000000Z 65.50651391347249 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:52:00.000000000Z 2018-11-03T17:53:00.000000000Z used_percent mem 2018-11-03T17:53:00.000000000Z 65.30719598134358 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:53:00.000000000Z 2018-11-03T17:54:00.000000000Z used_percent mem 2018-11-03T17:54:00.000000000Z 64.39330975214641 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:54:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:55:00.000000000Z 64.49386278788249 + + +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:55:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:55:00.000000000Z 64.49816226959229 +``` +{{% /truncate %}} + +## "Unwindow" aggregate tables +Keeping aggregate values in separate tables generally isn't the format in which you want your data. +Use the `window()` function to "unwindow" your data into a single infinite (`inf`) window. + +```js +dataSet + |> window(every: 1m) + |> mean() + |> duplicate(column: "_stop", as: "_time") + |> window(every: inf) +``` + +> Windowing requires a `_time` column which is why it's necessary to +> [recreate the `_time` column](#recreate-the-time-column) after an aggregation. + +###### Unwindowed output table +``` +Table: keys: [_start, _stop, _field, _measurement] + _start:time _stop:time _field:string _measurement:string _time:time _value:float +------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ---------------------------- +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:51:00.000000000Z 65.88549613952637 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:52:00.000000000Z 65.50651391347249 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:53:00.000000000Z 65.30719598134358 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:54:00.000000000Z 64.39330975214641 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:55:00.000000000Z 64.49386278788249 +2018-11-03T17:50:00.000000000Z 2018-11-03T17:55:00.000000000Z used_percent mem 2018-11-03T17:55:00.000000000Z 64.49816226959229 +``` + +With the aggregate values in a single table, data points in the visualization are connected. + +![Unwindowed aggregate data](/img/flux/simple-unwindowed-data.png) + +## Summing up +You have now created a Flux query that windows and aggregates data. +The data transformation process outlined in this guide should be used for all aggregation operations. + +Flux also provides the [`aggregateWindow()` function](/v2.0/reference/flux/functions/transformations/aggregates/aggregatewindow) +which performs all these separate functions for you. + +The following Flux query will return the same results: + +###### aggregateWindow function +```js +dataSet + |> aggregateWindow(every: 1m, fn: mean) +``` From 1f3708d13d7d7139b74f9e8c89800637a255a93f Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Tue, 22 Jan 2019 08:24:11 -0700 Subject: [PATCH 08/14] updated flux guides with v2 information --- .../v2.0/query-data/flux/guides/group-data.md | 56 ++++++++---------- .../v2.0/query-data/flux/guides/histograms.md | 10 ++-- content/v2.0/query-data/flux/guides/join.md | 14 +++-- .../flux/guides/regular-expressions.md | 6 +- .../v2.0/query-data/flux/guides/sort-limit.md | 17 ++++-- .../flux/guides/window-aggregate.md | 43 ++++++++------ .../v2.0/reference/flux/language/_index.md | 2 +- .../flux/language/assignment-scope.md | 2 +- .../v2.0/reference/flux/language/blocks.md | 2 +- .../flux/language/built-ins/_index.md | 2 +- .../reference/flux/language/data-model.md | 2 +- .../reference/flux/language/expressions.md | 2 +- .../flux/language/lexical-elements.md | 2 +- .../v2.0/reference/flux/language/notation.md | 2 +- .../v2.0/reference/flux/language/operators.md | 2 +- .../v2.0/reference/flux/language/options.md | 2 +- .../v2.0/reference/flux/language/packages.md | 2 +- .../reference/flux/language/representation.md | 2 +- .../reference/flux/language/side-effects.md | 2 +- .../reference/flux/language/statements.md | 2 +- content/v2.0/reference/flux/language/types.md | 2 +- .../v2.0/reference/flux/language/variables.md | 2 +- static/img/grouping-by-cpu-time.png | Bin 0 -> 13011 bytes static/img/grouping-by-time.png | Bin 0 -> 12111 bytes static/img/grouping-data-set.png | Bin 0 -> 23515 bytes static/img/simple-unwindowed-data.png | Bin 0 -> 14096 bytes static/img/simple-windowed-aggregate-data.png | Bin 0 -> 12511 bytes static/img/simple-windowed-data.png | Bin 0 -> 19874 bytes 28 files changed, 98 insertions(+), 80 deletions(-) create mode 100644 static/img/grouping-by-cpu-time.png create mode 100644 static/img/grouping-by-time.png create mode 100644 static/img/grouping-data-set.png create mode 100644 static/img/simple-unwindowed-data.png create mode 100644 static/img/simple-windowed-aggregate-data.png create mode 100644 static/img/simple-windowed-data.png diff --git a/content/v2.0/query-data/flux/guides/group-data.md b/content/v2.0/query-data/flux/guides/group-data.md index 1b6e6191f..d185d9ea4 100644 --- a/content/v2.0/query-data/flux/guides/group-data.md +++ b/content/v2.0/query-data/flux/guides/group-data.md @@ -11,9 +11,9 @@ menu: weight: 3 --- -With Flux, data can be grouped by any column in your queried data set. -"Grouping" is accomplished by partitioning data into tables where each row shares a common value for specified columns. -This guide walks through grouping data in Flux with examples of how data is shaped in the process. +With Flux, you can group data by any column in your queried data set. +"Grouping" partitions data into tables in which each row shares a common value for specified columns. +This guide walks through grouping data in Flux and provides examples of how data is shaped in the process. ## Group keys Every table has a **group key** – a list of columns which for which every row in the table has the same value. @@ -44,22 +44,13 @@ dataStream The `group()` function has the following parameters: -### by -An explicit method for defining the group key with an array of strings. -Only columns specified are included in the output group key. +### columns +The list of columns to include or exclude (depending on the [mode](#mode)) in the grouping operation. -### except -An implicit method for defining the group key with an array of strings. -All columns **except** those specified are included in the output group key. +### mode +The method used to define the group and resulting group key. +Possible values include `by` and `except`. -### none -A boolean that removes all grouping and outputs everything as a single table. - ---- - -_For more information, see the [`group()` function](/v2.0/reference/flux/functions/transformations/group)._ - ---- ## Example grouping operations To illustrate how grouping works, define a `dataSet` variable that queries System @@ -80,9 +71,11 @@ dataSet = from(bucket: "telegraf/autogen") |> drop(columns: ["host"]) ``` -> This example drops the `host` column from the returned data since the CPU data -> is only tracked for a single host and it simplifies the output tables. -> Don't drop the `host` column if monitoring multiple hosts. +{{% note %}} +This example drops the `host` column from the returned data since the CPU data +is only tracked for a single host and it simplifies the output tables. +Don't drop the `host` column if monitoring multiple hosts. +{{% /note %}} {{% truncate %}} ``` @@ -158,7 +151,7 @@ Table: keys: [_start, _stop, _field, _measurement, cpu] **Note that the group key is output with each table: `Table: keys: `.** -![Group example data set](/img/flux/grouping-data-set.png) +![Group example data set](/img/grouping-data-set.png) ### Group by CPU Group the `dataSet` stream by the `cpu` column. @@ -168,7 +161,8 @@ dataSet |> group(columns: ["cpu"]) ``` -This won't actually change the structure of the data since it already has `cpu` in the group key and is therefore grouped by `cpu`. +This won't actually change the structure of the data since it already has `cpu` +in the group key and is therefore grouped by `cpu`. However, notice that it does change the group key: {{% truncate %}} @@ -246,7 +240,7 @@ Table: keys: [cpu] The visualization remains the same. -![Group by CPU](/img/flux/grouping-data-set.png) +![Group by CPU](/img/grouping-data-set.png) ### Group by time Grouping data by the `_time` column is a good illustration of how grouping changes the structure of your data. @@ -369,20 +363,22 @@ Table: keys: [_time] {{% /truncate %}} Because each timestamp is a structured as a separate table, when visualized, they appear as individual, unconnected points. -Even though there are multiple records per timestamp, it will only visualize the last record of the table. +Even though there are multiple records per timestamp, it will only visualize the last record of group's table. -![Group by time](/img/flux/grouping-by-time.png) +![Group by time](/img/grouping-by-time.png) + +{{% note %}} +With some further processing, you could calculate the average CPU usage across all CPUs per point +of time and group them into a single table, but we won't cover that in this example. +If you're interested in running and visualizing this yourself, here's what the query would look like: -> With some further processing, you could calculate the average CPU usage across all CPUs per point -> of time and group them into a single table, but we won't cover that in this example. -> If you're interested in running and visualizing this yourself, here's what the query would look like: -> ```js dataSet |> group(columns: ["_time"]) |> mean() |> group(columns: ["_value", "_time"], mode: "except") ``` +{{% /note %}} ## Group by CPU and time Group by the `cpu` and `_time` columns. @@ -661,7 +657,7 @@ Table: keys: [_time, cpu] When visualized, tables appear as individual, unconnected points. -![Group by CPU and time](/img/flux/grouping-by-cpu-time.png) +![Group by CPU and time](/img/grouping-by-cpu-time.png) Grouping by `cpu` and `_time` is a good illustration of how grouping works. diff --git a/content/v2.0/query-data/flux/guides/histograms.md b/content/v2.0/query-data/flux/guides/histograms.md index ff6f46543..398c23384 100644 --- a/content/v2.0/query-data/flux/guides/histograms.md +++ b/content/v2.0/query-data/flux/guides/histograms.md @@ -18,7 +18,7 @@ The [`histogram()` function](/v2.0/reference/flux/functions/transformations/hist cumulative distribution of a dataset by counting data frequencies for a list of "bins." A **bin** is simply a range in which a data point falls. All data points that are less than or equal to the bound are counted in the bin. -In the histogram output, a column is added (le) that represents the upper bounds of of each bin. +In the histogram output, a column is added (`le`) that represents the upper bounds of of each bin. Bin counts are cumulative. ```js @@ -31,12 +31,14 @@ from(bucket:"telegraf/autogen") |> histogram(bins: [0.0, 10.0, 20.0, 30.0]) ``` -> Values output by the `histogram` function represent points of data aggregated over time. -> Since values do not represent single points in time, there is no `_time` column in the output table. +{{% note %}} +Values output by the `histogram` function represent points of data aggregated over time. +Since values do not represent single points in time, there is no `_time` column in the output table. +{{% /note %}} ## Bin helper functions Flux provides two helper functions for generating histogram bins. -Each generates and outputs an array of floats designed to be used in the `histogram()` function's `bins` parameter. +Each generates an array of floats designed to be used in the `histogram()` function's `bins` parameter. ### linearBins() The [`linearBins()` function](/v2.0/reference/flux/functions/misc/linearbins) generates a list of linearly separated floats. diff --git a/content/v2.0/query-data/flux/guides/join.md b/content/v2.0/query-data/flux/guides/join.md index 521bf799b..3ed02b59d 100644 --- a/content/v2.0/query-data/flux/guides/join.md +++ b/content/v2.0/query-data/flux/guides/join.md @@ -10,12 +10,12 @@ menu: --- The [`join()` function](/v2.0/reference/flux/functions/transformations/join) merges two or more -input streams whose values are equal on a set of common columns into a single output stream. +input streams, whose values are equal on a set of common columns, into a single output stream. Flux allows you to join on any columns common between two data streams and opens the door for operations such as cross-measurement joins and math across measurements. To illustrate a join operation, use data captured by Telegraf and and stored in -InfluxDB with a default TICK stack installation - memory usage and processes. +InfluxDB - memory usage and processes. In this guide, we'll join two data streams, one representing memory usage and the other representing the total number of running processes, then calculate the average memory usage per running process. @@ -142,7 +142,7 @@ In the example below, `mem` is the alias for `memUsed` and `proc` is the alias f ##### `on` An array of strings defining the columns on which the tables will be joined. -_**Both tables must have all columns defined in this list.**_ +_**Both tables must have all columns specified in this list.**_ ```js join( @@ -203,9 +203,11 @@ Notice the output table includes the following columns: These represent the columns with values unique to the two input tables. ## Calculate and create a new table -With the two streams of data joined into a single table, use the [`map()` function](/v2.0/reference/flux/functions/transformations/map) -to build a new table by mapping the existing `_time` column to a new `_time` column and dividing `_value_mem` by `_value_proc` -and mapping it to a new `_value` column. +With the two streams of data joined into a single table, use the +[`map()` function](/v2.0/reference/flux/functions/transformations/map) +to build a new table by mapping the existing `_time` column to a new `_time` +column and dividing `_value_mem` by `_value_proc` and mapping it to a +new `_value` column. ```js join(tables: {mem:memUsed, proc:procTotal}, on: ["_time", "_stop", "_start", "host"]) diff --git a/content/v2.0/query-data/flux/guides/regular-expressions.md b/content/v2.0/query-data/flux/guides/regular-expressions.md index de0862fd2..ecda474e3 100644 --- a/content/v2.0/query-data/flux/guides/regular-expressions.md +++ b/content/v2.0/query-data/flux/guides/regular-expressions.md @@ -10,8 +10,8 @@ menu: --- Regular expressions (regexes) are incredibly powerful when matching patterns in large collections of data. -With Flux, regular expressions are primarily used for evaluation logic in operations such as filtering rows, -dropping and keeping columns, state detection, etc. +With Flux, regular expressions are primarily used for evaluation logic in predicate functions for things +such as filtering rows, dropping and keeping columns, state detection, etc. This guide shows how to use regular expressions in your Flux scripts. ## Go regular expression syntax @@ -71,7 +71,7 @@ The following example drops columns whose names do not being with `_`. from(bucket: "telegraf/autogen") |> range(start: -15m) |> filter(fn: (r) => r._measurement == "mem") - |> drop(fn: (col) => col !~ /_.*/) + |> drop(fn: (column) => column !~ /_.*/) ``` ## Helpful links diff --git a/content/v2.0/query-data/flux/guides/sort-limit.md b/content/v2.0/query-data/flux/guides/sort-limit.md index c0da4e5ff..7256f54aa 100644 --- a/content/v2.0/query-data/flux/guides/sort-limit.md +++ b/content/v2.0/query-data/flux/guides/sort-limit.md @@ -11,7 +11,9 @@ menu: weight: 6 --- -The [`sort()`function](/v2.0/reference/flux/functions/transformations/sort) orders the records within each table. The following example orders system uptime first by region, then host, then value. +The [`sort()`function](/v2.0/reference/flux/functions/transformations/sort) +orders the records within each table. +The following example orders system uptime first by region, then host, then value. ```js from(bucket:"telegraf/autogen") @@ -23,7 +25,9 @@ from(bucket:"telegraf/autogen") |> sort(columns:["region", "host", "_value"]) ``` -The [`limit()` function](/v2.0/reference/flux/functions/transformations/limit) limit the number of records in output tables to a fixed number (n). The following example shows up to 10 records from the past hour. +The [`limit()` function](/v2.0/reference/flux/functions/transformations/limit) +limits the number of records in output tables to a fixed number, `n`. +The following example shows up to 10 records from the past hour. ```js from(bucket:"telegraf/autogen") @@ -31,7 +35,9 @@ from(bucket:"telegraf/autogen") |> limit(n:10) ``` -You can use `sort()` and `limit()` together to show the top N records. The example below returns the 10 top system uptime values sorted first by region, then host, then value. +You can use `sort()` and `limit()` together to show the top N records. +The example below returns the 10 top system uptime values sorted first by +region, then host, then value. ```js from(bucket:"telegraf/autogen") @@ -44,4 +50,7 @@ from(bucket:"telegraf/autogen") |> limit(n:10) ``` -You now have created a Flux query that sorts and limits data. Flux also provides the [`top()`](/v2.0/reference/flux/functions/transformations/selectors/top) and [`bottom()`](/v2.0/reference/flux/functions/transformations/selectors/bottom) functions to perform both of these functions at the same time. +You now have created a Flux query that sorts and limits data. +Flux also provides the [`top()`](/v2.0/reference/flux/functions/transformations/selectors/top) +and [`bottom()`](/v2.0/reference/flux/functions/transformations/selectors/bottom) +functions to perform both of these functions at the same time. diff --git a/content/v2.0/query-data/flux/guides/window-aggregate.md b/content/v2.0/query-data/flux/guides/window-aggregate.md index c0c3fda8e..82d30feca 100644 --- a/content/v2.0/query-data/flux/guides/window-aggregate.md +++ b/content/v2.0/query-data/flux/guides/window-aggregate.md @@ -16,9 +16,11 @@ or "windowing" data, then aggregating windowed values into a new value. This guide walks through windowing and aggregating data with Flux and demonstrates how data is shaped in the process. -> The following example is an in-depth walk through of the steps required to window and aggregate data. -> The [`aggregateWindow()` function](#summing-up) performs these operations for you, but understanding -> how data is shaped in the process helps to successfully create your desired output. +{{% note %}} +The following example is an in-depth walk-through of the steps required to window and aggregate data. +The [`aggregateWindow()` function](#summing-up) performs these operations for you, but understanding +how data is shaped in the process helps to successfully create your desired output. +{{% /note %}} ## Data set For the purposes of this guide, define a variable that represents your base data set. @@ -34,10 +36,12 @@ dataSet = from(bucket: "telegraf/autogen") |> drop(columns: ["host"]) ``` -> This example drops the `host` column from the returned data since the memory data -> is only tracked for a single host and it simplifies the output tables. -> Dropping the `host` column is column is optional and not recommended if monitoring memory -> on multiple hosts. +{{% note %}} +This example drops the `host` column from the returned data since the memory data +is only tracked for a single host and it simplifies the output tables. +Dropping the `host` column is column is optional and not recommended if monitoring memory +on multiple hosts. +{{% /note %}} `dataSet` can now be used to represent your base data, which will look similar to the following: @@ -81,9 +85,12 @@ Table: keys: [_start, _stop, _field, _measurement] {{% /truncate %}} ## Windowing data -Use the [`window()` function](/v2.0/reference/flux/functions/transformations/window) to group your data based on time bounds. -The most common parameter passed with the `window()` is `every` which defines the duration of time between windows. -Other parameters are available, but for this example, window the base data set into one minute windows. +Use the [`window()` function](/v2.0/reference/flux/functions/transformations/window) +to group your data based on time bounds. +The most common parameter passed with the `window()` is `every` which +defines the duration of time between windows. +Other parameters are available, but for this example, window the base data +set into one minute windows. ```js dataSet @@ -157,9 +164,9 @@ Table: keys: [_start, _stop, _field, _measurement] ``` {{% /truncate %}} -When visualized in [Chronograf](/chronograf/latest/), each window table is displayed in a different color. +When visualized in the InfluxDB UI, each window table is displayed in a different color. -![Windowed data](/img/flux/simple-windowed-data.png) +![Windowed data](/img/simple-windowed-data.png) ## Aggregate data [Aggregate functions](/v2.0/reference/flux/functions/transformations/aggregates) take the values @@ -221,7 +228,7 @@ Table: keys: [_start, _stop, _field, _measurement] Because each data point is contained in its own table, when visualized, they appear as single, unconnected points. -![Aggregated windowed data](/img/flux/simple-windowed-aggregate-data.png) +![Aggregated windowed data](/img/simple-windowed-aggregate-data.png) ### Recreate the time column **Notice the `_time` column is not in the [aggregated output tables](#mean-output-tables).** @@ -232,7 +239,7 @@ Also notice the `_start` and `_stop` columns still exist. These represent the lower and upper bounds of the time window. Many Flux functions rely on the `_time` column. -To further process your data after an aggregate function, you need to add `_time` back in. +To further process your data after an aggregate function, you need to re-add `_time`. Use the [`duplicate()` function](/v2.0/reference/flux/functions/transformations/duplicate) to duplicate either the `_start` or `_stop` column as a new `_time` column. @@ -295,8 +302,10 @@ dataSet |> window(every: inf) ``` -> Windowing requires a `_time` column which is why it's necessary to -> [recreate the `_time` column](#recreate-the-time-column) after an aggregation. +{{% note %}} +Windowing requires a `_time` column which is why it's necessary to +[recreate the `_time` column](#recreate-the-time-column) after an aggregation. +{{% /note %}} ###### Unwindowed output table ``` @@ -313,7 +322,7 @@ Table: keys: [_start, _stop, _field, _measurement] With the aggregate values in a single table, data points in the visualization are connected. -![Unwindowed aggregate data](/img/flux/simple-unwindowed-data.png) +![Unwindowed aggregate data](/img/simple-unwindowed-data.png) ## Summing up You have now created a Flux query that windows and aggregates data. diff --git a/content/v2.0/reference/flux/language/_index.md b/content/v2.0/reference/flux/language/_index.md index 323ab6a21..d00c46420 100644 --- a/content/v2.0/reference/flux/language/_index.md +++ b/content/v2.0/reference/flux/language/_index.md @@ -5,7 +5,7 @@ description: > which is designed for querying, analyzing, and acting on data. menu: v2_0_ref: - name: Flux language specification + name: Flux specification parent: Flux query language weight: 5 --- diff --git a/content/v2.0/reference/flux/language/assignment-scope.md b/content/v2.0/reference/flux/language/assignment-scope.md index 1a572a13a..e528aa66f 100644 --- a/content/v2.0/reference/flux/language/assignment-scope.md +++ b/content/v2.0/reference/flux/language/assignment-scope.md @@ -3,7 +3,7 @@ title: Assignment and scope description: An assignment binds an identifier to a variable, option, or function. Every identifier in a program must be assigned. menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Assignment and scope weight: 20 --- diff --git a/content/v2.0/reference/flux/language/blocks.md b/content/v2.0/reference/flux/language/blocks.md index 54cd7a459..299422884 100644 --- a/content/v2.0/reference/flux/language/blocks.md +++ b/content/v2.0/reference/flux/language/blocks.md @@ -3,7 +3,7 @@ title: Blocks description: A block is a possibly empty sequence of statements within matching braces ({}). menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Blocks weight: 30 --- diff --git a/content/v2.0/reference/flux/language/built-ins/_index.md b/content/v2.0/reference/flux/language/built-ins/_index.md index 28f44d1b0..9438f2842 100644 --- a/content/v2.0/reference/flux/language/built-ins/_index.md +++ b/content/v2.0/reference/flux/language/built-ins/_index.md @@ -6,7 +6,7 @@ description: > menu: v2_0_ref: name: Built-ins - parent: Flux language specification + parent: Flux specification weight: 80 --- diff --git a/content/v2.0/reference/flux/language/data-model.md b/content/v2.0/reference/flux/language/data-model.md index b9e1067fb..ff3e5ec6a 100644 --- a/content/v2.0/reference/flux/language/data-model.md +++ b/content/v2.0/reference/flux/language/data-model.md @@ -4,7 +4,7 @@ description: Flux employs a basic data model built from basic data types. The da menu: v2_0_ref: name: Data model - parent: Flux language specification + parent: Flux specification weight: 1 --- diff --git a/content/v2.0/reference/flux/language/expressions.md b/content/v2.0/reference/flux/language/expressions.md index 2ad8a6e1a..e4396bcf0 100644 --- a/content/v2.0/reference/flux/language/expressions.md +++ b/content/v2.0/reference/flux/language/expressions.md @@ -3,7 +3,7 @@ title: Expressions description: An expression specifies the computation of a value by applying the operators and functions to operands. menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Expressions weight: 40 --- diff --git a/content/v2.0/reference/flux/language/lexical-elements.md b/content/v2.0/reference/flux/language/lexical-elements.md index f7d8db05f..841b54a6a 100644 --- a/content/v2.0/reference/flux/language/lexical-elements.md +++ b/content/v2.0/reference/flux/language/lexical-elements.md @@ -3,7 +3,7 @@ title: Lexical elements description: Descriptions of Flux comments, tokens, identifiers, keywords, and other lexical elements. menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Lexical elements weight: 50 --- diff --git a/content/v2.0/reference/flux/language/notation.md b/content/v2.0/reference/flux/language/notation.md index 390eaaa19..3b53e81ce 100644 --- a/content/v2.0/reference/flux/language/notation.md +++ b/content/v2.0/reference/flux/language/notation.md @@ -3,7 +3,7 @@ title: Notation description: Notation principles for the Flux functional data scripting language. menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Notation weight: 60 --- diff --git a/content/v2.0/reference/flux/language/operators.md b/content/v2.0/reference/flux/language/operators.md index 100afac17..8d519c5af 100644 --- a/content/v2.0/reference/flux/language/operators.md +++ b/content/v2.0/reference/flux/language/operators.md @@ -4,7 +4,7 @@ description: Flux supports many types of operators including arithmetic operator menu: v2_0_ref: name: Operators - parent: Flux language specification + parent: Flux specification weight: 130 --- diff --git a/content/v2.0/reference/flux/language/options.md b/content/v2.0/reference/flux/language/options.md index 3908357c8..dd2085aba 100644 --- a/content/v2.0/reference/flux/language/options.md +++ b/content/v2.0/reference/flux/language/options.md @@ -3,7 +3,7 @@ title: Options description: placeholder menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Options weight: 110 --- diff --git a/content/v2.0/reference/flux/language/packages.md b/content/v2.0/reference/flux/language/packages.md index 6dabf4004..b1889f35f 100644 --- a/content/v2.0/reference/flux/language/packages.md +++ b/content/v2.0/reference/flux/language/packages.md @@ -8,7 +8,7 @@ aliases: - /v2.0/reference/flux/language/programs menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Packages weight: 70 --- diff --git a/content/v2.0/reference/flux/language/representation.md b/content/v2.0/reference/flux/language/representation.md index 91950f1f3..9b6a6ddbb 100644 --- a/content/v2.0/reference/flux/language/representation.md +++ b/content/v2.0/reference/flux/language/representation.md @@ -3,7 +3,7 @@ title: Representation description: Source code is encoded in UTF-8. The text need not be canonicalized. menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Representation weight: 80 --- diff --git a/content/v2.0/reference/flux/language/side-effects.md b/content/v2.0/reference/flux/language/side-effects.md index 948e0dcd6..14054928c 100644 --- a/content/v2.0/reference/flux/language/side-effects.md +++ b/content/v2.0/reference/flux/language/side-effects.md @@ -3,7 +3,7 @@ title: Side effects description: A summary of side effects in the Flux functional data scripting language. menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Side effects weight: 90 --- diff --git a/content/v2.0/reference/flux/language/statements.md b/content/v2.0/reference/flux/language/statements.md index 7b0bde503..c0e8cd2d8 100644 --- a/content/v2.0/reference/flux/language/statements.md +++ b/content/v2.0/reference/flux/language/statements.md @@ -3,7 +3,7 @@ title: Statements description: Statements control execution in the Flux functional data scripting language. menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Statements weight: 100 --- diff --git a/content/v2.0/reference/flux/language/types.md b/content/v2.0/reference/flux/language/types.md index 1b605b501..8e4a346d0 100644 --- a/content/v2.0/reference/flux/language/types.md +++ b/content/v2.0/reference/flux/language/types.md @@ -3,7 +3,7 @@ title: Types description: A type defines the set of values and operations on those values. Types are never explicitly declared as part of the syntax. Types are always inferred from the usage of the value. menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Types weight: 110 --- diff --git a/content/v2.0/reference/flux/language/variables.md b/content/v2.0/reference/flux/language/variables.md index ab9d16314..9ea0e5ebe 100644 --- a/content/v2.0/reference/flux/language/variables.md +++ b/content/v2.0/reference/flux/language/variables.md @@ -3,7 +3,7 @@ title: Variables description: Flux variables hold values. A variable can only hold values defined by its type. menu: v2_0_ref: - parent: Flux language specification + parent: Flux specification name: Variables weight: 120 --- diff --git a/static/img/grouping-by-cpu-time.png b/static/img/grouping-by-cpu-time.png new file mode 100644 index 0000000000000000000000000000000000000000..05d896df7a138db409940d04c11b17b78d4c8120 GIT binary patch literal 13011 zcmbVzc|4Te`~NUmieXyp;@AvclNy{js$leFRoUA4*_pIbMLc6M{ge+k^|z9J<-+yGQit7IT$(E*!z0E zb!kH9!Y=2X7nbGTxowrEytHoZhbn z_zvI3h(q)y-fmjrj6s*gi%yiSH~#q z!|dB92U^H^`4YTsF3S*U z1Pq3`Y~z7ZlvjYKw9(Me@c(X#$Ny>VfNx6EpE5MmFd%q&+Ysy= z40N@`K|k^?F7_H~N-D~V>I%xb>S_v#ihBFi^>lUgbW~M!_US1fQdZR4cvhEacMwIT17BOmv&%QNP<>M9M=Hf+WE|eqg6rR{m=8S$Xf4UP9F6a7m zpOTjA$=o#`+3V4F_RUdzE+W5ZYZqTXRw2}U^?p5XG z-RaHi6_=1HC$G+PcfsC~MDPrhyZuX3S|5A$o53*?k<*?gBF9#Zb#(PkYwy-LVQ9gY z)URur7#p9l70u6^J@VNBV`E1g)6w_w4L_)8@`=^e!KE{U{n|yl?ln!L<-L~f1ixFV z+UCmp4<0lKwCqqj;R^NX?6HA8KhQs8`9#syRA)?=}nBeZ(&`cWsc9N8l92RYTtCx)Z9)r?5m)4M2)I}+#Z#m zkd2F*ulJ>^`7$S^RgYW%Y@;k#s~+xu3<=)&24}2cQAt4C?0Z33lOi&Q~6Bh#igv5n~B!q>*eX*Uwk~?=w zitOCQ&ku=+>_T(%iHb^zib$>5YCxj9lTx1;2TqA6&8yw|ImhH?i982*@v$u?vU6c?y84x=ZMMhD5dN?GkfJ#8;8Ek!(C_EpBLAEc{H5rcd{k6*>|ChW2K`hZN*yv@u4AL>20E+BsR^S3-R6lHAa~K@(g&p4o_wt>%cEFM986oRu!R=ga9T) zxc+XvXx77ghzt9%%4oz(V^#8yn1&T1MH9G;Vk)z|JXN(w%YVROS5RXF`!?Hm@L3 zlUy2)D4XW@m&onzP_Mo-`juSC)bX)wwuY^cO0WK7`m^|D(nln>Go%m+UcaRNl?k

#4tCI2Vv zHj7vla-AV#uHi_@5f`6!K`)hF1@Kj|ora?IS9CICH z%Urh-xw-agAk~j)F4x%98MHn7hG_crRrM#>;9-bo#QUKyp^;b5Wwjs8Ug$e{WqaQu zakuJOdbej^C%3PD=gv~sEg|lHm9i!~H{!UNOswZ#w>utr*@_p+m$?vQz)|Npp~IZ{dgDD$yr)ewlXM!gx#=$<)@j zy>@Weh{&EocOLu<4Y~WSx6N$z6XTNsIf^LzGc7zZGa{ViJanGG!gFJWPl`#OMvg_f zEnNt4=jiWV$d(8j2Bso$5I78?S=$~b&T_F(GSH?Cryinf<>4W)l+?RJZ*_iRq~2n% zs=#+6z`}Q5p^C?yyb-QOu3}|JKy2rpfe_h(hE%}f+1)bEUtzYlU^i!vLDdci)kn~qTkJB-M5O6*5<8! z=Mbf#^KBahT+xGi0yPw+c=U|@&KNIM7?&ggHQ$D~-Q5zV25^ops*MUOO z+|Hu=k|1PCz7`jtBx89{ZqNhii$0QSQ9blY2~xsT*S}sPK+)>De2iD`t#}LT0J;bM?b$rSKr|y* z;*tI@FnJgeVp-$2ZszjUo=iZl?(=mQby$B4*!bav`O zIV(NjJ}|=8R}~%cjdiQx)TId*K1+`;ri5pwvOOb%?6!gaTO|p6?{3L{E+sGM` zCpi;Jlx&0ECB2tZM#Dan8`Czqr8`3rC3$f>7`)*C?aI?D!tdm0(qoxPmj*l5;~Vs2 zX06~)W63G2uO>j(pMJnv4tit3v`<;%0wnsQt)u_8c4=sU zTs#lJ17P~Y$k0PW3AmvytZ;@9jEgi8NQJAl=1%$gEImRbZ!>@j^iD^)03rPWDTe)D zGsT^_;iA?n{0FD%AG5|${>MtLqbGk(XlAee!iyAQW!Z)mJwm!u|8s@@x%5B9mdyUW zLe}m7T>O)}oX*enb6b<7W?u+J>UhHhVMuyweMDZY+VThbcaB;X-EpYJKFj^G*n=km zB=5pl2A1&o~Efxd`z8bJ8S;>qbAazXH#U9aH-smBCZ3I1rm3NBuM~BE2V@|4|@jjLZhRB`M8pO)?)G7tv`VUd5KplP-!@N z6W!qjh^>_3NuBKY(VahJfmELi5PE8ksuL>*-RItZJ^DUbY36&tjF+kDA;+!@AmjjU zq||J?3PV4ix(DvIuh*?)uXmNHGtZgwYikv&(P?GUH?SyKz5&ci=@dT(nmWRa|dMIZme|Oa!Cg<)eI8>(m zB{`G;wF5W}MXnTI$lCT*=cj1H&!jSSDas(jDWe48nfaeBS*d>7R*O!EKi;mhAhC{? zoMd|=<;qa+I_AP0%kw*G%NWn7kTsHoYVVMI9oU5!Y_Dp9bOAn5j?U{U%vO?}AayGn_WQ)@7BlVY||5=Zp~m3h$k=*~Q&jL&A+G^d-J6R-qaCH8?DX}b6FX2+#pjXI(_-Zes>)D?AHvUf`{VvQn3DbmQ*K7{ z%ng{<)d1f054lDu3hmy`z=_V4l?kzk6`2y6+@r^#l7+Tk@IG%C@>SCBT^QM zTjo964s;9T&~NE_rtFSd8%=Pq#kXbQLmnx@tGlA7pM`JYS z9+Z?(Hlt7IAq}K}wlF_=*<>i}vuItqBhy?vh`Y7QAGA`?z!{QjOfxh2M*MY1_P5?O zttka(c~W<=33B#GE$iHES?!>xG`^FnWmSE01q zEEIKl($``aAz0CpS-lpcZdFz8WSt!fUWmADHS{uV# zn@Fq3G4p4o9BtV@_<&AuPPZGc`zw>rSO$3y^}u&HP?;y2o0*5)VcXX)2uAAs6gzg* z?7O2H2=ufbY+J-PpT3JNR-ba@gec2}E0@z@EF9=U4dua%Eg_u3LV32{(Rq1oT38c( z1cU!vX%K)tJ`dMddY!OohDc8a8zz|}4Z7EvT@q_l;^NciJ8!iiyuv~`@`WI=z zxBE*8T_rzG`J|nqIOvq)TR@~f4BfA*7MZkZC^7$hw2#`{ z^T8L`7Zvrkl?3?IzeCs3wL43R9v#tR`%*CBvF4+PvoJ!~Eqm?l)a_mqwA-VmGz?p;>FmM-k)hyn#wa@!a5XAT%@ zzw}P+K6vIO=E*%&Ceeo7t#MdIOcq0D%`e?k*Roc|^V3K+MapCG|;3$d^? zIhllr4>&s0Jujmsi7Fnt(SaoxI_M1s|7EZ?9cnf^Jq;vdEDroS5&Yt?0a9T1L!K0! zq~*@MLU^VbJw05T^g*}FgsMT>c>Ir!Voy6#QIak^q<2$?EP{lMOEEI>+v8c|%6aVm z{2vSQfu=V;ou>uAInZqLO79l#M0sfU(lB)< z|2*(X7%RH5)2Ha@1hVq=B!P~1unbUxVG7(w(`7XW6Ab!5ZG2!Nt;v-H{SnCeAXEMq z+;IF|DWN11%hnPYaxM3L@(;80E-pfg=IqCm$+4TpC%c2ASO)+RO*pJ>ja|&0p=D~v zq}rc#gV0d-NdeIo=Al7w^SDJwz}rW8uo z( zd*@ddH3?I=$tIW=ZI91Ao6$SikQ*2k$Oo0w(F+T8j#)pew(J6pPUx+hE4L9F;^Ec@ z=QmGp3l*-E!h!<$t?ga+s(w4siu>u{db#j_DIL+pFfQ>^(Mhd%7KV6_@=N}8+Mbsi ztet1lX z1-r3vM9Ghg#6ThVvuu8C8>9JGMmeUNL~VxGwJxy5B=Ni-Psvx)5FpOXF^Sb;B7zV=N6R6nH1%f?Y8PTg`a5ncpQn&Ra+)f3^yMJf98;)G7MrPhbDeUbIm$h zhd?RQT&h{`T7;>I(e;*oG5DCrTn@8s8#jm80uu>Vt|KeKWorl0N@@;ygP^UK0!cJw&n?aVFbS{frqQaUiu|BoKR7c$PWuUa z=KxmlCC+-MZw|fj`%cigT3lO>%%ox@tb9M9Bm+TxSotqLhs9a(tKR^k0L^B!JN<3i ztEHRtXMkn06RV>z(@OLw3^t0wCm`&t|L>ykhRqmAOAkE2`~M>(G6nxF=Uo3O4)1S} zU;w?|2}(@YH&HkS-)vOA9x#IhnIU{^uh7>OEY>DQovH@0u8{dfPX2L*7gb7+;Nce8r)2P@DUL(e)JBNwmnOTy>_ zdnhdQIaK~51%#D7sO=}ohV?` za2WkML2v3$Z4Baw3R%~5$Gy;>_zK&RpKr<;;p_@seCFmz>8w6X2Pu4!3Y_`R%SHD; z^S8N4Hemo6C|G3c{IuRbeeI1&J}6q4h&`Q>pRX63lVXU{dQS47kwxow)s;-i5nSiUGs#hHI3;tqbG@nxDiIfDC{34_M>W9E0eCQj#7@lbK?|1E zT%fj%Axo)ORLMUD^tTF963`kQW<*{DRz%qo*;TCN-e(Gn&JT#}F07zmq=>}}i7>hB z@+bLzbjf)$s{mNF#=~+vVU(}|0qSqb(;=FK>!e0*Pd{ZNtqj#X}%##3|Baa{73&Pa3H) zr$1Nr7pIk3=Zm=~t_-Cun$mNK5GaIX1I%46Awhs=r9$nLVbD(m}C-30T4>~IJ z>LH$#7ySHi8j&+_>i*ysvLH(G8gMLU-jaxx^kByWNm^d3*;C(ra#Q1m-$<+X_MeHk zwHpiX*OgN4m-RGjk0eFJB=;@Zj%s^%>Tth!wKjS^-~VT8$K50!rdBM3{n9o?jk#V~ z^{YNDxKk%$=cS+n?)N#g$rC-lN9{qC!y9A=Mbz9k#+_g0Z0!Q-*t=XK3>7;>HJVxB z&58KLh86${;AIBg2UCGrb1}5r7Rbd+l8r7gArl zW`r9JTp8pzA3GD+e1mE3!_$D7AMauU#*T}=UF6x4zyDiy7FT2YlpC%nv~r=o#Ybfu z)P3vi3Jb^FL0p=eQ*)}s$Z$Yb(a8HIb?wH)l_NuOpo4pfN4Xkjxnk~EBBjU0mZGdz zY|SK?YVli>&))ylvwGwjXfw=1s(hWY>a{Q88^*9_%Qnd0@)iJYIgy8*+O{Efi#4Hl-_+Iia)9PjT@$Be#BL96KewDB6hy3rD?!HZz zda1pa&L1enBT&9uCXiWaem2cMi`}x?X(=;-BIMyWOpCpv{xO0-)Qd*kpqWL>YM;xC zt+7o%s#<)4)#j!@*HEmMnqrTG>yqC_LwII;>w=|0uaN-F(HfT7kJ`~g0qk?3Nl?rC zc`Jcojpru6axkG{EcsLXpTNjXi5M-3nYE^t$@$6_mV35}CX6Z3gN%q9bq3{2Sr@`OMc?77^~$@%F@pQidm>mbkGwZx9;n&1PKu?1UC*#xRK#k6c82iA^T z6q%O1s{Xa(a}wR&!M{7yMdhu7E=Z25ZJSN>utrgEe&H>i#LK2n>(s=tK({ScPBip)bDKdN3A6(Qa(Mu*khWuXvDUhC!MW7gz8 zc0{V7p6i7Ke2TB^7<_ZQ)jjQYyD_JYdfJEC3W-F<-Dr zD4e6WmIECIr)sd&Ba6@Cw1d9C67}EG#K_oJY~RI#6#86}*Qz9mE^chQ!Fhp)g8D*p zS7fp0UtR@?)d%cpQUBG8dG@&+uT7oUuUjyh;FUg#FO<^-GMz6Y$7VM7&yup$!VDSQszRP^=c!derYCG!FWfh@6r-D?(=`s8%QMOc$%6V1 z`t(cp07Y_EI;;TJVqhB?=JM#UXaXd3ggNuUUT~m8{X-_O3If}T)I~BOPT-L)?jf=q z{f!IRn6TzJ1XGCP7P57eXpU(RQ=wE?&+J(rcCV@}O*WFCMDaMk_L#liYa#tNw+bFS zn6rHm548_08+s;Anze3+Cxyx4E{T>R)NU zXVeJY!xhCboqAK%Ues5ifz&JT&f?*fYeo+VN^A&0aFPX#^4=w6nLOBwf=`k#s<2E5 zX9y9ZDFn}%9kC|}X;2;ez5HXeJmMu|FS%B+PP>__I%P%DJ$PnWg!PI z<0Rkp(eT@jN$LWQN92Kf+w!HZdriG}Y4~O)qd|04E6TnjVT}5Epx;`aOYEsomyj9f zN@jhT5ZjvGn#!zMb+XDg?TN-_1yb_(G^foHW#$rUrL%G8>!PxKtFA-aIu!$7{wnJ- z$zO|z4?cgHH~V&92?txy)`TL}u|WUndrbws;4DVt@UN~@Mw}8?JD<@6u9k|gj^9bP zvDeP!V9w;-8RIZ_1U2we!4K`D*M9eSLPWvu+0fu;QkoLjOKnri}DmEfy9`F|zCGz?rN3CAAqO-%sDIlX`okv94_@ zUrCqIiPxMsZ3Aw0x0_AJv@br2-KVw<5-N$9nvhAXb{m+fXA6u;JYFT{K5(&c`BLC7 z>xA&@)6^F>Ka|$omW%sAsg;Nn4x5qh!OnfW5H?6I=<)foK$AdZ^hFQ!@@V)& z88%kW_Qz~uvxkm2(b$R%pX-Nw%&0N;y+L$F`|FH`GhLtE^0`ZU{26IDmNb9s`=c@> z(>||{S4ZpP*TBx-sAJKj@3M4fiD$=o)A27Bw)nZ_;seyAo(Z3w@2EXg>wcxKzzXrOvS$;yR!6!r=kZVd zQta}n)!BW(Mbfk)Cw#(A#j~+JtMvT-@N(CgA=<_G2%=f|wF4}a1=eSngL~vrT(-Vu zW-iYA2kW2u7revEaI@Hl!iwNQ7*#ARdiTq3(*4)BAvJg zmQ)h!wLJ{8yF0dvrm&M|Q3Fd;{WnOTpy_i(<2PQ!=ZQ9TH#|W|=lTZpbT_ukvwDux zrl6e%(xip9ca>a~4Gec*R^>KQpLNr7g(`T%KeV@R*Os609m)KakGe18zt`9IRLe-I znqTv;DPcRb&zqy}T3W3*reIpt?yo$G?dzSQsy9H* zJ7ZtiI@g)zHum!M+(mT^oXKO`o$C)8`WNlRXjv(J-M`vZmU@!ZN5h=2-0u}&cxgP$ zV2V2Tq&}+1I>7bY`x7+$10j7+uGVEU(@322SmX3UlU00lf!3;>ix=%H9yeT)|1Qm< z_bdE5!ehz}N40!SnJtt2rFVXDY4-0csSJAgYi{8&!J)GB;!m@apNjSs9$e~4(&{S& zX#Thb_Z2L2QPe-5I`%YkZhi z>ZWR`s&sIHf2qr*8QY)|iR3((Sm{Qfz4LfFe<072_GnmwyEnWU%R6OWQQcjob?t}CzuQtt3J-V!J`RuV1R#xf-v8W{c>AUlyJ!P%c?-Rl9!?;7` z12)gEC=08*R|79CblEW(twCufzB;BZ-$p-Jwa-6A2<(p&UKtamk}y-m7$4N73yf;Kpr_UK#o&1m=WJ;?Wg;2~KN;%iTL?nJ7PFUmL*v(d2qokItSQ zD|9daoG0X~I0jC9S#;j6Y6$w&ws0ZuM+7pl?ABx!3(ehQhdRr0@pI`Ly#Wu?6%XpR#MD;nxbxmsO8~ojdFKYYSzrtt<$B_zd5fZg5oc17K@8knaaYav}Ekg@B_| zH3DEC9d5%##=x5c8zcur3F4(WeE4$J8|?q-(NAVpCxhcPn-KTFSu^^XDkcQuK-H-% zf;TwB2_Fr#K$hkCG5VW;(?0}43NG0p5#TTo*i*yQ?Z`e4+R~3qt{>^7lz{w}4Kb+# z`$Hc%%2oA^8NnIbcpU>1vf_gGdsb3U`=<$mT5zabGr95l9VaOGI6vY-i0DUW=oHxP zKmTSsD}w*)0yu_qF(S-;kYGTZaf2D!DO-9mCXNBL~5iWYZ1cl^`0?qx}Waj`}2pHdB0!FdA(leJkL4L^NQQM$KX#^9##Yb@u!iY zt~mm+o`66gomkd^Cs*f|YQV=9KRqkI{p7=b0ggVd2yGX#lPhH8>FDNa?&|0gXJu-l>P+^OafI8*1bTXb-Ux)cR-l)ovxlo6XQc?BT59BCMqesRycp0X$v(9HBtZV5zR~Aq5#lX=gckSx8AmMov*yUO`R@+94~agqKyo%PLCC z%BjjKtL~74R{n&+Y(6fBRn2wvSLOo0G=z`%`FW}0@c{t=G66ef$Ubg(ITaNZyzCDA zjvdmVg|u%F$!mg5VJyoyh)v8p1$Q#u7ZeR);0|u51(7 zFnpk+7hXvrIJ$>Z0#M_JrsQS9SljEW3U=-b!DWU(2HN%f5IZ3?4q- zAC8;(2|oH0m^+RTX_-K3}Uaz0lh?w=ehG&_4N0i&Aj61Ox6=q z-@aYmX!pS#J2f_AxNFyk9^=`2_=rE>k?eq=7>7g0obktl!%iO~1xraORv>azRrQ`7 zR3UkVDJW=lqR2~5s@Vogew@cr&K4Y#SJ5!=vvWAAscB@o$HvC)$eGj&7FI4YGRoFA zhebuD<@ku#~+ZA_9oBJJ)Iu)Pga@gBk zz&th~%iSX|>QwrPsAM1ilW~dXITLto)QvrM?3%07@UB>)&UWFetxlSl1l8Gxl524e0-vL8%}euZ((D@a^nOgq?Dv2 zhHzX+`udb|*<>Ke$;Nj*LGzo-De7&kX0ARvZe z15$0pVfh6GwuYTZgapNLI6-f}6PY>J6O!{FNIWg$a>U6quHNqv#dC9zqjv1nvaV_5 z!S+wT`E%ud%aJZ8XqzOaI2oO)q_it3 zHD6Fb{B+`ZJYGdJygwzqKt;vqprw{?_u-0$SuUEOQL%Um3K*QQ#9LWw@1x)hx}e8SyOc-17%s$9XI=? ziK_kqQBvD?D(!X%36IBXJIm_0Vc4+B%K8u_v2EK7PHY;nvQCxZ1Pa zJ3_Kuc0qOpQSrk1m)m#azc?Mme+ehM&pwi=`S5WeZ|881QUCB$p`B0F#mjK=L5mXf*?>3 zs~=4P)P<)tEr=8DPiykG?5C(d;6uF0x`f9p{)XVbrL9`OkVlIsGl)PSuK&qC9(M&D zC5IrSU$TYPvmh=gY9}3~e4Cd<+?O3E)f0sA^$3K*ThsPIAlBan1St&SeoGahAmfAI zZwP&<X zRToo*s?a0CU;dJ=P@YEynN4Z+Tq69wxz8tFpVC;}fZVL>)gmp0VG*wAYe4RUPBuwb zN-rVfE;8EiOkT-MK$Twd9ZDhJ8XF%sr#J8{LrrDPy0kSeD#~Pjy@GkqKY6%qFH3+V zMw^E1!=_o8g}&F|&o{CW4cYohseLxkP^FYG#>Nl_r?$oA$nctLG z%2%zM>nYSLb!%XCnY-C89$OJ-0vYG8*NUcHqq(paYHjr2MxUm<7p$g3q-SRe zHAMvNa-3z_>>xvYsXKY!dYZp!CX2ATOnw}!O+{{Qy!IDc-F)YA7OL$QD%FzW7enD6 z+KNKn#T75zi?W~@MbCUuNQ9~KS$1a3&;+!7Jfu#^&xu%8XErkD^jRW zO?jZGBy{_Qy15MEjS0L-AgQROs6;dOJNZ4$5^{@CZQ+cC%2hg14Uxmgw_^_fPcNItT6f6Z{8Hn_Yo;CH9Ps z3BxrlqzW|WsI$^2f7P{ZXDnTt5~|w8l{UA0@xZm3hY#MmZ`LLKbqlrQ%BhFXg=70p z_6p;Z&BPQ^RTu})eTN`ypCXgoWDEwa9KwD@#wkXlgk#^Fv$cXSx}++-JTxuL=!{De zLmfV$rmk68T0tjSNL4_$v}tfV#_4&M5Wo^o+LFA#fJU^4QUk`2iQtT)m(Rw*uiCE@ z-Y#$BK+~0udj#pdfj(7&4b-ldJS(P=s=*NJ>gJI+GwUB`p;Zy=3ed2Kx3Gp^*cZe0 zF(%t5($9&dPwPk_V5)|&Dl*At;{P!aQ`~4GG;SO$8p0f=iz9yzD~+T1OxB7KY(|TA zaIG1-N(Phh0baRYJTlIqAVU3P%2S|-@Pj&n=H?Mr#=1%fP%JPabzH2mpZ4z+eFdFx zYhvmb!}k&x`U3|@@C-1`wcFe%Rt;~FTrfqxN|!1+0}P5Wa)GHMV<)jJzo*!eQihS< zaWe1;B{)En#$U`trJ37yL@N2G!=cqMx9t*r+unhrJi|E3qm99DIo7N%Xmx!zQ0#R; zhL0(uE9*-HhTHceA_%vk-m=@n*ukBOwjnK zs4rvF<(w$GYjH;SG3qVvA1D}uEYPC!iBTDSDgSI+JwX%ut4((Lf^_$Z3pW_O%z&q0 zif*>k4s26WYr058b>;X?joI;60J>6ay*wxhk5Fa(oDH>`$v68r%-0JXU|?4Tiap5m z1-W2+a<^@QMDoLyw5jfT=^)>7uyE2}o^oi>SDgGAH;sZteF1oRJ1UnF4xjRJG7Fll z65=bSk8S$`#s2Yh4z4M>#qFzr1C%QyWg4C-*x=2u5v<|PyxpyXsHyv-Js;c7MgM(` z-m4cZc9txvUQ4*{m(SnuoY7d%9=byo{oZ}vxXZgJ4W`<>-1&Ou1meUG7nnNcjX?z;V4Ba$mLOiPf%x^ z&}h!VK_oa&fFz1GP+CA-|CWXspCIk4J8c=Tl#CV)j|+c%zFYDPGU$-kc-e}`;{BXy zC4^6Bt8LY#^00Jo5KN(kZJ6p1#G1uJK4?-r;kjCJ_X-dJ)TmSK5?y1+YEpFbX+TmS zpBCvUJ4mLTsVc#7_3uTDQkXCWblWL$jeXUlMaKaMF-|h1ou=iQde@M)qAc_OLcJuk z%QJ&9tF#q7q8paq%_@aei+>mk4tlM@+pQYB9z_@5PEM>_!4`b-k)Mp>M=%>@Ulg&t zU8S&sMD13<36{{67>)iNINge-07|7U4tB=+(!klz8{AG{j{I-%VI zpDz4z)q?BbV4}idO9=3vFktD|ZdozwZ%VFhZO=TIcC=`4 z9B9ovR1n1c3}roMSMA%W5gF&F_c&b%_<*`%j=d=W(4dPc)t93yertR(~)JIx!G zbc^glsvOPR4f}6^VGGj9yFx`SGJagd!JhQd2)SC5=?00=%g5CP-RR#D$?NuegMgNG zFZ2xCwOJK)8p6I>= zR0qv*@-7_Ub*+S!xku53m;uA93^e+HVqes67%8rrJkNANVH7)#e;*dZ#RBw?BNh-N zOquWxqSLiX;LWrl8>wU<{#RQNI`J_yIKc`^`N`t^S6g!$0{c8K{gT|gqVF%zF2+A8 z{2C-W9vA+s^X0MC=CNNJ6@KAm<5BEXT{9cesTEfr27K0QEBq<)){tfKKq*~K3N-q{KPYf%HcCHwvK;i; z*a{kPws)j_gHFvHU!dI8(!4MIsCBs<1VPWAe;(;T2Ju&Lt1UY|MZ2`jd)#h?xA6$B zqIl#(Ab4RJ8A=`jMtItV()cTV;=?s{j#Sv?{tz2Vuh~&-EJL4pr7$ZedXdmYt_u!h zTH?N+A(bAg0!u?v3Ae4NOz7Pw(#0`SVe?biS-y3x^8Oetqu4#y>OPQlUh;PTd8VxR zPonLksdChQ3WfSkL;-?Epjm-=U=Iq6Dx4^d%Au5A~i$?wn+zv&*6@1 zTxWz44rtnGf7_R?ZMutFtMfN*>z(Hz-%>B0>bft*KnExGk+@qUJ`NCi7kBVr9OkgV zb!xiBle}o7$WhA`cYprR*nAaF( z8O))b5)+atu2T@-0!d0fce%|t2yGnj38^Ud5YxZMFBaQyoZ8iGA2I#~(~8e9W4Q=> zc#zm7H)abDpi)N@E;qAX{`BeU4oPb~K8Ck2NxbYKuFz*wx#}i(C<_u~1OGWsIS^R9x zQFCZSGvs0QXx7z%#@m?IuwzSmS&W|yiJf_Bb=Ks}mS)=`zq}71Dh8peEr`-mW|7+j zyRGL^Z~UGA-QVr&v~|&efj>5|0j~q6*mns4{h#>0-#YE=W*Hp4bpK7i+w%$;RWcM6 zvcPth;bEkc_ep$xQGc@HK#c;i_7h>-Oi7>(92?uxP(E|jZHLX|1XiQ6o8nzzipwkd z=|N$5Hyemyg5EFC|AAkRQKe&3l3lJVDF+W)v}vGa_oQtq+0Q=-( zSoSU0M}TDLAe4@*`d;dv;l>>BXa9{x>oTa(piRh{1YsSPs5gp%G zri^9;o18xg0(g*vbl&8%%fxBt2-+zt2`h@-@y?+Pu+B;CO?_DKRBX^nVCyY{6CD{h zka{WOKqD6dVe4n}>3jR(+neG8-!LHm0zHyI4!z$I2EfJ*Dqgk{_37BAioi@!I9mD! zfzBtLsb6miI{pP@(rofZlZqOURin3C@GZZL!RSZ`V)O=l@(r09Nv3faO}dv)l?!BgHb zQ`Ng5!#DnNL-9XAekEIW@;h%=VEZEmGv@;tJ0s%Y)}b8h4(*I#Mh2JY+x)|#)h_O8@DZNTUe#sRxV!u!hsWmF|pM9O^iY5x7a6eeqGKuR>)0ALA)i0U1Kp zg@Ff2m{ubt1D2MNacSfEO9vRal=KuF;Z_rbivfIoiI!EG8{ErMGcqo=&ZYAbF?ehx zu_}-O2?NO7u~8-Ra~C;xS*d)tAhU?Knucr2-Y6to@xm;Brll=Qj}xaO>PPICUvE%p z`y3efaD=$b+84%V0M`}6*;Fr^|Hp|SwBZZ!(Dey7cglC`dg}Y}Le!L6pY!PV12&MBg7lJ^ zXB9#5akWziwkc<@V$UCgljc>m+k-sPky+P67l*fpYP=*)XGn(Th9#sgPolXrZxT-5 zA@pM#wluTF>r_@8e>ce?RGYjMc$~#-fwSxE;(_~umlr1u=9bP!hH`ZETaA&@r>{4}T1L+^Y!I={|SX=wl-$n5*gRpKk|%WJ++i3~bZDHcZ>+?z#B8 zgr$?;_F)b41`Gp_5WAI%rXE_giEEAQp|X2S0+260&@z^a%&Hco?OE!jYN&uMw2xRC z9zVZ{q*1}~cx3rwe&0n)wCGY|RCaN%m*s&h~t{)lnKPlv6iyt(4(E_u&9PI8~=x{6){~vt*$0SMv8v zCH$(ICBS?Vxt=tAuV;V4DzAqbiF*bzYzA;jGa{j0_gaEay)MJGdp@AEIy2SJUNQms z6Pi~2Gh?)xn_j{herI2NHSz<0vzD_;La%UE?M@Vs%@o(c%D;jl|D7H;%Cr~P%)QEH z#;0GARVqJuhBahVjV^WU+z5#tJObkD1qiDB*MxxowEq7Ns+Gjo@@Iac8tg%gL>SIU zYZ zc!qOO>3db%BB7r8hPW8oKpa}|sQ+ro(Z4;GwXp8PxAv$_q@vAt@+zyE&@|ut1>)S& zNoqc}X?%FWV~Jz~Q6=Ga-%bIIrr4qj;YM0%!^N{ZwTME%!WOAEgNrAx5?;lzEggww z^H%$eGxWx{`x2pu)S1WmG%$$vE5V32kvT|re7*SRGvkBDRi)w7M;b_{GsAg_(4Rbx z|MvMVp+0xKc<%aLf`b@5qq3rOvU64L9yBe+%91mXQaiZ(e(~=2$qMS? zT3}qgNR)4?;JK)h(8iimn_}QV0H){cU>Dx&%=BHJ37F$&0xMRlF4zh}foaM((i+py zK|5v6;^?~gtVign)&?LuyezO7GrL2k$83$#kE6lw;L&C~1}7ErF^A%iL-KsWfj z5tB;p7Mu6G%h|Iz>-n%r#5O=6kL$`OFVlrnk8<7H0$DDJs5c zb9c%l>vl!_!-RU)5Pa%%&9>3w?-#G%uJ9nR-2k#Iq(k@zP!#6ng8Pq@2;gE|KV4FP=H(>zU-mJ%&>nbc|7dKHof|Z z+gqvEj3DFe3kHHdzB2y7${?WZ$FA-x&KL2y(1I9haVT z-(W%c+^6nGNazE0s^C6g2KQzff0?m?5GPJ@#)4!!PzBfY5R{cR*&|MDrq|3^5cffG z98~OB7ZRfoCmKMBT8Z6(FQgrXxXL(O#0k)`$(O?UhJBInaxO7ny?bCl>FxNlvD$?e zNo@KjEn>&Oa$!W)GwZ5(HmKKsU07mExM7=n?r?in=1^kFhvfyz5bx zJfQ5`Kf(KYSX65oU-rjqT&^4^kKmQym|c}MlL3N@p$~`ZgB8sT!;hPYTp`}9&dUye zF%n)n^|E)~kbl`X#O$lMBi`d(IB9rO&BnSs;HT}za@9l@aSm*6b2!-0Z%b)z!=ryB z4!c_b9yPOU&8!SNvsn4nPnhADMp+&cR7RUEO z4v!c5ZkuR+^EB>h?yb!4olZS|_3x3YdxR%LbDdfmFCr`D`LK;j(Ipq`@^Wh}!=T`? zgOF*@K7VVrx1YSMF!^XlFYl@av=AS0(RmvYnYCP6ieO!*bFdhf8#G%x zMbV4TGhPZlIQ4kQ{QO7Jq#?i_4Fd^2KNs@bF*BuJ5wG*jXL0%Ba5(}s^#s(A#VZCn z-_rW**_>v329+`_BD<{@lry{zK^reGADZKB798a?+*xg$ZZ_F@vRg2w`ntA>=E-NH z0+Vh-Px8-NK4W>DZ!ev!A+j{_{TRz^dP|tlQ`0iO1`@ovnw$;WF7xBgTJbLX_Hr~# z4+YFv41Dc-ZwuH$iH3|x#gmm%8>M;)R>=>JBe#HH&HxAHAe62JVBH*Pe& zFloBV9@~)B+@%$DnC%3k^!$%ufdHocZ~4q0&-QKF={7J}DhJZR%;`;OpR1ll zTA9_B8lqi>$}#IVY;cPy37D8D^cURdXf%I~_Bt|>Xq1=u*YM)VvCWO2g{1iSOln`N zDKAe%IEAx3zMQ{XV7HxC&eHx<#4$~thVrqg-d)OpAguGq^M{;X28dI

RVp# z=H+i`uHZ7rjvcY&jeo{aJ3U)Rd(sVWvs}Cp8v2eL@toR<)mASP^5~fuu-OrEd_JsQ z!zkeF9^S!VWp|A-H%rYBs_f$EeUP~pFO?DB`Q@~E&bk-no>L7Mj>?BTL)$!qX55P| z&kB%>!*-?(A1?)=yG(mchJ~TP#UW!$F)e%RAk6wys+1VpW9OmzCj$;C;fJeQ;Auy% zH&x^3ru#bTb>Ce4WKhP{X;zuzFm|iDKl}SNlKa$Oo_hj@;!U-P*F#_0D(}_2iyzJ2 zKUaWtCId5{*S=HyYUi{e?|^T1c)`Typ(jd5^DJii-q*sRG|nI^0ot_ldlhXupzWJ} z)t5O;>zJym+0>~suOif*@i!T>I;9V`iD~&CpU)b^N0*QvetH+6v@~H+v(CC?{17>) z^FecdKUP|;ZvK{4Gd0>*Fk4H0YQHS^JKx@{P-Q#C2-zrcW!`b$1wWSAypj(sLQgY? ztDa>9?&ti}5@@jgPT5qVw(;SO<2{|Os2%SihqJU>HFnn*13t7`hw4&tZ1>e4`bK8E zyqtb=01>p*Kh_%v=U4q5ODo( z9B_A=5Zv@ME8hfM82Erhz=?N=GzLLH_rLa|*nM}JQP<%{TzIncPTrB=4O6^Pzag%N z-fKn%$$_gQJp}mGJ#gj9r3~dWX}M(`qVW5pcmv>B=HAZwu182@P^s2t@^ktPv~Cbi zxJ1a0#JN!9ou@6sq_GGSkG#20@83(ehfieU>S|xpn1|1cz{5|-ElV5RTFCIe@XMM^hvR?8Q{+n+<_8L+Y0y0xuni`_A=99S>`_C!N$51-dqPd5a7>9 MZ;$TnU58HoKT}HaDgXcg literal 0 HcmV?d00001 diff --git a/static/img/grouping-data-set.png b/static/img/grouping-data-set.png new file mode 100644 index 0000000000000000000000000000000000000000..211701fa64b7b2ed678ebfde00c494396f4a746f GIT binary patch literal 23515 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 Gt^WfUg0olv literal 0 HcmV?d00001 diff --git a/static/img/simple-windowed-aggregate-data.png b/static/img/simple-windowed-aggregate-data.png new file mode 100644 index 0000000000000000000000000000000000000000..830ac6b9203e8f400bb21ac1e5c6fb5beaab8a4e GIT binary patch literal 12511 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 q={Q7$F^IWNzV_%o_ Date: Tue, 22 Jan 2019 08:29:28 -0700 Subject: [PATCH 09/14] moved custom functions guide into query data guides --- .../v2.0/query-data/flux/get-started/syntax-basics.md | 2 +- .../v2.0/query-data/flux/get-started/transform-data.md | 2 +- .../flux/guides}/custom-functions.md | 9 +++++---- .../v2.0/query-data/flux/guides/regular-expressions.md | 2 +- content/v2.0/reference/flux/functions/_index.md | 4 ---- 5 files changed, 8 insertions(+), 11 deletions(-) rename content/v2.0/{reference/flux/functions => query-data/flux/guides}/custom-functions.md (96%) diff --git a/content/v2.0/query-data/flux/get-started/syntax-basics.md b/content/v2.0/query-data/flux/get-started/syntax-basics.md index ad69de95c..3770bf467 100644 --- a/content/v2.0/query-data/flux/get-started/syntax-basics.md +++ b/content/v2.0/query-data/flux/get-started/syntax-basics.md @@ -183,7 +183,7 @@ topN = (tables=<-, n) => tables |> sort(desc: true) |> limit(n: n) {{% /code-tab-content %}} {{< /code-tabs-wrapper >}} -_More information about creating custom functions is available in the [Custom functions](/v2.0/reference/flux/functions/custom-functions) documentation._ +_More information about creating custom functions is available in the [Custom functions](/v2.0/query-data/flux/guides/custom-functions) documentation._ Using the `cpuUsageUser` data stream variable defined above, find the top five data points with the custom `topN` function and yield the results. diff --git a/content/v2.0/query-data/flux/get-started/transform-data.md b/content/v2.0/query-data/flux/get-started/transform-data.md index b075ca8d7..8658cba27 100644 --- a/content/v2.0/query-data/flux/get-started/transform-data.md +++ b/content/v2.0/query-data/flux/get-started/transform-data.md @@ -34,7 +34,7 @@ from(bucket:"example-bucket") ## Flux functions Flux provides a number of functions that perform specific operations, transformations, and tasks. -You can also [create custom functions](/v2.0/reference/flux/functions/custom-functions) in your Flux queries. +You can also [create custom functions](/v2.0/query-data/flux/guides/custom-functions) in your Flux queries. _Functions are covered in detail in the [Flux functions](/v2.0/reference/flux/functions) documentation._ A common type of function used when transforming data queried from InfluxDB is an aggregate function. diff --git a/content/v2.0/reference/flux/functions/custom-functions.md b/content/v2.0/query-data/flux/guides/custom-functions.md similarity index 96% rename from content/v2.0/reference/flux/functions/custom-functions.md rename to content/v2.0/query-data/flux/guides/custom-functions.md index 9365f72d9..11094e717 100644 --- a/content/v2.0/reference/flux/functions/custom-functions.md +++ b/content/v2.0/query-data/flux/guides/custom-functions.md @@ -1,11 +1,12 @@ --- title: Create custom Flux functions +seotitle: Create custom Flux functions description: Create your own custom Flux functions to transform and manipulate data. menu: - v2_0_ref: - name: Custom functions - parent: Flux functions - weight: 6 + v2_0: + name: Create custom functions + parent: How-to guides + weight: 8 --- Flux's functional syntax allows for custom functions. diff --git a/content/v2.0/query-data/flux/guides/regular-expressions.md b/content/v2.0/query-data/flux/guides/regular-expressions.md index ecda474e3..7112b670d 100644 --- a/content/v2.0/query-data/flux/guides/regular-expressions.md +++ b/content/v2.0/query-data/flux/guides/regular-expressions.md @@ -6,7 +6,7 @@ menu: v2_0: name: Regular expressions parent: How-to guides - weight: 7 + weight: 9 --- Regular expressions (regexes) are incredibly powerful when matching patterns in large collections of data. diff --git a/content/v2.0/reference/flux/functions/_index.md b/content/v2.0/reference/flux/functions/_index.md index 6832835ca..a6f31fd37 100644 --- a/content/v2.0/reference/flux/functions/_index.md +++ b/content/v2.0/reference/flux/functions/_index.md @@ -23,7 +23,3 @@ Transformation functions transform or shape your data in specific ways. ## [Miscellaneous functions](/v2.0/reference/flux/functions/misc) Functions that serve miscellaneous purposes when writing Flux scripts. - -## [Custom functions](/v2.0/reference/flux/functions/custom-functions) -Flux's functional syntax allows for custom functions. -This guide walks through the basics of creating your own function. From 6024e26dcc67474e3fd3b14cb0e580317f2ea11a Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Tue, 22 Jan 2019 08:54:31 -0700 Subject: [PATCH 10/14] updated the custom functions and execute queries guides --- assets/styles/layouts/_layout-article.scss | 14 +++++---- .../flux/guides/custom-functions.md | 29 ++++++++++--------- .../query-data/flux/guides/execute-queries.md | 17 ++++++----- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/assets/styles/layouts/_layout-article.scss b/assets/styles/layouts/_layout-article.scss index 4d7923045..95bdd3681 100644 --- a/assets/styles/layouts/_layout-article.scss +++ b/assets/styles/layouts/_layout-article.scss @@ -141,12 +141,14 @@ font-family: 'Inconsolata', monospace; } - p code { - padding: .15rem .45rem .25rem; - border-radius: $border-radius; - color: $article-code; - white-space: nowrap; - font-style: normal; + p,li,table,h2,h3,h4,h5,h6 { + code { + padding: .15rem .45rem .25rem; + border-radius: $border-radius; + color: $article-code; + white-space: nowrap; + font-style: normal; + } } a { diff --git a/content/v2.0/query-data/flux/guides/custom-functions.md b/content/v2.0/query-data/flux/guides/custom-functions.md index 11094e717..5f214f5c8 100644 --- a/content/v2.0/query-data/flux/guides/custom-functions.md +++ b/content/v2.0/query-data/flux/guides/custom-functions.md @@ -20,14 +20,14 @@ The basic structure for defining functions in Flux is as follows: functionName = (functionParameters) => functionOperations ``` -##### `functionName` +##### functionName The name used to call the function in your Flux script. -##### `functionParameters` +##### functionParameters A comma-separated list of parameters passed into the function and used in its operations. [Parameter defaults](#define-parameter-defaults) can be defined for each. -##### `functionOperations` +##### functionOperations Operations and functions that manipulate the input into the desired output. #### Basic function examples @@ -52,14 +52,14 @@ multiply = (x, y) => x * y 30 ``` -## Functions that manipulate pipe-forwarded data -Most Flux functions manipulate data pipe-forwarded into the function. -In order for a custom function to process pipe-forwarded data, one of the function +## Functions that manipulate piped-forward data +Most Flux functions manipulate data piped-forward into the function. +In order for a custom function to process piped-forward data, one of the function parameters must capture the input tables using the `<-` pipe-receive expression. In the example below, the `tables` parameter is assigned to the `<-` expression, -which represents all data pipe-forwarded into the function. -`tables` is then pipe-forwarded into other operations in the function definition. +which represents all data piped-forward into the function. +`tables` is then piped-forward into other operations in the function definition. ```js functionName = (tables=<-) => tables |> functionOperations @@ -70,7 +70,8 @@ functionName = (tables=<-) => tables |> functionOperations ###### Multiply row values by x The example below defines a `multByX` function that multiplies the `_value` column of each row in the input table by the `x` parameter. -It uses the [`map()` function](/v2.0/reference/flux/functions/transformations/map) to modify each `_value`. +It uses the [`map()` function](/v2.0/reference/flux/functions/transformations/map) +to modify each `_value`. ```js // Function definition @@ -89,8 +90,8 @@ from(bucket: "telegraf/autogen") ``` ## Define parameter defaults -To define parameters with default values, use the `=` assignment operator to assign -a default in your function definition: +Use the `=` assignment operator to assign a default value to function parameters +in your function definition: ```js functionName = (param1=defaultValue1, param2=defaultValue2) => functionOperation @@ -103,8 +104,10 @@ Defaults are overridden by explicitly defining the parameter in the function cal ###### Get the winner or the "winner" The example below defines a `getWinner` function that returns the record with the highest or lowest `_value` (winner versus "winner") depending on the `noSarcasm` parameter which defaults to `true`. -It uses the [`sort()` function](/v2.0/reference/flux/functions/transformations/sort) to sort records in either descending or ascending order. -It then uses the [`limit()` function](/v2.0/reference/flux/functions/transformations/limit) to return the first record from the sorted table. +It uses the [`sort()` function](/v2.0/reference/flux/functions/transformations/sort) +to sort records in either descending or ascending order. +It then uses the [`limit()` function](/v2.0/reference/flux/functions/transformations/limit) +to return the first record from the sorted table. ```js // Function definition diff --git a/content/v2.0/query-data/flux/guides/execute-queries.md b/content/v2.0/query-data/flux/guides/execute-queries.md index 1e94bb6f1..d96487687 100644 --- a/content/v2.0/query-data/flux/guides/execute-queries.md +++ b/content/v2.0/query-data/flux/guides/execute-queries.md @@ -48,17 +48,18 @@ data = from(bucket: "example-bucket") |> range(start: -10m) # ... ``` ## InfluxDB API -Flux can be used to query InfluxDB through InfluxDB's `/api/v2/query` endpoint. +Query InfluxDB through the `/api/v2/query` endpoint. Queried data is returned in annotated CSV format. In your request, set the following: +- `Authorization` header to `Token ` + your authentication token. - `accept` header to `application/csv` - `content-type` header to `application/vnd.flux` This allows you to POST the Flux query in plain text and receive the annotated CSV response. -Below is an example `curl` command that queries InfluxDB using Flux: +Below is an example `curl` command that queries InfluxDB: {{< code-tabs-wrapper >}} {{% code-tabs %}} @@ -68,18 +69,20 @@ Below is an example `curl` command that queries InfluxDB using Flux: {{% code-tab-content %}} ```bash -curl localhost:8086/api/v2/query -XPOST -sS \ +curl http://localhost:9999/api/v2/query -XPOST -sS \ +-H 'Authorization: Token YOURAUTHTOKEN' \ -H 'accept:application/csv' \ -H 'content-type:application/vnd.flux' \ --d 'from(bucket:"telegraf") - |> range(start:-5m) - |> filter(fn:(r) => r._measurement == "cpu")' +-d 'from(bucket:“test”) + |> range(start:-1000h) + |> group(columns:[“_measurement”], mode:“by”) + |> sum()' ``` {{% /code-tab-content %}} {{% code-tab-content %}} ```bash -curl localhost:8086/api/v2/query -XPOST -sS -H 'accept:application/csv' -H 'content-type:application/vnd.flux' -d 'from(bucket:"telegraf") |> range(start:-5m) |> filter(fn:(r) => r._measurement == "cpu")' +curl http://localhost:9999/api/v2/query -XPOST -sS -H 'Authorization: Token TOKENSTRINGHERE' -H 'accept:application/csv' -H 'content-type:application/vnd.flux' -d 'from(bucket:“test”) |> range(start:-1000h) |> group(columns:[“_measurement”], mode:“by”) |> sum()' ``` {{% /code-tab-content %}} {{< /code-tabs-wrapper >}} From 2c0f6eeca3ceeddb9c135691c328d1f848859db1 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Tue, 22 Jan 2019 09:46:01 -0700 Subject: [PATCH 11/14] restructured query data section --- content/v2.0/example.md | 1 + content/v2.0/query-data/_index.md | 14 +++- .../{flux/guides => }/execute-queries.md | 20 +++--- content/v2.0/query-data/flux/_index.md | 39 ----------- .../{flux => }/get-started/_index.md | 64 ++++++++----------- .../{flux => }/get-started/query-influxdb.md | 10 +-- .../{flux => }/get-started/syntax-basics.md | 8 +-- .../{flux => }/get-started/transform-data.md | 14 ++-- .../query-data/{flux => }/guides/_index.md | 15 ++--- .../{flux => }/guides/custom-functions.md | 0 .../{flux => }/guides/group-data.md | 0 .../{flux => }/guides/histograms.md | 0 .../v2.0/query-data/{flux => }/guides/join.md | 0 .../{flux => }/guides/regular-expressions.md | 2 +- .../{flux => }/guides/sort-limit.md | 0 .../{flux => }/guides/window-aggregate.md | 0 16 files changed, 75 insertions(+), 112 deletions(-) rename content/v2.0/query-data/{flux/guides => }/execute-queries.md (79%) delete mode 100644 content/v2.0/query-data/flux/_index.md rename content/v2.0/query-data/{flux => }/get-started/_index.md (53%) rename content/v2.0/query-data/{flux => }/get-started/query-influxdb.md (93%) rename content/v2.0/query-data/{flux => }/get-started/syntax-basics.md (95%) rename content/v2.0/query-data/{flux => }/get-started/transform-data.md (91%) rename content/v2.0/query-data/{flux => }/guides/_index.md (57%) rename content/v2.0/query-data/{flux => }/guides/custom-functions.md (100%) rename content/v2.0/query-data/{flux => }/guides/group-data.md (100%) rename content/v2.0/query-data/{flux => }/guides/histograms.md (100%) rename content/v2.0/query-data/{flux => }/guides/join.md (100%) rename content/v2.0/query-data/{flux => }/guides/regular-expressions.md (98%) rename content/v2.0/query-data/{flux => }/guides/sort-limit.md (100%) rename content/v2.0/query-data/{flux => }/guides/window-aggregate.md (100%) diff --git a/content/v2.0/example.md b/content/v2.0/example.md index e1a64c78a..25eca418a 100644 --- a/content/v2.0/example.md +++ b/content/v2.0/example.md @@ -7,6 +7,7 @@ menu: weight: 1 #enterprise_all: true enterprise_some: true +draft: true --- This is a paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc rutrum, metus id scelerisque euismod, erat ante suscipit nibh, ac congue enim risus id est. Etiam tristique nisi et tristique auctor. Morbi eu bibendum erat. Sed ullamcorper, dui id lobortis efficitur, mauris odio pharetra neque, vel tempor odio dolor blandit justo. diff --git a/content/v2.0/query-data/_index.md b/content/v2.0/query-data/_index.md index 6a0c716dd..200c89069 100644 --- a/content/v2.0/query-data/_index.md +++ b/content/v2.0/query-data/_index.md @@ -8,4 +8,16 @@ menu: weight: 2 --- -_Placeholder content for the query data page._ +Flux is InfluxData's functional data scripting language designed for querying, analyzing, and acting on data. + +## Get started with Flux +The best way to familiarize yourself with Flux is to walk through creating a simple Flux query. + +[Get Started with Flux](/v2.0/query-data/get-started) + +## Execute queries +A guide that covers the different options for executing Flux queries with InfluxDB. + +[Different ways to execute Flux queries](/v2.0/query-data/execute-queries) + +## How-to guides diff --git a/content/v2.0/query-data/flux/guides/execute-queries.md b/content/v2.0/query-data/execute-queries.md similarity index 79% rename from content/v2.0/query-data/flux/guides/execute-queries.md rename to content/v2.0/query-data/execute-queries.md index d96487687..9a5af8dcc 100644 --- a/content/v2.0/query-data/flux/guides/execute-queries.md +++ b/content/v2.0/query-data/execute-queries.md @@ -1,15 +1,15 @@ --- -title: Execute Flux queries -seotitle: Different ways to execute Flux queries -description: There are multiple ways to execute Flux queries include the InfluxDB user interface, CLI, and API. +title: Execute queries +seotitle: Different ways to query InfluxDB +description: There are multiple ways to query data from InfluxDB including the the InfluxDB UI, CLI, and API. menu: v2_0: - name: Execute Flux queries - parent: How-to guides - weight: 1 + name: Execute queries + parent: Query data + weight: 2 --- -There are multiple ways to execute Flux queries with InfluxDB and Chronograf v1.7+. +There are multiple ways to execute queries with InfluxDB. This guide covers the different options: 1. [Data Explorer](#data-explorer) @@ -18,7 +18,7 @@ This guide covers the different options: 5. [InfluxDB API](#influxdb-api) ## Data Explorer -Flux queries can be built, executed, and visualized in InfluxDB UI's Data Explorer. +Queries can be built, executed, and visualized in InfluxDB UI's Data Explorer. ![Data Explorer with Flux](/img/flux-data-explorer.png) @@ -31,10 +31,10 @@ influx repl --org org-name ``` ## Influx query command -You can pass Flux queries to the [`influx query` command](/v2.0/reference/cli/influx/query) +You can pass queries to the [`influx query` command](/v2.0/reference/cli/influx/query) as either a file or raw Flux via stdin. -###### Run a Flux query from a file +###### Run a query from a file ```bash influx query @/path/to/query.flux ``` diff --git a/content/v2.0/query-data/flux/_index.md b/content/v2.0/query-data/flux/_index.md deleted file mode 100644 index f4b47863a..000000000 --- a/content/v2.0/query-data/flux/_index.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: Introduction to Flux -description: > - Flux is InfluxData's functional data scripting language designed for querying, - analyzing, and acting on data. -menu: - v2_0: - parent: Query data - name: Flux - weight: 1 ---- - -Flux is InfluxData's functional data scripting language designed for querying, analyzing, and acting on data. - -## Flux design principles -Flux is designed to be usable, readable, flexible, composable, testable, contributable, and shareable. -Its syntax is largely inspired by [2018's most popular scripting language](https://insights.stackoverflow.com/survey/2018#technology), -Javascript, and takes a functional approach to data exploration and processing. - -The following example illustrates querying data stored from the last five minutes, -filtering by the `cpu` measurement and the `cpu=cpu-usage` tag, windowing the data in 1 minute intervals, -and calculating the average of each window: - -```js -from(bucket:"example-bucket") - |> range(start:-1h) - |> filter(fn:(r) => - r._measurement == "cpu" and - r.cpu == "cpu-total" - ) - |> aggregateWindow(every: 1m, fn: mean) -``` - -## Get started with Flux -The best way to familiarize yourself with Flux is to walk through creating a simple Flux query. - -

diff --git a/content/v2.0/query-data/flux/get-started/_index.md b/content/v2.0/query-data/get-started/_index.md similarity index 53% rename from content/v2.0/query-data/flux/get-started/_index.md rename to content/v2.0/query-data/get-started/_index.md index afe2148cf..aaf1c5888 100644 --- a/content/v2.0/query-data/flux/get-started/_index.md +++ b/content/v2.0/query-data/get-started/_index.md @@ -5,18 +5,36 @@ description: > This step-by-step guide through the basics of writing a Flux query. menu: v2_0: - name: Get started - identifier: get-started - parent: Flux - weight: 2 + name: Get started with Flux + parent: Query data + weight: 1 --- -Flux is InfluxData's new functional data scripting language designed for querying, +Flux is InfluxData's functional data scripting language designed for querying, analyzing, and acting on data. This multi-part getting started guide walks through important concepts related to Flux, how to query time series data from InfluxDB using Flux, and introduces Flux syntax and functions. +## Flux design principles +Flux is designed to be usable, readable, flexible, composable, testable, contributable, and shareable. +Its syntax is largely inspired by [2018's most popular scripting language](https://insights.stackoverflow.com/survey/2018#technology), +Javascript, and takes a functional approach to data exploration and processing. + +The following example illustrates querying data stored from the last five minutes, +filtering by the `cpu` measurement and the `cpu=cpu-usage` tag, windowing the data in 1 minute intervals, +and calculating the average of each window: + +```js +from(bucket:"example-bucket") + |> range(start:-1h) + |> filter(fn:(r) => + r._measurement == "cpu" and + r.cpu == "cpu-total" + ) + |> aggregateWindow(every: 1m, fn: mean) +``` + ## Key concepts Flux introduces important new concepts you should understand as you get started. @@ -51,38 +69,10 @@ are unique to each row. ## Tools for working with Flux -You have multiple [options for writing and running Flux queries](/v2.0/reference/flux/guides/execute-queries), -but as you're getting started, we recommend using the following: - -### 1. Data Explorer -The InfluxDB user interface's (UI) Data Explorer makes it easy to build or write -your first Flux script and visualize the results. - -![Flux in the Data Explorer](/img/flux-data-explorer,png) - -The Data Explorer provides multiple ways to create Flux queries. -Toggle between the two with the button to the left of **Submit** in the Data Explorer. - -![Flux Query Builder and Script Editor Toggle](/img/flux-ui-toggle.png) - -#### Query Builder _(default)_ -The Query Builder is a visual tool for building Flux Queries. -Select the organization and bucket from which you would like to query data. -Filter data by any columns available in the data. -Transform you data using using aggregate functions. - -#### Script Editor -The Script Editor is an in-browser code editor where you can write raw Flux scripts. - -### 2. influx CLI -The [`influx repl` command](/v2.0/reference/cli/influx/repl) opens an interactive -read-eval-print-loop (REPL) for querying data within an organization in InfluxDB with Flux. - -```bash -influx repl --org org-name -``` +The [Execute queries](/v2.0/query-data/execute-queries) guide walks through +the different tools available for querying InfluxDB with Flux. diff --git a/content/v2.0/query-data/flux/get-started/query-influxdb.md b/content/v2.0/query-data/get-started/query-influxdb.md similarity index 93% rename from content/v2.0/query-data/flux/get-started/query-influxdb.md rename to content/v2.0/query-data/get-started/query-influxdb.md index 5f027ed17..16ee0c5d7 100644 --- a/content/v2.0/query-data/flux/get-started/query-influxdb.md +++ b/content/v2.0/query-data/get-started/query-influxdb.md @@ -4,7 +4,7 @@ description: Learn the basics of using Flux to query data from InfluxDB. menu: v2_0: name: Query InfluxDB - parent: get-started + parent: Get started with Flux weight: 1 --- @@ -47,7 +47,9 @@ from(bucket:"example-bucket") |> range(start: -1h, stop: -10m) ``` -> Relative ranges are relative to "now." +{{% note %}} +Relative ranges are relative to "now." +{{% /note %}} ###### Example absolute time range ```js @@ -123,6 +125,6 @@ You have now queried data from InfluxDB using Flux. This is a barebones query that can be transformed in other ways. diff --git a/content/v2.0/query-data/flux/get-started/syntax-basics.md b/content/v2.0/query-data/get-started/syntax-basics.md similarity index 95% rename from content/v2.0/query-data/flux/get-started/syntax-basics.md rename to content/v2.0/query-data/get-started/syntax-basics.md index 3770bf467..9bc285a9c 100644 --- a/content/v2.0/query-data/flux/get-started/syntax-basics.md +++ b/content/v2.0/query-data/get-started/syntax-basics.md @@ -4,7 +4,7 @@ description: An introduction to the basic elements of the Flux syntax with real- menu: v2_0: name: Syntax basics - parent: get-started + parent: Get started with Flux weight: 3 --- @@ -106,7 +106,7 @@ data |> someFunction() |> anotherFunction() ## Real-world application of basic syntax This likely seems familiar if you've already been through through the other -[getting started guides](/v2.0/query-data/flux/get-started). +[getting started guides](/v2.0/query-data/get-started). Flux's syntax is inspired by Javascript and other functional scripting languages. As you begin to apply these basic principles in real-world use cases such as creating data stream variables, custom functions, etc., the power of Flux and its ability to query and process data will become apparent. @@ -183,7 +183,7 @@ topN = (tables=<-, n) => tables |> sort(desc: true) |> limit(n: n) {{% /code-tab-content %}} {{< /code-tabs-wrapper >}} -_More information about creating custom functions is available in the [Custom functions](/v2.0/query-data/flux/guides/custom-functions) documentation._ +_More information about creating custom functions is available in the [Custom functions](/v2.0/query-data/guides/custom-functions) documentation._ Using the `cpuUsageUser` data stream variable defined above, find the top five data points with the custom `topN` function and yield the results. @@ -213,5 +213,5 @@ cpuUsageUser |> topN(n:5) |> yield() This query will return the five data points with the highest user CPU usage over the last hour. diff --git a/content/v2.0/query-data/flux/get-started/transform-data.md b/content/v2.0/query-data/get-started/transform-data.md similarity index 91% rename from content/v2.0/query-data/flux/get-started/transform-data.md rename to content/v2.0/query-data/get-started/transform-data.md index 8658cba27..c6868022f 100644 --- a/content/v2.0/query-data/flux/get-started/transform-data.md +++ b/content/v2.0/query-data/get-started/transform-data.md @@ -4,11 +4,11 @@ description: Learn the basics of using Flux to transform data queried from Influ menu: v2_0: name: Transform your data - parent: get-started + parent: Get started with Flux weight: 2 --- -When [querying data from InfluxDB](/v2.0/query-data/flux/get-started/query-influxdb), +When [querying data from InfluxDB](/v2.0/query-data/get-started/query-influxdb), you often need to transform that data in some way. Common examples are aggregating data into averages, downsampling data, etc. @@ -19,7 +19,7 @@ averages the `_value`s in each window, and outputs the averages as a new table. It's important to understand how the "shape" of your data changes through each of these operations. ## Query data -Use the query built in the previous [Query data from InfluxDB](/v2.0/query-data/flux/get-started/query-influxdb) +Use the query built in the previous [Query data from InfluxDB](/v2.0/query-data/get-started/query-influxdb) guide, but update the range to pull data from the last hour: ```js @@ -34,7 +34,7 @@ from(bucket:"example-bucket") ## Flux functions Flux provides a number of functions that perform specific operations, transformations, and tasks. -You can also [create custom functions](/v2.0/query-data/flux/guides/custom-functions) in your Flux queries. +You can also [create custom functions](/v2.0/query-data/guides/custom-functions) in your Flux queries. _Functions are covered in detail in the [Flux functions](/v2.0/reference/flux/functions) documentation._ A common type of function used when transforming data queried from InfluxDB is an aggregate function. @@ -166,11 +166,11 @@ and your own custom functions, but this is a good introduction into the basic sy --- _For a deeper dive into windowing and aggregating data with example data output for each transformation, -view the [Windowing and aggregating data](/v2.0/reference/flux/guides/window-aggregate) guide._ +view the [Window and aggregate data](/v2.0/query-data/guides/window-aggregate) guide._ --- diff --git a/content/v2.0/query-data/flux/guides/_index.md b/content/v2.0/query-data/guides/_index.md similarity index 57% rename from content/v2.0/query-data/flux/guides/_index.md rename to content/v2.0/query-data/guides/_index.md index 77be5ea8d..0fbfee811 100644 --- a/content/v2.0/query-data/flux/guides/_index.md +++ b/content/v2.0/query-data/guides/_index.md @@ -4,25 +4,22 @@ description: Helpful guides that walk through both common and complex tasks and menu: v2_0: name: How-to guides - parent: Flux + parent: Query data weight: 3 --- -## [Different ways to execute Flux queries](/v2.0/query-data/flux/guides/execute-queries) -A guide that covers the different options for executing Flux queries with InfluxDB and Chronograf v1.7+. - -## [How to window and aggregate data with Flux](/v2.0/query-data/flux/guides/window-aggregate) +## [How to window and aggregate data with Flux](/v2.0/query-data/guides/window-aggregate) This guide walks through windowing and aggregating data with Flux and demonstrates how data is shaped in the process. -## [How to group data with Flux](/v2.0/query-data/flux/guides/group-data) +## [How to group data with Flux](/v2.0/query-data/guides/group-data) This guide walks through grouping data in Flux with examples of how data is shaped in the process. -## [How to join data with Flux](/v2.0/query-data/flux/guides/join) +## [How to join data with Flux](/v2.0/query-data/guides/join) This guide walks through joining data with Flux, illustrating how joined data is output and how it can be used. -## [How to sort and limit data with Flux](/v2.0/query-data/flux/guides/sort-limit) +## [How to sort and limit data with Flux](/v2.0/query-data/guides/sort-limit) This guide walks through sorting and limiting data with Flux. -## [Regular expressions in Flux](/v2.0/query-data/flux/guides/regular-expressions) +## [Regular expressions in Flux](/v2.0/query-data/guides/regular-expressions) This guide walks through using regular expressions in evaluation logic in Flux functions. diff --git a/content/v2.0/query-data/flux/guides/custom-functions.md b/content/v2.0/query-data/guides/custom-functions.md similarity index 100% rename from content/v2.0/query-data/flux/guides/custom-functions.md rename to content/v2.0/query-data/guides/custom-functions.md diff --git a/content/v2.0/query-data/flux/guides/group-data.md b/content/v2.0/query-data/guides/group-data.md similarity index 100% rename from content/v2.0/query-data/flux/guides/group-data.md rename to content/v2.0/query-data/guides/group-data.md diff --git a/content/v2.0/query-data/flux/guides/histograms.md b/content/v2.0/query-data/guides/histograms.md similarity index 100% rename from content/v2.0/query-data/flux/guides/histograms.md rename to content/v2.0/query-data/guides/histograms.md diff --git a/content/v2.0/query-data/flux/guides/join.md b/content/v2.0/query-data/guides/join.md similarity index 100% rename from content/v2.0/query-data/flux/guides/join.md rename to content/v2.0/query-data/guides/join.md diff --git a/content/v2.0/query-data/flux/guides/regular-expressions.md b/content/v2.0/query-data/guides/regular-expressions.md similarity index 98% rename from content/v2.0/query-data/flux/guides/regular-expressions.md rename to content/v2.0/query-data/guides/regular-expressions.md index 7112b670d..5918b0f9e 100644 --- a/content/v2.0/query-data/flux/guides/regular-expressions.md +++ b/content/v2.0/query-data/guides/regular-expressions.md @@ -4,7 +4,7 @@ seotitle: How to use regular expressions in Flux description: This guide walks through using regular expressions in evaluation logic in Flux functions. menu: v2_0: - name: Regular expressions + name: Use regular expressions parent: How-to guides weight: 9 --- diff --git a/content/v2.0/query-data/flux/guides/sort-limit.md b/content/v2.0/query-data/guides/sort-limit.md similarity index 100% rename from content/v2.0/query-data/flux/guides/sort-limit.md rename to content/v2.0/query-data/guides/sort-limit.md diff --git a/content/v2.0/query-data/flux/guides/window-aggregate.md b/content/v2.0/query-data/guides/window-aggregate.md similarity index 100% rename from content/v2.0/query-data/flux/guides/window-aggregate.md rename to content/v2.0/query-data/guides/window-aggregate.md From da141ac889c0b653b56247714c958b3c40b3ac0d Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Tue, 22 Jan 2019 10:20:10 -0700 Subject: [PATCH 12/14] minor updates to flux guides --- content/v2.0/query-data/execute-queries.md | 2 ++ .../query-data/get-started/transform-data.md | 2 +- content/v2.0/query-data/guides/_index.md | 20 ++++++++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/content/v2.0/query-data/execute-queries.md b/content/v2.0/query-data/execute-queries.md index 9a5af8dcc..43ec6c883 100644 --- a/content/v2.0/query-data/execute-queries.md +++ b/content/v2.0/query-data/execute-queries.md @@ -30,6 +30,8 @@ read-eval-print-loop (REPL) where you can write and execute Flux queries. influx repl --org org-name ``` +_**Note:** `ctrl-d` will close the REPL._ + ## Influx query command You can pass queries to the [`influx query` command](/v2.0/reference/cli/influx/query) as either a file or raw Flux via stdin. diff --git a/content/v2.0/query-data/get-started/transform-data.md b/content/v2.0/query-data/get-started/transform-data.md index c6868022f..0fe5a4654 100644 --- a/content/v2.0/query-data/get-started/transform-data.md +++ b/content/v2.0/query-data/get-started/transform-data.md @@ -3,7 +3,7 @@ title: Transform data with Flux description: Learn the basics of using Flux to transform data queried from InfluxDB. menu: v2_0: - name: Transform your data + name: Transform data parent: Get started with Flux weight: 2 --- diff --git a/content/v2.0/query-data/guides/_index.md b/content/v2.0/query-data/guides/_index.md index 0fbfee811..cb77221e3 100644 --- a/content/v2.0/query-data/guides/_index.md +++ b/content/v2.0/query-data/guides/_index.md @@ -8,18 +8,28 @@ menu: weight: 3 --- -## [How to window and aggregate data with Flux](/v2.0/query-data/guides/window-aggregate) +## Window and aggregate data This guide walks through windowing and aggregating data with Flux and demonstrates how data is shaped in the process. -## [How to group data with Flux](/v2.0/query-data/guides/group-data) +[Window and aggregate data with Flux](/v2.0/query-data/guides/window-aggregate) + +## Group data This guide walks through grouping data in Flux with examples of how data is shaped in the process. -## [How to join data with Flux](/v2.0/query-data/guides/join) +[Group data with Flux](/v2.0/query-data/guides/group-data) + +## Join data This guide walks through joining data with Flux, illustrating how joined data is output and how it can be used. -## [How to sort and limit data with Flux](/v2.0/query-data/guides/sort-limit) +[Join data with Flux](/v2.0/query-data/guides/join) + +## Sort and limit data This guide walks through sorting and limiting data with Flux. -## [Regular expressions in Flux](/v2.0/query-data/guides/regular-expressions) +[Sort and limit data with Flux](/v2.0/query-data/guides/sort-limit) + +## Use regular expressions in Flux This guide walks through using regular expressions in evaluation logic in Flux functions. + +[Use regular expressions in Flux](/v2.0/query-data/guides/regular-expressions) From c3410ed0a4e0b1af907e6b56c8429bdbf2146bb0 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Tue, 22 Jan 2019 12:04:11 -0700 Subject: [PATCH 13/14] added children shortcode for automatically generating article lists --- CONTRIBUTING.md | 9 +++ assets/js/content-interactions.js | 3 +- assets/styles/layouts/_layout-article.scss | 20 +++++ content/v2.0/query-data/_index.md | 11 +-- content/v2.0/query-data/guides/_index.md | 26 +------ layouts/shortcodes/children.html | 90 ++++++++++++++++++++++ 6 files changed, 124 insertions(+), 35 deletions(-) create mode 100644 layouts/shortcodes/children.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3f3243f0..cf22fe34d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -241,6 +241,15 @@ Truncated markdown content here. {{% /truncate %}} ``` +### Generate a list of children articles +Section landing pages often contain just a list of articles with links and descriptions for each. +This can be cumbersome to maintain as content is added. +To automate the listing of articles in a section, use the `{{< children >}}` shortcode. + +```md +{{< children >}} +``` + ### Reference content The InfluxDB documentation is "task-based," meaning content primarily focuses on what a user is **doing**, not what they are **using**. diff --git a/assets/js/content-interactions.js b/assets/js/content-interactions.js index e6b312ac8..4e02a7125 100644 --- a/assets/js/content-interactions.js +++ b/assets/js/content-interactions.js @@ -14,7 +14,8 @@ $(".article--content h2, \ var elementWhiteList = [ ".tabs p a", ".code-tabs p a", - ".truncate-toggle" + ".truncate-toggle", + ".children-links a" ] $('.article a[href^="#"]:not(' + elementWhiteList + ')').click(function (e) { diff --git a/assets/styles/layouts/_layout-article.scss b/assets/styles/layouts/_layout-article.scss index 95bdd3681..c6c6f21fb 100644 --- a/assets/styles/layouts/_layout-article.scss +++ b/assets/styles/layouts/_layout-article.scss @@ -239,6 +239,26 @@ } } + ///////////////////////// Landing Page Article Links ///////////////////////// + + .children-links { + h2,h3,h4 { + margin-top: -.5rem; + + a a:after { + content: "\e919"; + font-family: "icomoon"; + color: rgba($article-heading, .35); + vertical-align: bottom; + transition: color .2s; + margin-left: .4rem; + } + a:hover { + &:after { color: $article-link; } + } + } + } + ////////////////// Blockquotes, Notes, Warnings, & Messages ////////////////// blockquote, diff --git a/content/v2.0/query-data/_index.md b/content/v2.0/query-data/_index.md index 200c89069..db478d7fe 100644 --- a/content/v2.0/query-data/_index.md +++ b/content/v2.0/query-data/_index.md @@ -10,14 +10,5 @@ menu: Flux is InfluxData's functional data scripting language designed for querying, analyzing, and acting on data. -## Get started with Flux -The best way to familiarize yourself with Flux is to walk through creating a simple Flux query. -[Get Started with Flux](/v2.0/query-data/get-started) - -## Execute queries -A guide that covers the different options for executing Flux queries with InfluxDB. - -[Different ways to execute Flux queries](/v2.0/query-data/execute-queries) - -## How-to guides +{{< children >}} diff --git a/content/v2.0/query-data/guides/_index.md b/content/v2.0/query-data/guides/_index.md index cb77221e3..c72e45394 100644 --- a/content/v2.0/query-data/guides/_index.md +++ b/content/v2.0/query-data/guides/_index.md @@ -8,28 +8,6 @@ menu: weight: 3 --- -## Window and aggregate data -This guide walks through windowing and aggregating data with Flux and demonstrates -how data is shaped in the process. +The following guides walk through common query uses cases. -[Window and aggregate data with Flux](/v2.0/query-data/guides/window-aggregate) - -## Group data -This guide walks through grouping data in Flux with examples of how data is shaped in the process. - -[Group data with Flux](/v2.0/query-data/guides/group-data) - -## Join data -This guide walks through joining data with Flux, illustrating how joined data is output and how it can be used. - -[Join data with Flux](/v2.0/query-data/guides/join) - -## Sort and limit data -This guide walks through sorting and limiting data with Flux. - -[Sort and limit data with Flux](/v2.0/query-data/guides/sort-limit) - -## Use regular expressions in Flux -This guide walks through using regular expressions in evaluation logic in Flux functions. - -[Use regular expressions in Flux](/v2.0/query-data/guides/regular-expressions) +{{% children %}} diff --git a/layouts/shortcodes/children.html b/layouts/shortcodes/children.html new file mode 100644 index 000000000..6c0919e2a --- /dev/null +++ b/layouts/shortcodes/children.html @@ -0,0 +1,90 @@ +{{ $showhidden := .Get "showhidden"}} +{{ $style := .Get "style" | default "h3" }} +{{ $depth := .Get "depth" | default 1 }} +{{ $withDescription := .Get "description" | default true }} +{{ $sortTerm := .Get "sort" | default "Weight" }} + + + + +{{.Inner|safeHTML}} + +{{ define "childs" }} + {{ range .menu }} + {{ if and .Params.hidden (not $.showhidden) }} + {{else}} + + +{{if hasPrefix $.style "h"}} + {{$num := sub ( int (trim $.style "h") ) 1 }} + {{$numn := add $num $.count }} + +{{(printf "" $numn)|safeHTML}} +{{ .Title }} +{{(printf "" $numn)|safeHTML}} + +{{else}} +{{(printf "<%s>" $.style)|safeHTML}} +{{ .Title }} +{{(printf "" $.style)|safeHTML}} +{{end}} + + {{if $.description}} + {{if .Description}} +

{{.Description}}

+ {{else}} +

{{.Summary}}

+ {{end}} + {{end}} + + {{ if lt $.count $.depth}} +{{if eq $.style "li"}} +
+{{end}} + {{ $.Page.Scratch.Set "pages" .Pages }} + {{ if .Sections}} + {{ $.Page.Scratch.Set "pages" (.Pages | union .Sections) }} + {{end}} + {{ $pages := ($.Page.Scratch.Get "pages") }} + + {{if eq $.sortTerm "Weight"}} + {{template "childs" dict "menu" $pages.ByWeight "style" $.style "showhidden" $.showhidden "count" (add $.count 1) "depth" $.depth "pages" $.pages "description" $.description "sortTerm" $.sortTerm}} + {{else if eq $.sortTerm "Name"}} + {{template "childs" dict "menu" $pages.ByTitle "style" $.style "showhidden" $.showhidden "count" (add $.count 1) "depth" $.depth "pages" $.pages "description" $.description "sortTerm" $.sortTerm}} + {{else if eq $.sortTerm "PublishDate"}} + {{template "childs" dict "menu" $pages.ByPublishDate "style" $.style "showhidden" $.showhidden "count" (add $.count 1) "depth" $.depth "pages" $.pages "description" $.description "sortTerm" $.sortTerm}} + {{else if eq $.sortTerm "Date"}} + {{template "childs" dict "menu" $pages.ByDate "style" $.style "showhidden" $.showhidden "count" (add $.count 1) "depth" $.depth "pages" $.pages "description" $.description "sortTerm" $.sortTerm}} + {{else if eq $.sortTerm "Length"}} + {{template "childs" dict "menu" $pages.ByLength "style" $.style "showhidden" $.showhidden "count" (add $.count 1) "depth" $.depth "pages" $.pages "description" $.description "sortTerm" $.sortTerm}} + {{else}} + {{template "childs" dict "menu" $pages "style" $.style "showhidden" $.showhidden "count" (add $.count 1) "depth" $.depth "pages" $.pages "description" $.description "sortTerm" $.sortTerm}} + {{end}} +{{if eq $.style "li"}} + +{{end}} + {{end}} + + {{end}} + {{end}} +{{end}} From d6c4cecbf8b8bd27a21c17ac7655fca23774a15c Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Tue, 22 Jan 2019 12:15:46 -0700 Subject: [PATCH 14/14] replaced placeholder content and added children shortcode to landing pages --- content/v2.0/query-data/_index.md | 8 +++++--- content/v2.0/reference/flux/_index.md | 7 ++++--- content/v2.0/reference/flux/functions/_index.md | 12 +----------- content/v2.0/reference/flux/language/options.md | 4 +++- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/content/v2.0/query-data/_index.md b/content/v2.0/query-data/_index.md index db478d7fe..10886830e 100644 --- a/content/v2.0/query-data/_index.md +++ b/content/v2.0/query-data/_index.md @@ -1,14 +1,16 @@ --- title: Query data in InfluxDB seotitle: Query data stored in InfluxDB -description: placeholder +description: > + Learn to query data stored in InfluxDB using Flux and tools such as the InfluxDB + user interface and the 'influx' command line interface. menu: v2_0: name: Query data weight: 2 --- -Flux is InfluxData's functional data scripting language designed for querying, analyzing, and acting on data. - +Learn to query data stored in InfluxDB using Flux and tools such as the InfluxDB +user interface and the 'influx' command line interface. {{< children >}} diff --git a/content/v2.0/reference/flux/_index.md b/content/v2.0/reference/flux/_index.md index c2f8e6cde..d41c5aa37 100644 --- a/content/v2.0/reference/flux/_index.md +++ b/content/v2.0/reference/flux/_index.md @@ -1,12 +1,13 @@ --- title: Flux query language -description: placeholder +description: Reference articles for Flux functions and the Flux language specification. menu: v2_0_ref: name: Flux query language weight: 2 --- -[Flux functions](/v2.0/reference/flux/functions/) +The following articles are meant as a reference for Flux functions and the +Flux language specification. -[Flux language specification](/v2.0/reference/flux/language/) +{{< children >}} diff --git a/content/v2.0/reference/flux/functions/_index.md b/content/v2.0/reference/flux/functions/_index.md index a6f31fd37..2e26bcdb0 100644 --- a/content/v2.0/reference/flux/functions/_index.md +++ b/content/v2.0/reference/flux/functions/_index.md @@ -12,14 +12,4 @@ Flux's functional syntax allows you to retrieve, transform, process, and output There is a large library of built-in functions, but you can also create your own custom functions to perform operations that suit your needs. -## [Input functions](/v2.0/reference/flux/functions/inputs) -Input functions define or display information about data sources. - -## [Output functions](/v2.0/reference/flux/functions/outputs) -Output functions yield results or send data to a specified output. - -## [Transformation functions](/v2.0/reference/flux/functions/transformations) -Transformation functions transform or shape your data in specific ways. - -## [Miscellaneous functions](/v2.0/reference/flux/functions/misc) -Functions that serve miscellaneous purposes when writing Flux scripts. +{{< children >}} diff --git a/content/v2.0/reference/flux/language/options.md b/content/v2.0/reference/flux/language/options.md index dd2085aba..1715d82ca 100644 --- a/content/v2.0/reference/flux/language/options.md +++ b/content/v2.0/reference/flux/language/options.md @@ -1,6 +1,8 @@ --- title: Options -description: placeholder +description: > + A Flux option represents a storage location for any value of a specified type. + Options are mutable. An option can hold different values during its lifetime. menu: v2_0_ref: parent: Flux specification