From 5eeb52460dc51483196169f1aaa90195c92a69bd Mon Sep 17 00:00:00 2001 From: Andreas Berger Date: Fri, 8 Jul 2022 08:59:56 +0200 Subject: [PATCH] feat: extend flux-dsl (#373) --- CHANGELOG.md | 11 + flux-dsl/README.md | 167 +++++++++++- .../com/influxdb/query/dsl/Expression.java | 43 +++ .../com/influxdb/query/dsl/Expressions.java | 122 +++++++++ .../java/com/influxdb/query/dsl/Flux.java | 220 +++++++++++++++- .../com/influxdb/query/dsl/HasImports.java | 34 +++ .../query/dsl/IsVariableAssignment.java | 42 +++ .../query/dsl/VariableAssignment.java | 102 ++++++++ .../functions/AbstractFluxWithUpstream.java | 11 +- .../functions/AbstractFunctionCallFlux.java | 84 ++++++ .../dsl/functions/AbstractFunctionFlux.java | 246 ++++++++++++++++++ .../query/dsl/functions/AggregateWindow.java | 30 +++ .../query/dsl/functions/ArrayFromFlux.java | 53 ++++ .../query/dsl/functions/DropFlux.java | 2 +- .../dsl/functions/FreestyleExpression.java | 66 +++++ .../query/dsl/functions/FromFlux.java | 4 +- .../dsl/functions/InterpolateLinearFlux.java | 78 ++++++ .../query/dsl/functions/JoinFlux.java | 37 ++- .../query/dsl/functions/RangeFlux.java | 25 ++ .../dsl/functions/TruncateTimeColumnFlux.java | 70 +++++ .../query/dsl/functions/UnionFlux.java | 70 +++++ .../properties/FunctionsParameters.java | 86 ++++-- .../restriction/ColumnRestriction.java | 8 +- .../influxdb/query/dsl/utils/ImportUtils.java | 46 ++++ .../query/dsl/AbstractFunctionFluxTest.java | 125 +++++++++ .../influxdb/query/dsl/ExpressionsTest.java | 59 +++++ .../dsl/functions/AggregateWindowTest.java | 3 +- .../dsl/functions/ArrayFromFluxTest.java | 63 +++++ .../dsl/functions/InterpolateLinearTest.java | 69 +++++ .../{JoinFluxText.java => JoinFluxTest.java} | 72 ++++- .../query/dsl/functions/RangeFluxTest.java | 26 +- .../dsl/functions/TruncateTimeColumnTest.java | 45 ++++ .../query/dsl/functions/UnionFluxTest.java | 58 +++++ 33 files changed, 2121 insertions(+), 56 deletions(-) create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/Expression.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/Expressions.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/HasImports.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/IsVariableAssignment.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/VariableAssignment.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFunctionCallFlux.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFunctionFlux.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/functions/ArrayFromFlux.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/functions/FreestyleExpression.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/functions/InterpolateLinearFlux.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/functions/TruncateTimeColumnFlux.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/functions/UnionFlux.java create mode 100644 flux-dsl/src/main/java/com/influxdb/query/dsl/utils/ImportUtils.java create mode 100644 flux-dsl/src/test/java/com/influxdb/query/dsl/AbstractFunctionFluxTest.java create mode 100644 flux-dsl/src/test/java/com/influxdb/query/dsl/ExpressionsTest.java create mode 100644 flux-dsl/src/test/java/com/influxdb/query/dsl/functions/ArrayFromFluxTest.java create mode 100644 flux-dsl/src/test/java/com/influxdb/query/dsl/functions/InterpolateLinearTest.java rename flux-dsl/src/test/java/com/influxdb/query/dsl/functions/{JoinFluxText.java => JoinFluxTest.java} (67%) create mode 100644 flux-dsl/src/test/java/com/influxdb/query/dsl/functions/TruncateTimeColumnTest.java create mode 100644 flux-dsl/src/test/java/com/influxdb/query/dsl/functions/UnionFluxTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 54c7cf5ca07..b7c74c1d7b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ ## 6.4.0 [unreleased] +### Features +1. [#373](https://github.com/influxdata/influxdb-client-java/pull/373): + * Add ability to define imports for each flux function [FluxDSL] + * Add ability use multiple flux expressions [FluxDSL] + * Add ability to define custom functions [FluxDSL] + * Improve join flux, so it can be nested [FluxDSL] + * Add missing parameter variants for RangeFlux amd AggregateWindow [FluxDSL] + * Add TruncateTimeColumnFlux [FluxDSL] + * Add ArrayFromFlux [FluxDSL] + * Add UnionFlux [FluxDSL] + ## 6.3.0 [2022-06-30] ### Features diff --git a/flux-dsl/README.md b/flux-dsl/README.md index b2bd0526c59..b550b0f215e 100644 --- a/flux-dsl/README.md +++ b/flux-dsl/README.md @@ -4,7 +4,9 @@ - [Function properties](#function-properties) - [Supported functions](#supported-functions) -- [Custom function](#custom-function) +- [Using Imports](#using-imports) +- [Add missing functions](#add-missing-functions) +- [Custom functions](#custom-functions) - [Time zones](#time-zones) ## Function properties @@ -67,6 +69,19 @@ Flux flux = Flux .last(); ``` +### arrayFrom + +Constructs a table from an array of records. +- `rows` - Array of records to construct a table with. [array of records] + +```java +Map record1 = new HashMap<>(); +record1.put("foo", "bar"); +record1.put("baz", 21.2); + +Flux flux = Flux.arrayFrom(record1, record1); +``` + ### custom expressions ```java Flux flux = Flux.from("telegraf") @@ -336,6 +351,15 @@ Flux flux = Flux .integral(1L, ChronoUnit.MINUTES); ``` +### interpolateLinear +the `interpolate.linear` function inserts rows at regular intervals using linear interpolation to determine values for inserted rows. +- `duration` - Time duration to use when computing the interpolation. [duration] +```java +Flux flux = Flux + .from("telegraf") + .interpolateLinear(1L, ChronoUnit.MINUTES); +``` + ### join Join two time series together on time and the list of `on` keys [[doc](http://bit.ly/flux-spec#join)]. - `tables` - Map of tables to join. Currently only two tables are allowed. [map of tables] @@ -631,6 +655,16 @@ Flux flux = Flux .timeShift(10L, ChronoUnit.HOURS, new String[]{"_time", "custom"}); ``` +### truncateTimeColumn +Truncates all input time values in the _time to a specified unit. [[doc](http://bit.ly/flux-spec#truncateTimeColumn)]. +- `unit` - Unit of time to truncate to. Has to be defined. [duration] +```java +Flux flux = Flux + .from("telegraf") + .truncateTimeColumn(ChronoUnit.SECONDS); +``` + + ### skew Skew of the results [[doc](http://bit.ly/flux-spec#skew)]. - `column` - The column on which to operate. Defaults to `_value`. [string] @@ -783,6 +817,24 @@ Flux flux = Flux .toUInt(); ``` +### union + +Merges two or more input streams into a single output stream [[doc](http://bit.ly/flux-spec#union)]. +- `tables` - the tables to union. [array of flux] + +```java +Flux flux = Flux.union( + Flux.from("telegraf1"), + Flux.from("telegraf2") +); +``` + +```java +Flux v1 = Flux.from("telegraf1").asVariable("v1"); +Flux v2 = Flux.from("telegraf1").asVariable("v2"); +Flux flux = Flux.union(v1, v2); +``` + ### window Groups the results by a given time range [[doc](http://bit.ly/flux-spec#window)]. - `every` - Duration of time between windows. Defaults to `period's` value. [duration] @@ -817,7 +869,13 @@ Flux flux = Flux .yield("0"); ``` -## Custom function +## Using Imports + +Each Flux-Class can add required imports via the `addImport` method. +For an example take a look at the [custom functions section](#custom-functions) below. + +## Add missing functions + We assume that exist custom function measurement that filter measurement by their name. The `Flux` implementation looks like this: ```flux @@ -873,6 +931,109 @@ from(bucket:"telegraf") |> sum() ``` +## Custom functions + +### Custom function with a flux implementation + +```java +public static class MyCustomFunction extends AbstractFunctionFlux { + + public MyCustomFunction() { + super("from2", + new FromFlux() + .withPropertyValue("bucket", "n"), + MyCustomFunctionCall::new, + new Parameter("n")); + addImport("foo"); + } + + public static class MyCustomFunctionCall extends AbstractFunctionCallFlux { + + public MyCustomFunctionCall(@Nonnull String name) { + super(name); + } + + public MyCustomFunctionCall withN(final String n) { + this.withPropertyValueEscaped("n", n); + return this; + } + } +} +``` + +usage: + +```java +MyCustomFunction fun = new MyCustomFunction(); + +Expressions flux = new Expressions( + fun, + fun.invoke().withN(0) +); +flux.toString() +``` + +result: + +```javascript +import "foo" +from2 = (n) => from(bucket: n) +from2(n:"telegraf") +``` + +### Defining a custom function with a freestyle expression + +```java +public static class MultByXFunction extends AbstractFunctionFlux { + + public MultByXFunction() { + super("multByX", + new FreestyleExpression("tables") + .map("(r) => ({r with _value: r._value * x})"), + MultByXFunctionCall::new, + new Parameter("tables").withPipeForward(true), + new Parameter("x")); + } + + public static class MultByXFunctionCall extends AbstractFunctionCallFlux { + + public MultByXFunctionCall(@Nonnull String name) { + super(name); + } + + @Nonnull + public MultByXFunctionCall withX(final Number x) { + this.withPropertyValue("x", x); + return this; + } + } +} +``` + +usage: + +```java +MultByXFunction multByX = new MultByXFunction(); +Expressions flux = new Expressions( + multByX, + Flux.from("telegraph") + .withPipedFunction(multByX) + .withX(42.) + .count() +); +flux.toString() +``` + +result: + +```javascript +multByX = (tables=<-, x) => () + |> map(fn: (r) => (r) => ({r with _value: r._value * x})) +from(bucket:"telegraph") + |> multByX(x:42.0) + |> count() +``` + ## Time zones The Flux option sets the default time zone of all times in the script. The default value is `timezone.utc`. @@ -955,4 +1116,4 @@ The snapshots are deployed into [OSS Snapshot repository](https://oss.sonatype.o repositories { maven { url "https://oss.sonatype.org/content/repositories/snapshots" } } -``` \ No newline at end of file +``` diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/Expression.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/Expression.java new file mode 100644 index 00000000000..17f861e2c6b --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/Expression.java @@ -0,0 +1,43 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl; + +import java.util.Map; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.functions.FreestyleExpression; + +/** + * Marker interface for Expressions. + * + * @see com.influxdb.query.dsl.Flux + * @see FreestyleExpression + */ +public interface Expression extends HasImports { + + /** + * @param parameters parameters to resolve + * @param prependImports true, if the imports should be prepended + * @return the string representation of the expression + */ + String toString(@Nonnull final Map parameters, final boolean prependImports); +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/Expressions.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/Expressions.java new file mode 100644 index 00000000000..eb559e1089b --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/Expressions.java @@ -0,0 +1,122 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.utils.ImportUtils; +import com.influxdb.utils.Arguments; + +/** + * A container holding a list of {@link Expression}s. + * + *

Example

+ *
+ * VariableAssignment a = Flux.from("test1").asVariable("a");
+ * VariableAssignment b = Flux.from("test2").asVariable("b");
+ *
+ * String flux = new Expressions(
+ *    a,
+ *    b,
+ *    a.first().yield("firstA"),
+ *    b.first().yield("firstB"),
+ *    a.last().yield("lastA"),
+ *    b.last().yield("lastB")
+ * ).toString();
+ * 
+ */ +public class Expressions implements HasImports { + private final List expressions = new ArrayList<>(); + + /** + * @param expressions the expressions to be used + */ + public Expressions(@Nonnull final Expression... expressions) { + Arguments.checkNotNull(expressions, "expressions"); + + this.expressions.addAll(Arrays.stream(expressions).collect(Collectors.toList())); + } + + /** + * @param expressions the expressions to be used + */ + public Expressions(@Nonnull final Collection expressions) { + Arguments.checkNotNull(expressions, "expressions"); + + this.expressions.addAll(expressions); + } + + /** + * Adds another expression to this container. + * + * @param expressions the expressions to be added + * @return this + */ + public Expressions addExpressions(@Nonnull final Expression... expressions) { + Arguments.checkNotNull(expressions, "expression"); + + this.expressions.addAll(Arrays.asList(expressions)); + return this; + } + + /** + * @param parameters parameters to resolve + * @param prependImports true, if the imports should be prepended + * @return the string representation of the expressions + */ + public String toString(@Nonnull final Map parameters, final boolean prependImports) { + StringBuilder builder = new StringBuilder(); + + if (prependImports) { + builder.append(ImportUtils.getImportsString(this)); + } + + for (Expression expression : expressions) { + builder.append(expression.toString(parameters, false)).append("\n"); + } + + return builder.toString(); + } + + @Override + public String toString() { + return toString(Collections.emptyMap(), true); + } + + @Override + public Set getImports() { + return expressions + .stream() + .map(HasImports::getImports) + .flatMap(Collection::stream) + .collect(Collectors.toCollection(TreeSet::new)); + } +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/Flux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/Flux.java index 9eb6d3ba5f5..876c0fb2474 100644 --- a/flux-dsl/src/main/java/com/influxdb/query/dsl/Flux.java +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/Flux.java @@ -25,12 +25,18 @@ import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import com.influxdb.query.dsl.functions.AbstractFunctionCallFlux; +import com.influxdb.query.dsl.functions.AbstractFunctionFlux; import com.influxdb.query.dsl.functions.AbstractParametrizedFlux; import com.influxdb.query.dsl.functions.AggregateWindow; +import com.influxdb.query.dsl.functions.ArrayFromFlux; import com.influxdb.query.dsl.functions.ColumnsFlux; import com.influxdb.query.dsl.functions.CountFlux; import com.influxdb.query.dsl.functions.CovarianceFlux; @@ -46,6 +52,7 @@ import com.influxdb.query.dsl.functions.FromFlux; import com.influxdb.query.dsl.functions.GroupFlux; import com.influxdb.query.dsl.functions.IntegralFlux; +import com.influxdb.query.dsl.functions.InterpolateLinearFlux; import com.influxdb.query.dsl.functions.JoinFlux; import com.influxdb.query.dsl.functions.KeepFlux; import com.influxdb.query.dsl.functions.LastFlux; @@ -76,10 +83,13 @@ import com.influxdb.query.dsl.functions.ToStringFlux; import com.influxdb.query.dsl.functions.ToTimeFlux; import com.influxdb.query.dsl.functions.ToUIntFlux; +import com.influxdb.query.dsl.functions.TruncateTimeColumnFlux; +import com.influxdb.query.dsl.functions.UnionFlux; import com.influxdb.query.dsl.functions.WindowFlux; import com.influxdb.query.dsl.functions.YieldFlux; import com.influxdb.query.dsl.functions.properties.FunctionsParameters; import com.influxdb.query.dsl.functions.restriction.Restrictions; +import com.influxdb.query.dsl.utils.ImportUtils; import com.influxdb.utils.Arguments; /** @@ -139,9 +149,10 @@ * @author Jakub Bednar (bednar@github) (22/06/2018 10:16) */ @SuppressWarnings({"FileLength"}) -public abstract class Flux { +public abstract class Flux implements HasImports, Expression { protected FunctionsParameters functionsParameters = FunctionsParameters.of(); + protected Set imports; /** * Get data from the specified database. @@ -242,6 +253,28 @@ public final AggregateWindow aggregateWindow(@Nonnull final Long every, return new AggregateWindow(this).withEvery(every, everyUnit).withAggregateFunction(namedFunction); } + /** + * Constructs a table from an array of records. + * + * @return {@link ArrayFromFlux} + */ + @Nonnull + public static ArrayFromFlux arrayFrom() { + return new ArrayFromFlux(); + } + + /** + * Constructs a table from an array of records. + * + * @param rows Array of records to construct a table with. + * @return {@link ArrayFromFlux} + */ + @SafeVarargs + @Nonnull + public static ArrayFromFlux arrayFrom(@Nonnull final Map... rows) { + return arrayFrom().withRow(rows); + } + /** * Join two time series together on time and the list of tags. * @@ -958,6 +991,40 @@ public final IntegralFlux integral(@Nonnull final Long duration, @Nonnull final return new IntegralFlux(this).withUnit(duration, unit); } + /** + * The `interpolate.linear` function inserts rows at regular intervals using linear interpolation to determine + * values for inserted rows. + * + *

The parameters had to be defined by:

+ *
    + *
  • {@link InterpolateLinearFlux#withEvery(long, ChronoUnit)}
  • + *
  • {@link InterpolateLinearFlux#withPropertyNamed(String)}
  • + *
  • {@link InterpolateLinearFlux#withPropertyNamed(String, String)}
  • + *
+ * + * @return {@link InterpolateLinearFlux} + */ + @Nonnull + public final InterpolateLinearFlux interpolateLinear() { + return new InterpolateLinearFlux(this); + } + + /** + * The `interpolate.linear` function inserts rows at regular intervals using linear interpolation to determine + * values for inserted rows. + * + * @param duration Time duration to use when computing the interpolation + * @param unit a {@code ChronoUnit} determining how to interpret the {@code duration} parameter + * @return {@link InterpolateLinearFlux} + */ + @Nonnull + public final InterpolateLinearFlux interpolateLinear(@Nonnull final Long duration, @Nonnull final ChronoUnit unit) { + Arguments.checkNotNull(duration, "Duration is required"); + Arguments.checkNotNull(unit, "ChronoUnit is required"); + + return new InterpolateLinearFlux(this).withEvery(duration, unit); + } + /** * Returns the last result of the query. * @@ -1373,6 +1440,29 @@ public final RangeFlux range(@Nonnull final Long start, @Nonnull final Long stop return new RangeFlux(this).withStart(start, unit).withStop(stop, unit); } + /** + * Filters the results by time boundaries. + * + * @param start Specifies the oldest time (Unix timestamp in seconds) to be included in the results + * @param stop Specifies the exclusive newest time (Unix timestamp in seconds) to be included in the results + * @return {@link RangeFlux} + */ + @Nonnull + public final RangeFlux range(final Long start, final Long stop) { + return new RangeFlux(this).withStart(start).withStop(stop); + } + + /** + * Filters the results by time boundaries. + * + * @param start Specifies the oldest time (Unix timestamp in seconds) to be included in the results + * @return {@link RangeFlux} + */ + @Nonnull + public final RangeFlux range(final Long start) { + return new RangeFlux(this).withStart(start); + } + /** * Reduce aggregates records in each table according to the reducer. * @@ -2047,6 +2137,29 @@ public final ToUIntFlux toUInt() { return new ToUIntFlux(this); } + /** + * Truncates all input time values in the _time to a specified unit. + * + * @param unit Unit of time to truncate to. Has to be defined. + * @return {@link TruncateTimeColumnFlux} + */ + @Nonnull + public final TruncateTimeColumnFlux truncateTimeColumn(@Nonnull final ChronoUnit unit) { + return new TruncateTimeColumnFlux(this).withUnit(unit); + } + + + /** + * Merges two or more input streams into a single output stream. + * + * @param tables the tables to union + * @return {@link UnionFlux} + */ + public static UnionFlux union(@Nonnull final Flux... tables) { + return new UnionFlux() + .withTables(tables); + } + /** * Groups the results by a given time range. * @@ -2299,6 +2412,56 @@ public final F function(@Nonnull final Clas } } + /** + * Creates a piped function call. + * + *

Example Definition

+ *
+     * public static class MultByXFunction extends AbstractFunctionFlux<MultByXFunction.MultByXFunctionCall> {
+     *
+     *     public MultByXFunction() {
+     *         super("multByX",
+     *                 new FreestyleExpression("tables")
+     *                         .map("(r) => ({r with _value: r._value * x})"),
+     *                 MultByXFunctionCall::new,
+     *                 new Parameter("tables").withPipeForward(true),
+     *                 new Parameter("x"));
+     *     }
+     *
+     *     public static class MultByXFunctionCall extends AbstractFunctionCallFlux {
+     *
+     *         public MultByXFunctionCall(@Nonnull String name) {
+     *             super(name);
+     *         }
+     *
+     *         public MultByXFunctionCall withX(final Number x) {
+     *             this.withPropertyValue("x", x);
+     *             return this;
+     *         }
+     *     }
+     * }
+     * 
+ *

Example Usage

+ *
+     * MultByXFunction multByX = new MultByXFunction();
+     *
+     * Expressions flux = new Expressions(
+     *         multByX,
+     *         Flux.from("telegraph")
+     *                 .withPipedFunction(multByX)
+     *                 .withX(42.)
+     *                 .count()
+     * );
+     * 
+ * + * @param fun the function to be piped + * @param the type of the invocation + * @return the invocation + */ + public CALL withPipedFunction(final AbstractFunctionFlux fun) { + return fun.invokePiped(this); + } + /** * Add named property to current function. * @@ -2518,13 +2681,68 @@ public String toString() { */ @Nonnull public String toString(@Nonnull final Map parameters) { + return toString(parameters, true); + } + @Override + public String toString(@Nonnull final Map parameters, final boolean prependImports) { Arguments.checkNotNull(parameters, "Parameters are required"); StringBuilder builder = new StringBuilder(); + if (prependImports) { + builder.append(ImportUtils.getImportsString(this)); + } + appendActual(parameters, builder); return builder.toString(); } + + /** + * Converts this flux to a variable assignment so multiple fluxes can be used in conjunction with + * {@link Expressions}. + * + * @param name the name of the variable + * @return a variable assignment + * @see Expressions + */ + public VariableAssignment asVariable(final String name) { + return new VariableAssignment(name, this); + } + + /** + * @return all used imports of this flux + */ + @Override + public Set getImports() { + Set collectedImports = new TreeSet<>(); + collectImports(collectedImports); + return collectedImports; + } + + /** + * Collects all imports of the flux. + * + * @param collectedImports a set to be filled by the used imports + */ + public void collectImports(@Nonnull final Set collectedImports) { + if (imports != null) { + collectedImports.addAll(imports); + } + } + + /** + * Adds a new import to this flux. + * + * @param pImport the import to be added + * @return this + */ + protected Flux addImport(final String pImport) { + if (imports == null) { + imports = new HashSet<>(); + } + imports.add(pImport); + return this; + } } diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/HasImports.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/HasImports.java new file mode 100644 index 00000000000..d763bebd62a --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/HasImports.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl; + +import java.util.Set; + +/** + * Marker interface indicating existence of potential imports. + */ +public interface HasImports { + /** + * @return the imports + */ + Set getImports(); +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/IsVariableAssignment.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/IsVariableAssignment.java new file mode 100644 index 00000000000..bd41102959a --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/IsVariableAssignment.java @@ -0,0 +1,42 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl; + +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.functions.AbstractFunctionCallFlux; + +/** + * Marker interface for variable assignments. + * + * @see AbstractFunctionCallFlux + * @see VariableAssignment + */ +public interface IsVariableAssignment { + + /** + * @return the name of the variable + */ + @Nonnull + String getVariableName(); + +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/VariableAssignment.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/VariableAssignment.java new file mode 100644 index 00000000000..d6c58b824f7 --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/VariableAssignment.java @@ -0,0 +1,102 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl; + +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.utils.ImportUtils; + +/** + * Hold the variable name and expression of an assignment. + * + *

Example

+ *
+ * VariableAssignment a = Flux.from("test1").asVariable("a");
+ * VariableAssignment b = Flux.from("test2").asVariable("b");
+ *
+ * String flux = new Expressions(
+ *    a,
+ *    b,
+ *    a.first().yield("firstA"),
+ *    b.first().yield("firstB"),
+ *    a.last().yield("lastA"),
+ *    b.last().yield("lastB")
+ * ).toString();
+ * 
+ * + * @see Expressions + * @see Flux#asVariable(String) + * @see com.influxdb.query.dsl.functions.AbstractFunctionFlux + */ +public class VariableAssignment extends Flux implements IsVariableAssignment { + + @Nonnull + protected final String name; + + @Nonnull + protected final Expression expression; + + /** + * @param name the name of teh variable + * @param expression the expression to assign to the variable + */ + public VariableAssignment(@Nonnull final String name, @Nonnull final Expression expression) { + this.name = name; + this.expression = expression; + } + + @Nonnull + public String getVariableName() { + return name; + } + + @Override + public void appendActual(@Nonnull final Map parameters, @Nonnull final StringBuilder builder) { + builder.append(name); + } + + @Override + public String toString(@Nonnull final Map parameters, final boolean prependImports) { + StringBuilder builder = new StringBuilder(); + + if (prependImports) { + builder.append(ImportUtils.getImportsString(this)); + } + builder.append(name).append(" = ").append(expression.toString(parameters, false)); + + return builder.toString(); + } + + + @Override + public Set getImports() { + Set result = new TreeSet<>(expression.getImports()); + if (imports != null) { + result.addAll(imports); + } + return result; + } + +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFluxWithUpstream.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFluxWithUpstream.java index 6e95970a79d..9511dd1a55b 100644 --- a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFluxWithUpstream.java +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFluxWithUpstream.java @@ -22,6 +22,7 @@ package com.influxdb.query.dsl.functions; import java.util.Map; +import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -62,9 +63,17 @@ public void appendActual(@Nonnull final Map parameters, @Nonnull * @param builder Flux query chain. */ void appendDelimiter(@Nonnull final StringBuilder builder) { - if (builder.length() != 0) { + if (source != null) { builder.append("\n"); builder.append("\t|> "); } } + + @Override + public void collectImports(@Nonnull final Set collectedImports) { + super.collectImports(collectedImports); + if (source != null) { + source.collectImports(collectedImports); + } + } } diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFunctionCallFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFunctionCallFlux.java new file mode 100644 index 00000000000..54196d15e94 --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFunctionCallFlux.java @@ -0,0 +1,84 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.util.Map; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.IsVariableAssignment; +import com.influxdb.query.dsl.utils.ImportUtils; + +/** + * Base class defining a function invocation. + * + *

Example

+ *
+ *  public static class MyCustomFunctionCall extends AbstractFunctionCallFlux {
+ *     public MyCustomFunctionCall(@Nonnull String name) {
+ *         super(name);
+ *     }
+ *
+ *     public MyCustomFunctionCall withN(final String n) {
+ *         this.withPropertyValueEscaped("n", n);
+ *         return this;
+ *     }
+ * }
+ * 
+ */ +public abstract class AbstractFunctionCallFlux extends AbstractParametrizedFlux implements IsVariableAssignment { + + private final String name; + + /** + * Will be called by the {@link AbstractFunctionFlux#invoke()} method of the function definition. + * + * @param name the name of the variable holding the implementation of the function + */ + protected AbstractFunctionCallFlux(@Nonnull final String name) { + this.name = name; + } + + @Nonnull + @Override + public String getVariableName() { + return name; + } + + + @Nonnull + @Override + protected String operatorName() { + return name; + } + + @Override + public String toString(@Nonnull final Map parameters, final boolean prependImports) { + StringBuilder builder = new StringBuilder(); + + if (prependImports) { + builder.append(ImportUtils.getImportsString(this)); + } + appendActual(parameters, builder); + + return builder.toString(); + } +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFunctionFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFunctionFlux.java new file mode 100644 index 00000000000..103228c3c9a --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AbstractFunctionFlux.java @@ -0,0 +1,246 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.influxdb.query.dsl.Expression; +import com.influxdb.query.dsl.Expressions; +import com.influxdb.query.dsl.Flux; +import com.influxdb.query.dsl.VariableAssignment; +import com.influxdb.query.dsl.functions.properties.TimeInterval; +import com.influxdb.query.dsl.utils.ImportUtils; +import com.influxdb.utils.Arguments; + +import static com.influxdb.query.dsl.functions.properties.FunctionsParameters.escapeDoubleQuotes; + +/** + * The base class for function definitions. + * The function definition describes the implementation of a function. + * A concrete call to this function can be created via the {@link #invoke()} method. + * + *

Example Definition

+ *
+ * public static class MyCustomFunction extends AbstractFunctionFlux<MyCustomFunction.MyCustomFunctionCall> {
+ *
+ *     public MyCustomFunction() {
+ *         super("from2",
+ *                 new FromFlux()
+ *                         .withPropertyValue("bucket", "n"),
+ *                 MyCustomFunctionCall::new,
+ *                 new Parameter("n"));
+ *         addImport("foo");
+ *     }
+ *
+ *     public static class MyCustomFunctionCall extends AbstractFunctionCallFlux {
+ *
+ *         public MyCustomFunctionCall(@Nonnull String name) {
+ *             super(name);
+ *         }
+ *
+ *         public MyCustomFunctionCall withN(final String n) {
+ *             this.withPropertyValueEscaped("n", n);
+ *             return this;
+ *         }
+ *     }
+ * }
+ * 
+ *

Example Usage

+ *
+ * MyCustomFunction fun = new MyCustomFunction();
+ *
+ * Expressions flux = new Expressions(
+ *    fun,
+ *    fun.invoke().withN("bar").count(),
+ *    fun.invoke().withN("bar2"),
+ * );
+ * 
+ * + * @param the type of the {@link AbstractFunctionCallFlux} + * @see Expressions + */ +public class AbstractFunctionFlux extends VariableAssignment { + + @Nonnull + private final Function invocationFactory; + @Nonnull + private final Parameter[] parameter; + + /** + * @param name the name of the function (the variable th function is assigned to) + * @param functionDefinition the function body + * @param invocationFactory a factory to create an invocation, should be the + * {@link AbstractFunctionCallFlux#AbstractFunctionCallFlux(String) constructor reference} + * to the concrete CALL implementation + * @param parameter the parameters of this function + */ + protected AbstractFunctionFlux( + @Nonnull final String name, + @Nonnull final Expression functionDefinition, + @Nonnull final Function invocationFactory, + @Nonnull final Parameter... parameter) { + super(name, functionDefinition); + this.invocationFactory = invocationFactory; + this.parameter = parameter; + } + + @Override + public String toString(@Nonnull final Map parameters, final boolean prependImports) { + StringBuilder builder = new StringBuilder(); + + if (prependImports) { + builder.append(ImportUtils.getImportsString(this)); + } + builder.append(name).append(" = ") + .append(Arrays.stream(parameter) + .map(Parameter::toString) + .collect(Collectors.joining(", ", "(", ") => "))) + .append(expression.toString(parameters, false)); + return builder.toString(); + } + + /** + * @return A flux invoking this method. The invocation arguments can be adjusted via this flux. + */ + @Nonnull + public CALL invoke() { + return invocationFactory.apply(getVariableName()); + } + + @Nonnull + public CALL invokePiped(@Nonnull final Flux flux) { + Arguments.checkNotNull(flux, "Source is required"); + + CALL invoke = invoke(); + invoke.source = flux; + return invoke; + } + + @Override + public Set getImports() { + return super.getImports(); + } + + + public static class Parameter { + @Nonnull + private final String name; + private String defaultValue; + + private boolean pipeForward; + + private boolean optional; + + public Parameter(@Nonnull final String name) { + + Arguments.checkNotNull(name, "name"); + + this.name = name; + } + + @Nonnull + public String getName() { + return name; + } + + @Nullable + public String getDefaultValue() { + return defaultValue; + } + + @Nonnull + public Parameter withDefaultValue(@Nonnull final Number defaultValue) { + Arguments.checkNotNull(defaultValue, "defaultValue"); + + this.defaultValue = String.valueOf(defaultValue); + return this; + } + + @Nonnull + public Parameter withDefaultValue(@Nonnull final String defaultValue) { + Arguments.checkNotNull(defaultValue, "defaultValue"); + + this.defaultValue = "\"" + escapeDoubleQuotes(defaultValue) + "\""; + + return this; + } + + @Nonnull + public Parameter withDefaultValue(final boolean defaultValue) { + this.defaultValue = String.valueOf(defaultValue); + return this; + } + + @Nonnull + public Parameter withDefaultValue(final long amount, @Nonnull final ChronoUnit unit) { + Arguments.checkNotNull(unit, "unit"); + + this.defaultValue = new TimeInterval(amount, unit).toString(); + return this; + } + + /** + * Indicates the parameter that, by default, represents the piped-forward value. + * + * @param pipeForward true to use <- for this parameter + * @return this + * @see Flux#withPipedFunction(AbstractFunctionFlux) + */ + @Nonnull + public Parameter withPipeForward(final boolean pipeForward) { + this.pipeForward = pipeForward; + return this; + } + + /** + * @param optional true, if this parameter is optional + * @return this + */ + @Nonnull + public Parameter withOptional(final boolean optional) { + this.optional = optional; + return this; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + if (optional) { + result.append("?"); + } + result.append(name); + if (pipeForward) { + result.append("=<-"); + } else if (defaultValue != null) { + result.append(" = ").append(defaultValue); + } + return result.toString(); + } + } +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AggregateWindow.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AggregateWindow.java index dd5fa3676ad..5df700d43a8 100644 --- a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AggregateWindow.java +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/AggregateWindow.java @@ -195,4 +195,34 @@ public AggregateWindow withCreateEmpty(final boolean createEmpty) { return this; } + + /** + * @param amount The offset of windows. + * @param unit a {@code ChronoUnit} determining how to interpret the {@code amount}. + * @return this + */ + @Nonnull + public AggregateWindow withOffset(@Nonnull final Long amount, @Nonnull final ChronoUnit unit) { + + Arguments.checkNotNull(amount, "amount"); + Arguments.checkNotNull(unit, "unit"); + + this.withPropertyValue("offset", amount, unit); + + return this; + } + + /** + * @param offset The offset of windows. + * @return this + */ + @Nonnull + public AggregateWindow withOffset(@Nonnull final String offset) { + + Arguments.checkDuration(offset, "offset"); + + this.withPropertyValue("offset", offset); + + return this; + } } diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/ArrayFromFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/ArrayFromFlux.java new file mode 100644 index 00000000000..7967e3e4dfa --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/ArrayFromFlux.java @@ -0,0 +1,53 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.util.Map; +import javax.annotation.Nonnull; + +/** + * Constructs a table from an array of records. + */ +public final class ArrayFromFlux extends AbstractParametrizedFlux { + + public ArrayFromFlux() { + addImport("array"); + } + + @Nonnull + @Override + protected String operatorName() { + return "array.from"; + } + + + /** + * @param rows Array of records to construct a table with. + * @return {@link ArrayFromFlux} + */ + @Nonnull + @SafeVarargs + public final ArrayFromFlux withRow(@Nonnull final Map... rows) { + withPropertyValue("rows", rows); + return this; + } +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/DropFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/DropFlux.java index ec34e865a0f..b677efe74bd 100644 --- a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/DropFlux.java +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/DropFlux.java @@ -111,4 +111,4 @@ public DropFlux withFunction(@Nonnull final String function) { return this; } -} \ No newline at end of file +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/FreestyleExpression.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/FreestyleExpression.java new file mode 100644 index 00000000000..1b6fadcccf2 --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/FreestyleExpression.java @@ -0,0 +1,66 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.util.Map; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.Expression; +import com.influxdb.query.dsl.HasImports; +import com.influxdb.query.dsl.utils.ImportUtils; +import com.influxdb.utils.Arguments; + +/** + * An expression to encapsulate an arbitrary expression. + */ +public final class FreestyleExpression extends AbstractParametrizedFlux implements HasImports, Expression { + + private final String expression; + + /** + * @param expression the string representation of th expression to encapsulate. + */ + public FreestyleExpression(@Nonnull final String expression) { + + Arguments.checkNonEmpty(expression, "Expression"); + + this.expression = expression; + } + + @Override + public String toString(@Nonnull final Map parameters, final boolean prependImports) { + StringBuilder builder = new StringBuilder(); + + if (prependImports) { + builder.append(ImportUtils.getImportsString(this)); + } + builder.append(expression); + + return builder.toString(); + } + + @Nonnull + @Override + protected String operatorName() { + return ""; + } +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/FromFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/FromFlux.java index dbaa23a3333..ec5b2ac7a09 100644 --- a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/FromFlux.java +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/FromFlux.java @@ -115,6 +115,7 @@ public FromFlux withHosts(@Nonnull final String[] hosts) { public Flux withLocationNamed(@Nonnull final String name) { Arguments.checkNonEmpty(name, "name"); this.options.location = String.format("timezone.location(name: \"%s\")", name); + addImport("timezone"); return this; } @@ -128,6 +129,7 @@ public Flux withLocationNamed(@Nonnull final String name) { public Flux withLocationFixed(@Nonnull final String offset) { Arguments.checkDuration(offset, "offset"); this.options.location = String.format("timezone.fixed(offset: %s)", offset); + addImport("timezone"); return this; } @@ -135,8 +137,6 @@ public Flux withLocationFixed(@Nonnull final String offset) { public void appendActual(@Nonnull final Map parameters, @Nonnull final StringBuilder builder) { // append timezone configuration if (options.location != null) { - builder.append("import \"timezone\""); - builder.append("\n"); builder.append("option location = "); builder.append(options.location); builder.append("\n"); diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/InterpolateLinearFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/InterpolateLinearFlux.java new file mode 100644 index 00000000000..99dbee38480 --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/InterpolateLinearFlux.java @@ -0,0 +1,78 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.time.temporal.ChronoUnit; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.Flux; +import com.influxdb.utils.Arguments; + +/** + * Inserts rows at regular intervals using linear interpolation to determine values for inserted rows. + * + *

Example

+ *
+ *         Flux flux = Flux
+ *                 .from("telegraf")
+ *                 .interpolateLinear()
+ *                 .withEvery(5L, ChronoUnit.MINUTES);
+ * 
+ */ +public class InterpolateLinearFlux extends AbstractParametrizedFlux { + + public InterpolateLinearFlux(@Nonnull final Flux source) { + super(source); + addImport("interpolate"); + } + + @Nonnull + @Override + protected String operatorName() { + return "interpolate.linear"; + } + + /** + * + * @param duration Time duration to use when computing the interpolation + * @param unit The unit of the duration + * @return this + */ + public InterpolateLinearFlux withEvery(final long duration, final ChronoUnit unit) { + this.withPropertyValue("every", duration, unit); + return this; + } + + /** + * @param every Time duration to use when computing the interpolation + * @return this + */ + @Nonnull + public InterpolateLinearFlux withEvery(@Nonnull final String every) { + + Arguments.checkDuration(every, "every"); + + this.withPropertyValue("every", every); + + return this; + } +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/JoinFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/JoinFlux.java index da99cff2697..f5e5f04eb2e 100644 --- a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/JoinFlux.java +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/JoinFlux.java @@ -24,11 +24,12 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; -import java.util.StringJoiner; import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import com.influxdb.query.dsl.Flux; +import com.influxdb.query.dsl.VariableAssignment; import com.influxdb.utils.Arguments; /** @@ -64,19 +65,21 @@ */ public final class JoinFlux extends AbstractParametrizedFlux { - private Map tables = new LinkedHashMap<>(); + private final Map tables = new LinkedHashMap<>(); + private String assignToVariable; public JoinFlux() { super(); // add tables: property - withPropertyValue("tables", (Supplier) () -> { - - StringJoiner tablesValue = new StringJoiner(", ", "{", "}"); - - tables.keySet().forEach(key -> tablesValue.add(String.format("%s:%s", key, key))); - return tablesValue.toString(); - }); + withPropertyValue("tables", (Supplier) () -> tables + .entrySet().stream().map(e -> { + String var = e.getValue() instanceof VariableAssignment + ? ((VariableAssignment) e.getValue()).getVariableName() + : e.getKey(); + return e.getKey() + ":" + var; + }) + .collect(Collectors.joining(", ", "{", "}"))); } public enum MethodType { @@ -118,10 +121,16 @@ protected void beforeAppendOperatorName(@Nonnull final StringBuilder operator, @Nonnull final Map parameters) { // add tables Flux scripts - tables.keySet().forEach(key -> { - - operator.append(String.format("%s = %s\n", key, tables.get(key).toString(parameters))); + tables.forEach((key, flux) -> { + if (flux instanceof JoinFlux) { + operator.append(flux.toString(parameters)).append("\n"); + } else if (!(flux instanceof VariableAssignment)) { + operator.append(key).append(" = ").append(flux.toString(parameters)).append("\n"); + } }); + if (assignToVariable != null) { + operator.append(assignToVariable).append(" = "); + } } /** @@ -138,7 +147,9 @@ public JoinFlux withTable(@Nonnull final String name, @Nonnull final Flux table) Arguments.checkNotNull(table, "Flux script to map table"); tables.put(name, table); - + if (table instanceof JoinFlux) { + ((JoinFlux) table).assignToVariable = name; + } return this; } diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/RangeFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/RangeFlux.java index 7f36dc9eef9..a894fdc1aec 100644 --- a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/RangeFlux.java +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/RangeFlux.java @@ -106,6 +106,18 @@ public RangeFlux withStart(@Nonnull final String start) { return this; } + /** + * @param start Specifies the oldest time (Unix timestamp in seconds) to be included in the results + * @return this + */ + @Nonnull + public RangeFlux withStart(final Long start) { + + this.withPropertyValue("start", start); + + return this; + } + /** * @param stop Specifies the exclusive newest time to be included in the results * @return this @@ -149,4 +161,17 @@ public RangeFlux withStop(@Nonnull final String stop) { return this; } + + /** + * @param stop Specifies the exclusive newest time (Unix timestamp in seconds) to be included in the results + * @return this + */ + @Nonnull + public RangeFlux withStop(final Long stop) { + + this.withPropertyValue("stop", stop); + + return this; + } + } diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/TruncateTimeColumnFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/TruncateTimeColumnFlux.java new file mode 100644 index 00000000000..5f60ada1c7f --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/TruncateTimeColumnFlux.java @@ -0,0 +1,70 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.time.temporal.ChronoUnit; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.Flux; +import com.influxdb.utils.Arguments; + +/** + * Truncates all input time values in the _time to a specified unit.. + * + *

Options

+ *
    + *
  • unit - Unit of time to truncate to. [unit].
  • + *
+ * + *

Example

+ *
+ * Flux flux = Flux
+ *     .from("telegraf")
+ *     .truncateTimeColumn("s");
+ * 
+ * + */ +public final class TruncateTimeColumnFlux extends AbstractParametrizedFlux { + + public TruncateTimeColumnFlux(@Nonnull final Flux source) { + super(source); + } + + @Nonnull + @Override + protected String operatorName() { + return "truncateTimeColumn"; + } + + /** + * @param unit Unit of time to truncate to. Has to be defined. + * @return this + */ + @Nonnull + public TruncateTimeColumnFlux withUnit(@Nonnull final ChronoUnit unit) { + Arguments.checkNotNull(unit, "unit"); + + withPropertyValue("unit", 1L, unit); + + return this; + } +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/UnionFlux.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/UnionFlux.java new file mode 100644 index 00000000000..05be1131909 --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/UnionFlux.java @@ -0,0 +1,70 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.Flux; +import com.influxdb.query.dsl.VariableAssignment; +import com.influxdb.utils.Arguments; + +/** + * Merges two or more input streams into a single output stream.. + */ +public final class UnionFlux extends AbstractParametrizedFlux { + + private final List tables = new ArrayList<>(); + + public UnionFlux() { + super(); + withPropertyValue("tables", (Supplier) () -> tables + .stream().map(table -> table instanceof VariableAssignment + ? ((VariableAssignment) table).getVariableName() + : table.toString()) + .collect(Collectors.joining(",", "[", "]"))); + } + + + @Nonnull + @Override + protected String operatorName() { + return "union"; + } + + /** + * @param tables Flux script to union + * @return this + */ + @Nonnull + public UnionFlux withTables(@Nonnull final Flux... tables) { + Arguments.checkNotNull(tables, "tables"); + this.tables.addAll(Arrays.asList(tables)); + return this; + } + + +} diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/properties/FunctionsParameters.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/properties/FunctionsParameters.java index 4f4e853d1ad..daefb3bd2d4 100644 --- a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/properties/FunctionsParameters.java +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/properties/FunctionsParameters.java @@ -21,6 +21,8 @@ */ package com.influxdb.query.dsl.functions.properties; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; @@ -28,14 +30,15 @@ import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; +import java.util.Locale; import java.util.Map; -import java.util.StringJoiner; import java.util.function.Supplier; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.influxdb.query.dsl.Flux; +import com.influxdb.query.dsl.VariableAssignment; import com.influxdb.utils.Arguments; /** @@ -51,13 +54,25 @@ public final class FunctionsParameters { private static final String DEFAULT_DELIMITER = ":"; private static final String FUNCTION_DELIMITER = " => "; + private static final int DOUBLE_FRACTION_DIGITS = 340; - private Map properties = new LinkedHashMap<>(); + + private final Map> properties = new LinkedHashMap<>(); public static String escapeDoubleQuotes(final String val) { return val.replace("\"", "\\\""); } + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat( + "0.0", + DecimalFormatSymbols.getInstance(Locale.ENGLISH) + ); + + static { + // https://stackoverflow.com/a/25307973 + DECIMAL_FORMAT.setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS); + } + private FunctionsParameters() { } @@ -73,7 +88,27 @@ public static FunctionsParameters of() { * @return serialized value */ @Nullable - public static String serializeValue(@Nonnull final Object value) { + public static String serializeValue( + @Nonnull final Object value, + final boolean escapeStrings + ) { + if (value instanceof String) { + if (escapeStrings) { + return '"' + escapeDoubleQuotes((String) value) + '"'; + } + return (String) value; + } + if (value instanceof Integer || value instanceof Long) { + return value.toString(); + } + if (value instanceof Number) { + String s = value.toString(); + if (s.contains("E")) { + return DECIMAL_FORMAT.format(value); + } else { + return s; + } + } Object serializedValue = value; if (serializedValue.getClass().isArray()) { @@ -89,30 +124,27 @@ public static String serializeValue(@Nonnull final Object value) { return null; } - serializedValue = collection.stream() - .map(host -> "\"" + escapeDoubleQuotes(host.toString()) + "\"") + return collection.stream() + .map((v) -> serializeValue(v, true)) .collect(Collectors.joining(", ", "[", "]")); } if (serializedValue instanceof Map) { - - StringJoiner joiner = new StringJoiner(", ", "{", "}"); - - Map map = (Map) serializedValue; - //noinspection unchecked - map.keySet().forEach(key -> { - joiner.add(String.format("%s: \"%s\"", key, escapeDoubleQuotes(map.get(key).toString()))); - }); - - serializedValue = joiner; + return ((Map) serializedValue).entrySet().stream() + .map(entry -> entry.getKey() + ": " + serializeValue(entry.getValue(), true)) + .collect(Collectors.joining(", ", "{", "}")); } if (serializedValue instanceof Instant) { - serializedValue = DATE_FORMATTER.format((Instant) value); + return DATE_FORMATTER.format((Instant) value); } if (serializedValue instanceof Supplier) { - return serializeValue(((Supplier) serializedValue).get()); + return serializeValue(((Supplier) serializedValue).get(), escapeStrings); + } + + if (serializedValue instanceof VariableAssignment) { + return ((VariableAssignment) serializedValue).getVariableName(); } return serializedValue.toString(); @@ -141,7 +173,7 @@ public void putFunctionNamed(@Nonnull final String functionName, @Nonnull final Arguments.checkNonEmpty(functionName, "functionName"); Arguments.checkNonEmpty(namedProperty, "Named property"); - put(functionName, new NamedProperty(namedProperty, FUNCTION_DELIMITER)); + put(functionName, new NamedProperty<>(namedProperty, FUNCTION_DELIMITER)); } /** @@ -179,10 +211,10 @@ public void putFunctionValue(@Nonnull final String functionName, @Nullable final return; } - put(functionName, new Property() { + put(functionName, new Property() { @Nonnull @Override - public Object value(@Nonnull final Map namedProperties) { + public Object value(@Nonnull final Map namedProperties) { return function; } @@ -237,7 +269,7 @@ public Collection keys() { @Nullable public String get(@Nonnull final String key, @Nonnull final Map namedProperties) { - Property property = properties.get(key); + Property property = properties.get(key); if (property == null) { return null; } @@ -248,10 +280,10 @@ public String get(@Nonnull final String key, @Nonnull final Map } // array to collection - return serializeValue(value); + return serializeValue(value, false); } - private void put(@Nonnull final String name, @Nullable final Property property) { + private void put(@Nonnull final String name, @Nullable final Property property) { if (property == null) { return; @@ -263,7 +295,7 @@ private void put(@Nonnull final String name, @Nullable final Property property) @Nonnull public String getDelimiter(@Nonnull final String key) { - Property property = properties.get(key); + Property property = properties.get(key); if (property == null) { return DEFAULT_DELIMITER; } @@ -287,7 +319,7 @@ private interface Property { String delimiter(); } - private final class NamedProperty implements Property { + private static final class NamedProperty implements Property { private final String parameterName; private final String delimiter; @@ -325,7 +357,7 @@ public String delimiter() { } } - private final class StringProperty extends AbstractProperty { + private static final class StringProperty extends AbstractProperty { private final String value; @@ -345,7 +377,7 @@ public String value(@Nonnull final Map namedProperties) { } } - private abstract class AbstractProperty implements Property { + private abstract static class AbstractProperty implements Property { /** * @return For value property it is ": ", but for function it is "=>". diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/restriction/ColumnRestriction.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/restriction/ColumnRestriction.java index 045cbd35a69..05f27970f6d 100644 --- a/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/restriction/ColumnRestriction.java +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/functions/restriction/ColumnRestriction.java @@ -134,7 +134,7 @@ public Restrictions exists() { return new ExistsRestrictions(fieldName); } - private final class ExistsRestrictions extends Restrictions { + private static final class ExistsRestrictions extends Restrictions { private final String fieldName; public ExistsRestrictions(@Nonnull final String fieldName) { @@ -157,7 +157,7 @@ public Restrictions contains(@Nonnull final String[] set) { return new ContainsRestrictions(fieldName, set); } - private final class ContainsRestrictions extends Restrictions { + private static final class ContainsRestrictions extends Restrictions { private final String fieldName; private final String[] set; @@ -174,7 +174,7 @@ public String toString() { } } - private final class OperatorRestrictions extends Restrictions { + private static final class OperatorRestrictions extends Restrictions { private final String fieldName; private final Object fieldValue; private final String operator; @@ -194,7 +194,7 @@ public String toString() { if (fieldValue instanceof String) { value = "\"" + escapeDoubleQuotes((String) fieldValue) + "\""; } else { - value = FunctionsParameters.serializeValue(fieldValue); + value = FunctionsParameters.serializeValue(fieldValue, false); } return "r[\"" + escapeDoubleQuotes(fieldName) + "\"] " + operator + " " + value; diff --git a/flux-dsl/src/main/java/com/influxdb/query/dsl/utils/ImportUtils.java b/flux-dsl/src/main/java/com/influxdb/query/dsl/utils/ImportUtils.java new file mode 100644 index 00000000000..6fce950bcf4 --- /dev/null +++ b/flux-dsl/src/main/java/com/influxdb/query/dsl/utils/ImportUtils.java @@ -0,0 +1,46 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.utils; + +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.HasImports; + +public final class ImportUtils { + + private ImportUtils() { + } + + public static String getImportsString(@Nonnull final HasImports hasImports) { + String importString = ""; + Set collectedImports = hasImports.getImports(); + if (!collectedImports.isEmpty()) { + importString = collectedImports + .stream() + .map(s -> "import \"" + s + "\"") + .collect(Collectors.joining("\n", "", "\n")); + } + return importString; + } +} diff --git a/flux-dsl/src/test/java/com/influxdb/query/dsl/AbstractFunctionFluxTest.java b/flux-dsl/src/test/java/com/influxdb/query/dsl/AbstractFunctionFluxTest.java new file mode 100644 index 00000000000..3ff93c25f7a --- /dev/null +++ b/flux-dsl/src/test/java/com/influxdb/query/dsl/AbstractFunctionFluxTest.java @@ -0,0 +1,125 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl; + +import java.time.temporal.ChronoUnit; +import javax.annotation.Nonnull; + +import com.influxdb.query.dsl.functions.AbstractFunctionCallFlux; +import com.influxdb.query.dsl.functions.AbstractFunctionFlux; +import com.influxdb.query.dsl.functions.FreestyleExpression; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +@RunWith(JUnitPlatform.class) +public class AbstractFunctionFluxTest { + + @Test + void testCustomFunction() { + + MultByXFunction multByX = new MultByXFunction(); + + Expressions flux = new Expressions( + multByX, + Flux.from("telegraph") + .withPipedFunction(multByX) + .withX(42.) + .count() + ); + + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("multByX = (tables=<-, x) => ()\n" + + "\t|> map(fn: (r) => (r) => ({r with _value: r._value * x}))\n" + + "from(bucket:\"telegraph\")\n" + + "\t|> multByX(x:42.0)\n" + + "\t|> count()\n"); + } + + @Test + void testFreestyleFunction() { + MyCustomFreestyleFunction fun = new MyCustomFreestyleFunction(); + + Expressions flux = new Expressions( + fun, + fun.invoke().withN(0) + ); + + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace( + "import \"freestyle/import\"\n" + + "freestyle = (?n, ignore1 = 42, ignore2 = true, ignore3 = \"foo\", ignore4 = 1s) => n * n\n" + + "freestyle(n:0)\n"); + } + + public static class MultByXFunction extends AbstractFunctionFlux { + + public MultByXFunction() { + super("multByX", + new FreestyleExpression("tables") + .map("(r) => ({r with _value: r._value * x})"), + MultByXFunctionCall::new, + new Parameter("tables").withPipeForward(true), + new Parameter("x")); + } + + public static class MultByXFunctionCall extends AbstractFunctionCallFlux { + + public MultByXFunctionCall(@Nonnull String name) { + super(name); + } + + @Nonnull + public MultByXFunctionCall withX(final Number x) { + this.withPropertyValue("x", x); + return this; + } + } + } + + public static class MyCustomFreestyleFunction extends AbstractFunctionFlux { + + public MyCustomFreestyleFunction() { + super("freestyle", + new FreestyleExpression("n * n").addImport("freestyle/import"), + MyCustomFreestyleFunctionCall::new, + new Parameter("n").withOptional(true), + new Parameter("ignore1").withDefaultValue(42), + new Parameter("ignore2").withDefaultValue(true), + new Parameter("ignore3").withDefaultValue("foo"), + new Parameter("ignore4").withDefaultValue(1, ChronoUnit.SECONDS) + ); + } + + public static class MyCustomFreestyleFunctionCall extends AbstractFunctionCallFlux { + + public MyCustomFreestyleFunctionCall(@Nonnull final String name) { + super(name); + } + + @Nonnull + public MyCustomFreestyleFunctionCall withN(final Number b) { + this.withPropertyValue("n", b); + return this; + } + } + } +} diff --git a/flux-dsl/src/test/java/com/influxdb/query/dsl/ExpressionsTest.java b/flux-dsl/src/test/java/com/influxdb/query/dsl/ExpressionsTest.java new file mode 100644 index 00000000000..6123ab5545b --- /dev/null +++ b/flux-dsl/src/test/java/com/influxdb/query/dsl/ExpressionsTest.java @@ -0,0 +1,59 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class ExpressionsTest { + + @Test + void testMultipleExpressions() { + VariableAssignment a = Flux.from("test1").asVariable("a"); + VariableAssignment b = Flux.from("test2").asVariable("b"); + + String flux = new Expressions( + a, + b, + a.first().yield("firstA"), + b.first().yield("firstB"), + a.last().yield("lastA"), + b.last().yield("lastB")) + .toString(); + + Assertions.assertThat(flux).isEqualToIgnoringWhitespace("a = from(bucket:\"test1\")\n" + + "b = from(bucket:\"test2\")\n" + + "a\n" + + "\t|> first()\n" + + "\t|> yield(name:\"firstA\")\n" + + "b\n" + + "\t|> first()\n" + + "\t|> yield(name:\"firstB\")\n" + + "a\n" + + "\t|> last()\n" + + "\t|> yield(name:\"lastA\")\n" + + "b\n" + + "\t|> last()\n" + + "\t|> yield(name:\"lastB\")"); + } + +} diff --git a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/AggregateWindowTest.java b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/AggregateWindowTest.java index d7ec56e0c87..3f1255d297f 100644 --- a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/AggregateWindowTest.java +++ b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/AggregateWindowTest.java @@ -56,9 +56,10 @@ void aggregateWindowAllParameters() { .withColumn("_value") .withTimeSrc("_stop") .withTimeDst("_time") + .withOffset("1ms") .withCreateEmpty(true); - Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("from(bucket:\"telegraf\") |> aggregateWindow(every: 10s, fn: sum, column: \"_value\", timeSrc: \"_stop\", timeDst: \"_time\", createEmpty:true)"); + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("from(bucket:\"telegraf\") |> aggregateWindow(every: 10s, fn: sum, column: \"_value\", timeSrc: \"_stop\", timeDst: \"_time\", offset:1ms, createEmpty:true)"); } @Test diff --git a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/ArrayFromFluxTest.java b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/ArrayFromFluxTest.java new file mode 100644 index 00000000000..b1597eff672 --- /dev/null +++ b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/ArrayFromFluxTest.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.util.HashMap; +import java.util.Map; + +import com.influxdb.query.dsl.Flux; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +@RunWith(JUnitPlatform.class) +class ArrayFromFluxTest { + + @Test + void arrayFluxInstance() { + + Map record1 = new HashMap<>(); + record1.put("foo", "bar"); + record1.put("int", 42); + record1.put("double1", 6.23); + record1.put("double2", 6.); + record1.put("doubleScientific", 6.23087E8); + Flux flux = new ArrayFromFlux() + .withRow(record1); + + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("import \"array\"\n" + + "array.from(rows:[{double2: 6.0, foo: \"bar\", double1: 6.23, doubleScientific: 623087000.0, int: 42}])"); + } + + @Test + void arrayFlux() { + + Map record1 = new HashMap<>(); + record1.put("foo", "bar"); + record1.put("baz", 21.2); + Flux flux = Flux.arrayFrom(record1, record1); + + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("import \"array\"\n" + + "array.from(rows:[{foo: \"bar\", baz: 21.2}, {foo: \"bar\", baz: 21.2}])"); + } +} diff --git a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/InterpolateLinearTest.java b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/InterpolateLinearTest.java new file mode 100644 index 00000000000..4d06d88eab6 --- /dev/null +++ b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/InterpolateLinearTest.java @@ -0,0 +1,69 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.time.temporal.ChronoUnit; + +import com.influxdb.query.dsl.Flux; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +@RunWith(JUnitPlatform.class) +class InterpolateLinearTest { + + @Test + void interpolateLinear() { + + Flux flux = Flux + .from("telegraf") + .interpolateLinear(1L, ChronoUnit.MINUTES); + + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("import \"interpolate\"\n" + + "from(bucket:\"telegraf\") |> interpolate.linear(every:1m)"); + } + + @Test + void interpolateLinearByParameter() { + + Flux flux = Flux + .from("telegraf") + .interpolateLinear() + .withEvery(5L, ChronoUnit.MINUTES); + + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("import \"interpolate\"\n" + + "from(bucket:\"telegraf\") |> interpolate.linear(every:5m)"); + } + + @Test + void interpolateLinearByString() { + + Flux flux = Flux + .from("telegraf") + .interpolateLinear() + .withEvery("10m6h"); + + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("import \"interpolate\"\n" + + "from(bucket:\"telegraf\") |> interpolate.linear(every:10m6h)"); + } +} diff --git a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/JoinFluxText.java b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/JoinFluxTest.java similarity index 67% rename from flux-dsl/src/test/java/com/influxdb/query/dsl/functions/JoinFluxText.java rename to flux-dsl/src/test/java/com/influxdb/query/dsl/functions/JoinFluxTest.java index bfdd89dd538..37fc144dafa 100644 --- a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/JoinFluxText.java +++ b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/JoinFluxTest.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List; +import com.influxdb.query.dsl.Expressions; import com.influxdb.query.dsl.Flux; import com.influxdb.query.dsl.functions.restriction.Restrictions; @@ -37,7 +38,7 @@ * @author Jakub Bednar (bednar@github) (19/07/2018 12:59) */ @RunWith(JUnitPlatform.class) -class JoinFluxText { +class JoinFluxTest { @Test void join() { @@ -115,4 +116,71 @@ void methodByEnum() { Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace(expected); } -} \ No newline at end of file + + @Test + void join3TablesDirectly() { + + Flux b1 = Flux.from("bucket1") + .range(-30L, ChronoUnit.MINUTES); + Flux b2 = Flux.from("bucket2") + .range(-30L, ChronoUnit.MINUTES); + Flux b3 = Flux.from("bucket3") + .range(-30L, ChronoUnit.MINUTES); + + Flux join1 = Flux + .join("b1", b1, "b2", b2, "_time", "left"); + + Flux join2 = Flux + .join("j1", join1, "b3", b3, "_time", "left"); + + String expected = "b1 = from(bucket:\"bucket1\")\n" + + "\t|> range(start:-30m)\n" + + "b2 = from(bucket:\"bucket2\")\n" + + "\t|> range(start:-30m)\n" + + "j1 = join(tables:{b1:b1, b2:b2}, on:[\"_time\"], method:\"left\")\n" + + "b3 = from(bucket:\"bucket3\")\n" + + "\t|> range(start:-30m)\n" + + "join(tables:{j1:j1, b3:b3}, on:[\"_time\"], method:\"left\")"; + + Assertions.assertThat(join2.toString()).isEqualToIgnoringWhitespace(expected); + } + + @Test + void join3TablesViaVariables() { + + Flux b1 = Flux.from("bucket1") + .range(-30L, ChronoUnit.MINUTES) + .asVariable("table1"); + Flux b2 = Flux.from("bucket2") + .range(-30L, ChronoUnit.MINUTES) + .asVariable("table2"); + Flux b3 = Flux.from("bucket3") + .range(-30L, ChronoUnit.MINUTES) + .asVariable("table3"); + + Flux join1 = Flux + .join("b1", b1, "b2", b2, "_time", "left") + .asVariable("join1"); + + Flux join2 = Flux + .join("j1", join1, "b3", b3, "_time", "left"); + + Expressions expressions = new Expressions( + b1, + b2, + b3, + join1, + join2 + ); + String expected = "table1 = from(bucket:\"bucket1\")\n" + + "\t|> range(start:-30m)\n" + + "table2 = from(bucket:\"bucket2\")\n" + + "\t|> range(start:-30m)\n" + + "table3 = from(bucket:\"bucket3\")\n" + + "\t|> range(start:-30m)\n" + + "join1 = join(tables:{b1:table1, b2:table2}, on:[\"_time\"], method:\"left\")\n" + + "join(tables:{j1:join1, b3:table3}, on:[\"_time\"], method:\"left\")"; + + Assertions.assertThat(expressions.toString()).isEqualToIgnoringWhitespace(expected); + } +} diff --git a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/RangeFluxTest.java b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/RangeFluxTest.java index de496b1d91b..d55ec958b40 100644 --- a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/RangeFluxTest.java +++ b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/RangeFluxTest.java @@ -141,4 +141,28 @@ void startStopParameter() { Assertions.assertThat(flux.toString(parameters)).isEqualToIgnoringWhitespace(expected); } -} \ No newline at end of file + + @Test + void startTimestamp() { + + Flux flux = Flux + .from("telegraf") + .range(1567029600L); + + Assertions.assertThat(flux.toString()) + .isEqualToIgnoringWhitespace("from(bucket:\"telegraf\") |> range(start: 1567029600)"); + } + + @Test + void startStopTimestamp() { + + Flux flux = Flux + .from("telegraf") + .range(1567029600L, 1567030000L); + + String expected = "from(bucket:\"telegraf\") |> " + + "range(start:1567029600, stop:1567030000)"; + + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace(expected); + } +} diff --git a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/TruncateTimeColumnTest.java b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/TruncateTimeColumnTest.java new file mode 100644 index 00000000000..1c60b7de7b6 --- /dev/null +++ b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/TruncateTimeColumnTest.java @@ -0,0 +1,45 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import java.time.temporal.ChronoUnit; + +import com.influxdb.query.dsl.Flux; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + + +@RunWith(JUnitPlatform.class) +class TruncateTimeColumnTest { + + @Test + void truncateTimeColumn() { + + Flux flux = Flux + .from("telegraf") + .truncateTimeColumn(ChronoUnit.SECONDS); + + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("from(bucket:\"telegraf\") |> truncateTimeColumn(unit:1s)"); + } +} diff --git a/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/UnionFluxTest.java b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/UnionFluxTest.java new file mode 100644 index 00000000000..ea2824ee53f --- /dev/null +++ b/flux-dsl/src/test/java/com/influxdb/query/dsl/functions/UnionFluxTest.java @@ -0,0 +1,58 @@ +/* + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.influxdb.query.dsl.functions; + +import com.influxdb.query.dsl.Expressions; +import com.influxdb.query.dsl.Flux; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +/** + * @author Jakub Bednar (bednar@github) (29/06/2018 10:03) + */ +@RunWith(JUnitPlatform.class) +class UnionFluxTest { + + @Test + void union() { + + Flux flux = Flux.union( + Flux.from("telegraf1"), + Flux.from("telegraf2") + ); + Assertions.assertThat(flux.toString()).isEqualToIgnoringWhitespace("union(tables:[from(bucket:\"telegraf1\"),from(bucket:\"telegraf2\")])"); + } + + @Test + void unionVariables() { + + Flux v1 = Flux.from("telegraf1").asVariable("v1"); + Flux v2 = Flux.from("telegraf1").asVariable("v2"); + Flux flux = Flux.union(v1, v2); + Assertions.assertThat(new Expressions(v1, v2, flux).toString()).isEqualToIgnoringWhitespace( + "v1 = from(bucket:\"telegraf1\")\n" + + "v2 = from(bucket:\"telegraf1\")\n" + + "union(tables:[v1,v2])"); + } +}