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());
+    }
+  }
+}

Reply via email to