This is an automated email from the ASF dual-hosted git repository. leirui pushed a commit to branch research/M4-visualization in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 0dc6b40453f1e00913d9ec3e8f26101beb9fd2f1 Author: Lei Rui <[email protected]> AuthorDate: Wed Sep 27 20:58:18 2023 +0800 add LTTB --- server/pom.xml | 25 ++ .../db/query/udf/builtin/BuiltinFunction.java | 3 +- .../iotdb/db/query/udf/builtin/UDTFMinMax.java | 3 + .../iotdb/db/query/udf/builtin/UDTFSample.java | 194 +++++++++++++ .../iotdb/db/query/udf/util/NoNumberException.java | 30 ++ .../org/apache/iotdb/db/query/udf/util/Util.java | 309 +++++++++++++++++++++ .../apache/iotdb/db/integration/m4/MyTest6.java | 171 ++++++++++++ 7 files changed, 734 insertions(+), 1 deletion(-) diff --git a/server/pom.xml b/server/pom.xml index 5c4f833d826..775937ae168 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -29,12 +29,29 @@ </parent> <artifactId>iotdb-server</artifactId> <name>IoTDB Server</name> + <repositories> + <repository> + <id>jitpack.io</id> + <url>https://jitpack.io</url> + </repository> + </repositories> <properties> <iotdb.test.skip>false</iotdb.test.skip> <iotdb.it.skip>${iotdb.test.skip}</iotdb.it.skip> <iotdb.ut.skip>${iotdb.test.skip}</iotdb.ut.skip> </properties> <dependencies> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-math3</artifactId> + <version>3.5</version> + </dependency> + <!-- Sampling --> + <dependency> + <groupId>com.github.ggalmazor</groupId> + <artifactId>lt_downsampling_java8</artifactId> + <version>0.0.6</version> + </dependency> <dependency> <groupId>org.apache.iotdb</groupId> <artifactId>service-rpc</artifactId> @@ -261,6 +278,14 @@ </execution> </executions> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>9</source> + <target>9</target> + </configuration> + </plugin> </plugins> </build> <profiles> diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java index 216d912d393..ebde0642972 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java @@ -48,7 +48,8 @@ public enum BuiltinFunction { TOP_K("TOP_K", UDTFTopK.class), BOTTOM_K("BOTTOM_K", UDTFBottomK.class), M4("M4", UDTFM4MAC.class), - M4_TW("M4_TW", UDTFM4.class); + M4_TW("M4_TW", UDTFM4.class), + Sample("SAMPLE", UDTFSample.class); ; private final String functionName; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFMinMax.java b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFMinMax.java new file mode 100644 index 00000000000..da6dee8a979 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFMinMax.java @@ -0,0 +1,3 @@ +package org.apache.iotdb.db.query.udf.builtin; + +public class UDTFMinMax {} diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSample.java b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSample.java new file mode 100644 index 00000000000..e9bf4d5a009 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSample.java @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.query.udf.builtin; + +import org.apache.iotdb.db.query.udf.api.UDTF; +import org.apache.iotdb.db.query.udf.api.access.Row; +import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.db.query.udf.api.access.RowWindow; +import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.db.query.udf.util.NoNumberException; +import org.apache.iotdb.db.query.udf.util.Util; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import com.github.ggalmazor.ltdownsampling.LTThreeBuckets; +import com.github.ggalmazor.ltdownsampling.Point; +import org.apache.commons.lang3.tuple.Pair; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +/** This function samples data by pool sampling. */ +public class UDTFSample implements UDTF { + + enum Method { + ISOMETRIC, + RESERVOIR, + TRIANGLE + } + + private int k; // sample numbers + private Method method; + private static final String METHOD_RESERVOIR = "reservoir"; + // These variables occurs in pool sampling + private Pair<Long, Object>[] samples; // sampled data + private int num = 0; // number of points already sampled + private Random random; + private TSDataType dataType; + + @Override + public void validate(UDFParameterValidator validator) throws Exception { + validator + .validateInputSeriesNumber(1) + .validate( + k -> (int) k > 0, + "k should be a positive integer.", + validator.getParameters().getIntOrDefault("k", 1)) + .validate( + method -> + "isometric".equalsIgnoreCase((String) method) + || METHOD_RESERVOIR.equalsIgnoreCase((String) method) + || "triangle".equalsIgnoreCase((String) method), + "Illegal sampling method.", + validator.getParameters().getStringOrDefault("method", METHOD_RESERVOIR)); + } + + @Override + public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) + throws Exception { + this.k = parameters.getIntOrDefault("k", 1); + this.dataType = parameters.getDataType(0); + String methodIn = parameters.getStringOrDefault("method", METHOD_RESERVOIR); + if ("triangle".equalsIgnoreCase(methodIn)) { + this.method = Method.TRIANGLE; + } else if ("isometric".equalsIgnoreCase(methodIn)) { + this.method = Method.ISOMETRIC; + } else { + this.method = Method.RESERVOIR; + } + if (this.method == Method.ISOMETRIC || this.method == Method.TRIANGLE) { + configurations + .setAccessStrategy(new SlidingSizeWindowAccessStrategy(Integer.MAX_VALUE)) + .setOutputDataType(parameters.getDataType(0)); + } else { + configurations + .setAccessStrategy(new RowByRowAccessStrategy()) + .setOutputDataType(parameters.getDataType(0)); + this.samples = new Pair[this.k]; + this.random = new Random(); + } + } + + @Override + public void transform(Row row, PointCollector collector) throws Exception { + // pool sampling + int x; + if (this.num < this.k) { + x = this.num; + } else { + x = random.nextInt(num + 1); + } + if (x < this.k) { + Object v = Util.getValueAsObject(row); + Long t = row.getTime(); + this.samples[x] = Pair.of(t, v); + } + this.num++; + } + + @Override + public void transform(RowWindow rowWindow, PointCollector collector) throws Exception { + // equal-distance sampling + int n = rowWindow.windowSize(); + + if (this.k < n) { + if (this.method == Method.TRIANGLE) { + List<Point> input = new LinkedList<>(); + for (int i = 0; i < n; i++) { + Row row = rowWindow.getRow(i); + BigDecimal time = BigDecimal.valueOf(row.getTime()); + BigDecimal data = BigDecimal.valueOf(Util.getValueAsDouble(row)); + input.add(new Point(time, data)); + } + if (k > 2) { + // The first and last element will always be sampled so the buckets is k - 2 + List<Point> output = LTThreeBuckets.sorted(input, k - 2); + for (Point p : output) { + switch (dataType) { + case INT32: + Util.putValue(collector, dataType, p.getX().longValue(), p.getY().intValue()); + break; + case INT64: + Util.putValue(collector, dataType, p.getX().longValue(), p.getY().longValue()); + break; + case FLOAT: + Util.putValue(collector, dataType, p.getX().longValue(), p.getY().floatValue()); + break; + case DOUBLE: + Util.putValue(collector, dataType, p.getX().longValue(), p.getY().doubleValue()); + break; + default: + throw new NoNumberException(); + } + } + } else { // For corner case of k == 1 and k == 2 + Row row = rowWindow.getRow(0); // Put first element + Util.putValue(collector, dataType, row.getTime(), Util.getValueAsObject(row)); + if (k == 2) { + row = rowWindow.getRow(n - 1); // Put last element + Util.putValue(collector, dataType, row.getTime(), Util.getValueAsObject(row)); + } + } + } else { // Method.ISOMETRIC + for (long i = 0; i < this.k; i++) { + long j = Math.floorDiv(i * n, k); // avoid intermediate result overflows + Row row = rowWindow.getRow((int) j); + Util.putValue(collector, dataType, row.getTime(), Util.getValueAsObject(row)); + } + } + } else { // when k is larger than series length, output all points + RowIterator iterator = rowWindow.getRowIterator(); + while (iterator.hasNextRow()) { + Row row = iterator.next(); + Util.putValue(collector, dataType, row.getTime(), Util.getValueAsObject(row)); + } + } + } + + @Override + public void terminate(PointCollector pc) throws Exception { + if (samples != null) { // for pool sampling only + int m = Math.min(num, k); + Arrays.sort(samples, 0, m); + for (int i = 0; i < m; i++) { + Pair<Long, Object> p = samples[i]; + Util.putValue(pc, dataType, p.getLeft(), p.getRight()); + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/util/NoNumberException.java b/server/src/main/java/org/apache/iotdb/db/query/udf/util/NoNumberException.java new file mode 100644 index 00000000000..7fd4802e7e1 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/util/NoNumberException.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.query.udf.util; + +/** throw when the value got is not numeric. */ +public class NoNumberException extends Exception { + + @Override + public String toString() { + String s = "The value of the input time series is not numeric.\n"; + return s + super.toString(); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/util/Util.java b/server/src/main/java/org/apache/iotdb/db/query/udf/util/Util.java new file mode 100644 index 00000000000..0528681a42b --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/util/Util.java @@ -0,0 +1,309 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.query.udf.util; + +import org.apache.iotdb.db.query.udf.api.access.Row; +import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import org.apache.commons.math3.stat.descriptive.rank.Median; +import org.eclipse.collections.api.tuple.primitive.LongIntPair; +import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; + +import java.io.IOException; +import java.util.List; + +/** This class offers functions of getting and putting values from iotdb interface. */ +public class Util { + + private Util() { + throw new IllegalStateException("Utility class"); + } + + /** + * Get value from specific column from Row, and cast to double. Make sure never get null from Row. + * + * @param row data row + * @param index the column index + * @return value of specific column from Row + * @throws NoNumberException when getting a no number datatype + */ + public static double getValueAsDouble(Row row, int index) throws IOException, NoNumberException { + double ans = 0; + switch (row.getDataType(index)) { + case INT32: + ans = row.getInt(index); + break; + case INT64: + ans = row.getLong(index); + break; + case FLOAT: + ans = row.getFloat(index); + break; + case DOUBLE: + ans = row.getDouble(index); + break; + default: + throw new NoNumberException(); + } + return ans; + } + + /** + * Get value from 0th column from Row, and cast to double. Make sure never get null from Row. + * + * @param row data row + * @return value from 0th column from Row + * @throws NoNumberException when getting a no number datatype + */ + public static double getValueAsDouble(Row row) throws IOException, NoNumberException { + return getValueAsDouble(row, 0); + } + + /** + * Get value from 0th column from Row, and cast to Object. + * + * @param row data row + * @return value from 0th column from Row + */ + public static Object getValueAsObject(Row row) throws IOException { + Object ans = 0; + switch (row.getDataType(0)) { + case INT32: + ans = row.getInt(0); + break; + case INT64: + ans = row.getLong(0); + break; + case FLOAT: + ans = row.getFloat(0); + break; + case DOUBLE: + ans = row.getDouble(0); + break; + case BOOLEAN: + ans = row.getBoolean(0); + break; + case TEXT: + ans = row.getString(0); + break; + default: + break; + } + return ans; + } + + /** + * Add new data point to PointCollector. + * + * @param pc PointCollector + * @param type datatype + * @param t timestamp + * @param o value in Object type + */ + public static void putValue(PointCollector pc, TSDataType type, long t, Object o) + throws IOException { + switch (type) { + case INT32: + pc.putInt(t, (Integer) o); + break; + case INT64: + pc.putLong(t, (Long) o); + break; + case FLOAT: + pc.putFloat(t, (Float) o); + break; + case DOUBLE: + pc.putDouble(t, (Double) o); + break; + case BOOLEAN: + pc.putBoolean(t, (Boolean) o); + break; + default: + break; + } + } + + /** + * cast {@code ArrayList<Double>} to {@code double[]}. + * + * @param list ArrayList to cast + * @return cast result + */ + public static double[] toDoubleArray(List<Double> list) { + return list.stream().mapToDouble(Double::valueOf).toArray(); + } + + /** + * cast {@code ArrayList<Long>} to {@code long[]}. + * + * @param list ArrayList to cast + * @return cast result + */ + public static long[] toLongArray(List<Long> list) { + return list.stream().mapToLong(Long::valueOf).toArray(); + } + + /** + * calculate median absolute deviation of input series. 1.4826 is multiplied in order to achieve + * asymptotic normality. Note: 1.4826 = 1/qnorm(3/4) + * + * @param value input series + * @return median absolute deviation MAD + */ + public static double mad(double[] value) { + Median median = new Median(); + double mid = median.evaluate(value); + double[] d = new double[value.length]; + for (int i = 0; i < value.length; i++) { + d[i] = Math.abs(value[i] - mid); + } + return 1.4826 * median.evaluate(d); + } + + /** + * calculate 1-order difference of input series. + * + * @param origin original series + * @return 1-order difference + */ + public static double[] variation(double[] origin) { + int n = origin.length; + double[] variance = new double[n - 1]; + for (int i = 0; i < n - 1; i++) { + variance[i] = origin[i + 1] - origin[i]; + } + return variance; + } + + /** + * calculate 1-order difference of input series. + * + * @param origin original series + * @return 1-order difference + */ + public static double[] variation(long[] origin) { + int n = origin.length; + double[] variance = new double[n - 1]; + for (int i = 0; i < n - 1; i++) { + variance[i] = (origin[i + 1] - origin[i]); + } + return variance; + } + + /** + * calculate 1-order difference of input series. + * + * @param origin original series + * @return 1-order difference + */ + public static int[] variation(int[] origin) { + int n = origin.length; + int[] variance = new int[n - 1]; + for (int i = 0; i < n - 1; i++) { + variance[i] = origin[i + 1] - origin[i]; + } + return variance; + } + + /** + * calculate speed (1-order derivative with backward difference). + * + * @param origin value series + * @param time timestamp series + * @return speed series + */ + public static double[] speed(double[] origin, double[] time) { + int n = origin.length; + double[] speed = new double[n - 1]; + for (int i = 0; i < n - 1; i++) { + speed[i] = (origin[i + 1] - origin[i]) / (time[i + 1] - time[i]); + } + return speed; + } + + /** + * calculate speed (1-order derivative with backward difference). + * + * @param origin value series + * @param time timestamp series + * @return speed series + */ + public static double[] speed(double[] origin, long[] time) { + int n = origin.length; + double[] speed = new double[n - 1]; + for (int i = 0; i < n - 1; i++) { + speed[i] = (origin[i + 1] - origin[i]) / (time[i + 1] - time[i]); + } + return speed; + } + + /** + * computes mode. + * + * @param values input series + * @return mode + */ + public static long mode(long[] values) { + LongIntHashMap map = new LongIntHashMap(); + for (long v : values) { + map.addToValue(v, 1); + } + long key = 0; + int maxValue = 0; + for (LongIntPair p : map.keyValuesView()) { + if (p.getTwo() > maxValue) { + key = p.getOne(); + maxValue = p.getTwo(); + } + } + return key; + } + + /** + * cast String to timestamp. + * + * @param s input string + * @return timestamp + */ + public static long parseTime(String s) { + long unit = 0; + s = s.toLowerCase(); + s = s.replace(" ", ""); + if (s.endsWith("ms")) { + unit = 1; + s = s.substring(0, s.length() - 2); + } else if (s.endsWith("s")) { + unit = 1000; + s = s.substring(0, s.length() - 1); + } else if (s.endsWith("m")) { + unit = 60 * 1000L; + s = s.substring(0, s.length() - 1); + } else if (s.endsWith("h")) { + unit = 60 * 60 * 1000L; + s = s.substring(0, s.length() - 1); + } else if (s.endsWith("d")) { + unit = 24 * 60 * 60 * 1000L; + s = s.substring(0, s.length() - 1); + } + double v = Double.parseDouble(s); + return (long) (unit * v); + } +} diff --git a/server/src/test/java/org/apache/iotdb/db/integration/m4/MyTest6.java b/server/src/test/java/org/apache/iotdb/db/integration/m4/MyTest6.java new file mode 100644 index 00000000000..46de9698d4d --- /dev/null +++ b/server/src/test/java/org/apache/iotdb/db/integration/m4/MyTest6.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.integration.m4; + +import org.apache.iotdb.db.conf.IoTDBConfig; +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.engine.compaction.CompactionStrategy; +import org.apache.iotdb.db.utils.EnvironmentUtils; +import org.apache.iotdb.jdbc.Config; +import org.apache.iotdb.tsfile.common.conf.TSFileDescriptor; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Locale; + +import static org.junit.Assert.fail; + +public class MyTest6 { + + private static final String TIMESTAMP_STR = "Time"; + + private static String[] creationSqls = + new String[] { + "SET STORAGE GROUP TO root.vehicle.d0", + "CREATE TIMESERIES root.vehicle.d0.s0 WITH DATATYPE=DOUBLE,ENCODING=PLAIN", + }; + + private final String d0s0 = "root.vehicle.d0.s0"; + + private static final String insertTemplate = + "INSERT INTO root.vehicle.d0(timestamp,s0)" + " VALUES(%d,%d)"; + + private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig(); + private static boolean originalEnableCPV; + private static CompactionStrategy originalCompactionStrategy; + private static boolean originalUseChunkIndex; + + @Before + public void setUp() throws Exception { + TSFileDescriptor.getInstance().getConfig().setTimeEncoder("PLAIN"); + originalCompactionStrategy = config.getCompactionStrategy(); + config.setCompactionStrategy(CompactionStrategy.NO_COMPACTION); + + originalEnableCPV = config.isEnableCPV(); + // config.setEnableCPV(false); // MOC + config.setEnableCPV(true); // CPV + + originalUseChunkIndex = TSFileDescriptor.getInstance().getConfig().isUseTimeIndex(); + TSFileDescriptor.getInstance().getConfig().setUseTimeIndex(false); + + EnvironmentUtils.envSetUp(); + Class.forName(Config.JDBC_DRIVER_NAME); + config.setTimestampPrecision("ms"); + + TSFileDescriptor.getInstance().getConfig().setErrorParam(10); + } + + @After + public void tearDown() throws Exception { + EnvironmentUtils.cleanEnv(); + config.setCompactionStrategy(originalCompactionStrategy); + config.setEnableCPV(originalEnableCPV); + TSFileDescriptor.getInstance().getConfig().setUseTimeIndex(originalUseChunkIndex); + } + + @Test + public void testM4Function() { + // create timeseries + try (Connection connection = + DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root"); + Statement statement = connection.createStatement()) { + statement.execute("SET STORAGE GROUP TO root.m4"); + statement.execute("CREATE TIMESERIES root.m4.d1.s1 with datatype=double,encoding=PLAIN"); + statement.execute("CREATE TIMESERIES root.m4.d1.s2 with datatype=INT64,encoding=PLAIN"); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + + // insert data + String insertTemplate = "INSERT INTO root.m4.d1(timestamp,%s)" + " VALUES(%d,%d)"; + try (Connection connection = + DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root"); + Statement statement = connection.createStatement()) { + // "root.m4.d1.s1" data illustration: + // https://user-images.githubusercontent.com/33376433/151985070-73158010-8ba0-409d-a1c1-df69bad1aaee.png + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 1, 5)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 2, 15)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 20, 1)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 25, 8)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 54, 3)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 120, 8)); + statement.execute("FLUSH"); + + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 5, 10)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 8, 8)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 10, 30)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 20, 20)); + statement.execute("FLUSH"); + + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 27, 20)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 30, 40)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 35, 10)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 40, 20)); + statement.execute("FLUSH"); + + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 33, 9)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 45, 30)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 52, 8)); + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s1", 54, 18)); + statement.execute("FLUSH"); + + // "root.m4.d1.s2" data: constant value 1 + for (int i = 0; i < 100; i++) { + statement.execute(String.format(Locale.ENGLISH, insertTemplate, "s2", i, 1)); + } + statement.execute("FLUSH"); + } catch (Exception e) { + e.printStackTrace(); + } + + // query tests + test_sample(); + } + + private void test_sample() { + String[] res = new String[] {"1,5.0", "10,30.0", "33,9.0", "120,8.0"}; + + String sql = "select Sample(s1, 'method'='triangle','k'='4') from root.m4.d1"; + + try (Connection conn = + DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root"); + Statement statement = conn.createStatement()) { + ResultSet resultSet = statement.executeQuery(sql); + int count = 0; + while (resultSet.next()) { + String str = resultSet.getString(1) + "," + resultSet.getString(2); + Assert.assertEquals(res[count], str); + count++; + System.out.println(str); + } + Assert.assertEquals(res.length, count); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } +}
