You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by lt...@apache.org on 2020/02/03 13:27:43 UTC

[incubator-iotdb] 01/01: add IN operation

This is an automated email from the ASF dual-hosted git repository.

lta pushed a commit to branch add_in_operation
in repository https://gitbox.apache.org/repos/asf/incubator-iotdb.git

commit 6b6bfe49a56e1c42d14ae3b521716cc742a3b158
Author: lta <li...@163.com>
AuthorDate: Mon Feb 3 21:27:18 2020 +0800

    add IN operation
---
 .../org/apache/iotdb/db/qp/strategy/SqlBase.g4     |   6 +
 .../apache/iotdb/db/qp/constant/SQLConstant.java   |   3 +
 .../org/apache/iotdb/db/qp/logical/Operator.java   |   2 +-
 .../db/qp/logical/crud/BasicFunctionOperator.java  |  32 +---
 .../iotdb/db/qp/logical/crud/FilterOperator.java   |  28 +--
 .../iotdb/db/qp/logical/crud/FunctionOperator.java |  17 ++
 .../iotdb/db/qp/logical/crud/InOperator.java       | 213 +++++++++++++++++++++
 .../iotdb/db/qp/strategy/LogicalGenerator.java     |  68 +++++--
 .../iotdb/db/qp/strategy/PhysicalGenerator.java    |   4 +-
 .../qp/strategy/optimizer/ConcatPathOptimizer.java |   6 +-
 .../optimizer/MergeSingleFilterOptimizer.java      |  14 +-
 .../qp/strategy/optimizer/RemoveNotOptimizer.java  |   3 +-
 .../iotdb/db/integration/IoTDBQueryDemoIT.java     |  93 ++++++++-
 .../apache/iotdb/db/qp/plan/PhysicalPlanTest.java  |  50 +++++
 .../iotdb/tsfile/read/filter/TimeFilter.java       |  13 ++
 .../iotdb/tsfile/read/filter/ValueFilter.java      |  13 ++
 .../tsfile/read/filter/factory/FilterFactory.java  |   2 -
 .../read/filter/factory/FilterSerializeId.java     |   2 +-
 .../iotdb/tsfile/read/filter/operator/Eq.java      |   4 +-
 .../read/filter/operator/{Eq.java => In.java}      |  90 +++++----
 20 files changed, 546 insertions(+), 117 deletions(-)

diff --git a/server/src/main/antlr4/org/apache/iotdb/db/qp/strategy/SqlBase.g4 b/server/src/main/antlr4/org/apache/iotdb/db/qp/strategy/SqlBase.g4
index 4e2e47f..ab0e68e 100644
--- a/server/src/main/antlr4/org/apache/iotdb/db/qp/strategy/SqlBase.g4
+++ b/server/src/main/antlr4/org/apache/iotdb/db/qp/strategy/SqlBase.g4
@@ -137,9 +137,13 @@ andExpression
 
 predicate
     : (suffixPath | prefixPath) comparisonOperator constant
+    | (suffixPath | prefixPath) inClause
     | OPERATOR_NOT? LR_BRACKET orExpression RR_BRACKET
     ;
 
+inClause
+    : OPERATOR_NOT? OPERATOR_IN LR_BRACKET constant (COMMA constant)* RR_BRACKET
+    ;
 
 fromClause
     : FROM prefixPath (COMMA prefixPath)*
@@ -746,6 +750,8 @@ OPERATOR_LTE : '<=';
 
 OPERATOR_NEQ : '!=' | '<>';
 
+OPERATOR_IN : 'IN' | 'in' | 'In' | 'iN';
+
 OPERATOR_AND
     : A N D
     | '&'
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java b/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
index 9a89fcc..b62287d 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
@@ -78,6 +78,7 @@ public class SQLConstant {
   public static final int GREATERTHANOREQUALTO = SqlBaseLexer.OPERATOR_GTE;
   public static final int GREATERTHAN = SqlBaseLexer.OPERATOR_GT;
   public static final int EQUAL_NS = SqlBaseLexer.OPERATOR_NEQ;
+  public static final int IN = SqlBaseLexer.OPERATOR_IN;
 
   public static final int TOK_SELECT = 21;
   public static final int TOK_FROM = 22;
@@ -165,6 +166,7 @@ public class SQLConstant {
     tokenNames.put(LESSTHAN, "lessthan");
     tokenNames.put(GREATERTHANOREQUALTO, "greaterthan_or_equalto");
     tokenNames.put(GREATERTHAN, "greaterthan");
+    tokenNames.put(IN, "in");
 
     tokenNames.put(TOK_SELECT, "TOK_SELECT");
     tokenNames.put(TOK_FROM, "TOK_FROM");
@@ -207,6 +209,7 @@ public class SQLConstant {
 
   static {
     reverseWords.put(KW_AND, KW_OR);
+    reverseWords.put(IN, IN);
     reverseWords.put(KW_OR, KW_AND);
     reverseWords.put(EQUAL, NOTEQUAL);
     reverseWords.put(NOTEQUAL, EQUAL);
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/Operator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/Operator.java
index 50d87bc..2dfd9ad 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/logical/Operator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/Operator.java
@@ -67,7 +67,7 @@ public abstract class Operator {
    */
   public enum OperatorType {
     SFW, JOIN, UNION, FILTER, GROUPBY, ORDERBY, LIMIT, SELECT, SEQTABLESCAN, HASHTABLESCAN,
-    MERGEJOIN, FILEREAD, NULL, TABLESCAN, UPDATE, INSERT, BATCHINSERT, DELETE, BASIC_FUNC, QUERY, MERGEQUERY,
+    MERGEJOIN, FILEREAD, NULL, TABLESCAN, UPDATE, INSERT, BATCHINSERT, DELETE, BASIC_FUNC, IN, QUERY, MERGEQUERY,
     AGGREGATION, AUTHOR, FROM, FUNC, LOADDATA, METADATA, PROPERTY, INDEX, INDEXQUERY, FILL,
     SET_STORAGE_GROUP, CREATE_TIMESERIES, DELETE_TIMESERIES, CREATE_USER, DELETE_USER, MODIFY_PASSWORD,
     GRANT_USER_PRIVILEGE, REVOKE_USER_PRIVILEGE, GRANT_USER_ROLE, REVOKE_USER_ROLE, CREATE_ROLE,
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/BasicFunctionOperator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/BasicFunctionOperator.java
index 2fce08a..fe1d05f 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/BasicFunctionOperator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/BasicFunctionOperator.java
@@ -39,7 +39,6 @@ import org.slf4j.LoggerFactory;
  */
 public class BasicFunctionOperator extends FunctionOperator {
 
-  protected Path path;
   protected String value;
   private Logger logger = LoggerFactory.getLogger(BasicFunctionOperator.class);
   private BasicOperatorType funcToken;
@@ -57,44 +56,19 @@ public class BasicFunctionOperator extends FunctionOperator {
     super(tokenIntType);
     operatorType = Operator.OperatorType.BASIC_FUNC;
     funcToken = BasicOperatorType.getBasicOpBySymbol(tokenIntType);
-    this.path = this.singlePath = path;
+    this.path = path;
     this.value = value;
     isLeaf = true;
     isSingle = true;
   }
 
-  /**
-   * get path.
-   *
-   * @return path
-   */
-  public String getPath() {
-    return path.toString();
-  }
-
   public String getValue() {
     return value;
   }
 
-  /**
-   * set reversed token.
-   *
-   * @throws LogicalOperatorException Logical Operator Exception
-   */
-  public void setReversedTokenIntType() throws LogicalOperatorException {
-    int intType = SQLConstant.reverseWords.get(tokenIntType);
-    setTokenIntType(intType);
-    funcToken = BasicOperatorType.getBasicOpBySymbol(intType);
-  }
-
-  @Override
-  public Path getSinglePath() {
-    return singlePath;
-  }
-
   @Override
-  public void setSinglePath(Path singlePath) {
-    this.path = this.singlePath = singlePath;
+  protected void reverseFunc(){
+    funcToken = BasicOperatorType.getBasicOpBySymbol(getTokenIntType());
   }
 
   @Override
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/FilterOperator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/FilterOperator.java
index b1f1903..a36522a 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/FilterOperator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/FilterOperator.java
@@ -55,7 +55,7 @@ public class FilterOperator extends Operator implements Comparable<FilterOperato
   // isSingle being true means all recursive children of this filter belong to one seriesPath.
   protected boolean isSingle = false;
   // if isSingle = false, singlePath must be null
-  protected Path singlePath = null;
+  protected Path path = null;
 
   public FilterOperator(int tokenType) {
     super(tokenType);
@@ -94,12 +94,12 @@ public class FilterOperator extends Operator implements Comparable<FilterOperato
     this.isSingle = b;
   }
 
-  public Path getSinglePath() {
-    return singlePath;
+  public Path getPath() {
+    return path;
   }
 
-  public void setSinglePath(Path path) {
-    this.singlePath = path;
+  public void setPath(Path path) {
+    this.path = path;
   }
 
   public boolean addChildOperator(FilterOperator op) {
@@ -114,7 +114,7 @@ public class FilterOperator extends Operator implements Comparable<FilterOperato
    * @return QueryFilter in TsFile
    */
   public IExpression transformToExpression(IQueryProcessExecutor executor)
-      throws QueryProcessException, LogicalOperatorException {
+      throws QueryProcessException {
     if (isSingle) {
       Pair<IUnaryExpression, String> ret = transformToSingleQueryFilter(executor);
       return ret.left;
@@ -192,16 +192,16 @@ public class FilterOperator extends Operator implements Comparable<FilterOperato
    */
   @Override
   public int compareTo(FilterOperator fil) {
-    if (singlePath == null && fil.singlePath == null) {
+    if (path == null && fil.path == null) {
       return 0;
     }
-    if (singlePath == null) {
+    if (path == null) {
       return 1;
     }
-    if (fil.singlePath == null) {
+    if (fil.path == null) {
       return -1;
     }
-    return fil.singlePath.toString().compareTo(singlePath.toString());
+    return fil.path.toString().compareTo(path.toString());
   }
 
   @Override
@@ -239,7 +239,7 @@ public class FilterOperator extends Operator implements Comparable<FilterOperato
     }
     sc.addTail(this.tokenName);
     if (isSingle) {
-      sc.addTail("[single:", getSinglePath().toString(), "]");
+      sc.addTail("[single:", getPath().toString(), "]");
     }
     sc.addTail("\n");
     for (FilterOperator filter : childOperators) {
@@ -253,7 +253,7 @@ public class FilterOperator extends Operator implements Comparable<FilterOperato
     StringContainer sc = new StringContainer();
     sc.addTail("[", this.tokenName);
     if (isSingle) {
-      sc.addTail("[single:", getSinglePath().toString(), "]");
+      sc.addTail("[single:", getPath().toString(), "]");
     }
     sc.addTail(" ");
     for (FilterOperator filter : childOperators) {
@@ -269,8 +269,8 @@ public class FilterOperator extends Operator implements Comparable<FilterOperato
     ret.tokenSymbol = tokenSymbol;
     ret.isLeaf = isLeaf;
     ret.isSingle = isSingle;
-    if (singlePath != null) {
-      ret.singlePath = singlePath.clone();
+    if (path != null) {
+      ret.path = path.clone();
     }
     for (FilterOperator filterOperator : this.childOperators) {
       ret.addChildOperator(filterOperator.clone());
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/FunctionOperator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/FunctionOperator.java
index 64039bd..87b492b 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/FunctionOperator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/FunctionOperator.java
@@ -18,6 +18,8 @@
  */
 package org.apache.iotdb.db.qp.logical.crud;
 
+import org.apache.iotdb.db.exception.query.LogicalOperatorException;
+import org.apache.iotdb.db.qp.constant.SQLConstant;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,6 +39,21 @@ public class FunctionOperator extends FilterOperator {
     operatorType = OperatorType.FUNC;
   }
 
+  /**
+   * set reversed token.
+   *
+   * @throws LogicalOperatorException Logical Operator Exception
+   */
+  public void setReversedTokenIntType() throws LogicalOperatorException {
+    int intType = SQLConstant.reverseWords.get(tokenIntType);
+    setTokenIntType(intType);
+    reverseFunc();
+  }
+
+  protected void reverseFunc(){
+
+  }
+
   @Override
   public boolean addChildOperator(FilterOperator op) {
     logger.error("cannot add child to leaf FilterOperator, now it's FunctionOperator");
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/InOperator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/InOperator.java
new file mode 100644
index 0000000..f86c719
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/InOperator.java
@@ -0,0 +1,213 @@
+/*
+ * 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.qp.logical.crud;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import org.apache.iotdb.db.exception.path.PathException;
+import org.apache.iotdb.db.exception.query.LogicalOperatorException;
+import org.apache.iotdb.db.exception.runtime.SQLParserException;
+import org.apache.iotdb.db.qp.executor.IQueryProcessExecutor;
+import org.apache.iotdb.db.qp.logical.Operator;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.read.common.Path;
+import org.apache.iotdb.tsfile.read.expression.IUnaryExpression;
+import org.apache.iotdb.tsfile.read.expression.impl.GlobalTimeExpression;
+import org.apache.iotdb.tsfile.read.expression.impl.SingleSeriesExpression;
+import org.apache.iotdb.tsfile.read.filter.TimeFilter;
+import org.apache.iotdb.tsfile.read.filter.ValueFilter;
+import org.apache.iotdb.tsfile.read.filter.basic.Filter;
+import org.apache.iotdb.tsfile.utils.Binary;
+import org.apache.iotdb.tsfile.utils.Pair;
+import org.apache.iotdb.tsfile.utils.StringContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * operator 'in' 'not in'
+ */
+public class InOperator extends FunctionOperator {
+
+  private boolean not;
+  protected Set<String> values;
+  private String valueToString;
+  private Logger logger = LoggerFactory.getLogger(InOperator.class);
+
+  /**
+   * In Operator Constructor.
+   *
+   * @param tokenIntType token in Int Type
+   * @param path path
+   * @param value value
+   * @throws LogicalOperatorException Logical Operator Exception
+   */
+  public InOperator(int tokenIntType, Path path, boolean not, Set<String> values)
+      throws SQLParserException {
+    super(tokenIntType);
+    operatorType = Operator.OperatorType.IN;
+    this.path = path;
+    this.values = values;
+    this.not = not;
+    List<String> valuesList = new ArrayList<>(values);
+    Collections.sort(valuesList);
+    this.valueToString = valuesList.toString();
+    isLeaf = true;
+    isSingle = true;
+  }
+
+  public Set<String> getValues() {
+    return values;
+  }
+
+  @Override
+  protected void reverseFunc(){
+    not = !not;
+  }
+
+  @Override
+  protected Pair<IUnaryExpression, String> transformToSingleQueryFilter(
+      IQueryProcessExecutor executor) throws LogicalOperatorException, PathException {
+    TSDataType type = executor.getSeriesType(path);
+    if (type == null) {
+      throw new PathException(
+          "given seriesPath:{" + path.getFullPath() + "} don't exist in metadata");
+    }
+    IUnaryExpression ret;
+
+    switch (type) {
+      case INT32:
+        Set<Integer> integerValues = new HashSet<>();
+        for (String val : values) {
+          integerValues.add(Integer.valueOf(val));
+        }
+        ret = In.getUnaryExpression(path, integerValues, not, valueToString);
+        break;
+      case INT64:
+        Set<Long> longValues = new HashSet<>();
+        for (String val : values) {
+          longValues.add(Long.valueOf(val));
+        }
+        ret = In.getUnaryExpression(path, longValues, not, valueToString);
+        break;
+      case BOOLEAN:
+        Set<Boolean> booleanValues = new HashSet<>();
+        for (String val : values) {
+          booleanValues.add(Boolean.valueOf(val));
+        }
+        ret = In.getUnaryExpression(path, booleanValues, not, valueToString);
+        break;
+      case FLOAT:
+        Set<Float> floatValues = new HashSet<>();
+        for (String val : values) {
+          floatValues.add(Float.parseFloat(val));
+        }
+        ret = In.getUnaryExpression(path, floatValues, not, valueToString);
+        break;
+      case DOUBLE:
+        Set<Double> doubleValues = new HashSet<>();
+        for (String val : values) {
+          doubleValues.add(Double.parseDouble(val));
+        }
+        ret = In.getUnaryExpression(path, doubleValues, not, valueToString);
+        break;
+      case TEXT:
+        Set<Binary> binaryValues = new HashSet<>();
+        for (String val : values) {
+          binaryValues.add(
+              (val.startsWith("'") && val.endsWith("'")) || (val.startsWith("\"") && val
+                  .endsWith("\"")) ? new Binary(val.substring(1, val.length() - 1))
+                  : new Binary(val));
+        }
+        ret = In.getUnaryExpression(path, binaryValues, not, valueToString);
+        break;
+      default:
+        throw new LogicalOperatorException(type.toString(), "");
+    }
+
+    return new Pair<>(ret, path.getFullPath());
+  }
+
+  @Override
+  public String showTree(int spaceNum) {
+    StringContainer sc = new StringContainer();
+    for (int i = 0; i < spaceNum; i++) {
+      sc.addTail("  ");
+    }
+    sc.addTail(path.toString(), this.tokenSymbol, not, valueToString, ", single\n");
+    return sc.toString();
+  }
+
+  @Override
+  public InOperator clone() {
+    InOperator ret;
+    try {
+      ret = new InOperator(this.tokenIntType, path.clone(), not, new HashSet<>(values));
+    } catch (SQLParserException e) {
+      logger.error("error clone:", e);
+      return null;
+    }
+    ret.tokenSymbol = tokenSymbol;
+    ret.isLeaf = isLeaf;
+    ret.isSingle = isSingle;
+    return ret;
+  }
+
+  @Override
+  public String toString() {
+    return "[" + path.getFullPath() + tokenSymbol + not + valueToString + "]";
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    InOperator that = (InOperator) o;
+    return Objects.equals(path, that.path) && Objects.equals(valueToString, that.valueToString)
+        && not == that.not;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), path, not, valueToString);
+  }
+
+  private static class In {
+
+    public static <T extends Comparable<T>> IUnaryExpression getUnaryExpression(Path path,
+        Set<T> values, boolean not, String valueToString) {
+      if (path.equals("time")) {
+        return new GlobalTimeExpression(TimeFilter.in((Set<Long>) values, not, valueToString));
+      } else {
+        return new SingleSeriesExpression(path, ValueFilter.in(values, not, valueToString));
+      }
+    }
+
+    public <T extends Comparable<T>> Filter getValueFilter(T value) {
+      return ValueFilter.notEq(value);
+    }
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
index 509912e..349d2ed 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java
@@ -23,8 +23,10 @@ import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.EnumMap;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import org.antlr.v4.runtime.tree.TerminalNode;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.exception.runtime.SQLParserException;
@@ -35,6 +37,7 @@ import org.apache.iotdb.db.qp.logical.crud.BasicFunctionOperator;
 import org.apache.iotdb.db.qp.logical.crud.DeleteDataOperator;
 import org.apache.iotdb.db.qp.logical.crud.FilterOperator;
 import org.apache.iotdb.db.qp.logical.crud.FromOperator;
+import org.apache.iotdb.db.qp.logical.crud.InOperator;
 import org.apache.iotdb.db.qp.logical.crud.InsertOperator;
 import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
 import org.apache.iotdb.db.qp.logical.crud.SelectOperator;
@@ -88,6 +91,7 @@ import org.apache.iotdb.db.qp.strategy.SqlBaseParser.GrantUserContext;
 import org.apache.iotdb.db.qp.strategy.SqlBaseParser.GrantWatermarkEmbeddingContext;
 import org.apache.iotdb.db.qp.strategy.SqlBaseParser.GroupByClauseContext;
 import org.apache.iotdb.db.qp.strategy.SqlBaseParser.GroupByDeviceClauseContext;
+import org.apache.iotdb.db.qp.strategy.SqlBaseParser.InClauseContext;
 import org.apache.iotdb.db.qp.strategy.SqlBaseParser.InsertColumnSpecContext;
 import org.apache.iotdb.db.qp.strategy.SqlBaseParser.InsertStatementContext;
 import org.apache.iotdb.db.qp.strategy.SqlBaseParser.InsertValuesSpecContext;
@@ -181,7 +185,7 @@ public class LogicalGenerator extends SqlBaseBaseListener {
   @Override
   public void enterCountTimeseries(CountTimeseriesContext ctx) {
     super.enterCountTimeseries(ctx);
-    if(ctx.INT() != null) {
+    if (ctx.INT() != null) {
       initializedOperator = new CountOperator(SQLConstant.TOK_COUNT_NODE_TIMESERIES,
           parsePrefixPath(ctx.prefixPath()), Integer.parseInt(ctx.INT().getText()));
     } else {
@@ -200,20 +204,24 @@ public class LogicalGenerator extends SqlBaseBaseListener {
   @Override
   public void enterShowDevices(ShowDevicesContext ctx) {
     super.enterShowDevices(ctx);
-    if(ctx.prefixPath() != null) {
-      initializedOperator = new ShowDevicesOperator(SQLConstant.TOK_DEVICES, parsePrefixPath(ctx.prefixPath()));
+    if (ctx.prefixPath() != null) {
+      initializedOperator = new ShowDevicesOperator(SQLConstant.TOK_DEVICES,
+          parsePrefixPath(ctx.prefixPath()));
     } else {
-      initializedOperator = new ShowDevicesOperator(SQLConstant.TOK_DEVICES, new Path(SQLConstant.ROOT));
+      initializedOperator = new ShowDevicesOperator(SQLConstant.TOK_DEVICES,
+          new Path(SQLConstant.ROOT));
     }
   }
 
   @Override
   public void enterShowChildPaths(ShowChildPathsContext ctx) {
     super.enterShowChildPaths(ctx);
-    if(ctx.prefixPath()!= null) {
-      initializedOperator = new ShowChildPathsOperator(SQLConstant.TOK_CHILD_PATHS, parsePrefixPath(ctx.prefixPath()));
+    if (ctx.prefixPath() != null) {
+      initializedOperator = new ShowChildPathsOperator(SQLConstant.TOK_CHILD_PATHS,
+          parsePrefixPath(ctx.prefixPath()));
     } else {
-      initializedOperator = new ShowChildPathsOperator(SQLConstant.TOK_CHILD_PATHS, new Path(SQLConstant.ROOT));
+      initializedOperator = new ShowChildPathsOperator(SQLConstant.TOK_CHILD_PATHS,
+          new Path(SQLConstant.ROOT));
     }
   }
 
@@ -284,11 +292,12 @@ public class LogicalGenerator extends SqlBaseBaseListener {
   @Override
   public void enterShowTimeseries(ShowTimeseriesContext ctx) {
     super.enterShowTimeseries(ctx);
-    if(ctx.prefixPath() != null) {
+    if (ctx.prefixPath() != null) {
       initializedOperator = new ShowTimeSeriesOperator(SQLConstant.TOK_TIMESERIES,
           parsePrefixPath(ctx.prefixPath()));
     } else {
-      initializedOperator = new ShowTimeSeriesOperator(SQLConstant.TOK_TIMESERIES, new Path("root"));
+      initializedOperator = new ShowTimeSeriesOperator(SQLConstant.TOK_TIMESERIES,
+          new Path("root"));
     }
   }
 
@@ -384,7 +393,7 @@ public class LogicalGenerator extends SqlBaseBaseListener {
     super.enterAlterUser(ctx);
     AuthorOperator authorOperator = new AuthorOperator(SQLConstant.TOK_AUTHOR_UPDATE_USER,
         AuthorOperator.AuthorType.UPDATE_USER);
-    if(ctx.ID() != null) {
+    if (ctx.ID() != null) {
       authorOperator.setUserName(ctx.ID().getText());
     } else {
       authorOperator.setUserName(ctx.ROOT().getText());
@@ -1167,28 +1176,49 @@ public class LogicalGenerator extends SqlBaseBaseListener {
       return parseOrExpression(ctx.orExpression());
     } else {
       Path path = null;
-      BasicFunctionOperator basic = null;
       if (ctx.prefixPath() != null) {
         path = parsePrefixPath(ctx.prefixPath());
       }
       if (ctx.suffixPath() != null) {
         path = parseSuffixPath(ctx.suffixPath());
       }
-      if (ctx.constant().dateExpression() != null) {
+      if (ctx.inClause() != null) {
+        return parseInOperator(ctx.inClause(), path);
+      } else {
+        return parseBasicFunctionOperator(ctx, path);
+      }
+    }
+  }
+
+  private FilterOperator parseInOperator(InClauseContext ctx, Path path) {
+    Set<String> values = new HashSet<>();
+    boolean not = ctx.OPERATOR_NOT() != null;
+    for (ConstantContext constant : ctx.constant()) {
+      if (constant.dateExpression() != null) {
         if (!path.equals(SQLConstant.RESERVED_TIME)) {
           throw new SQLParserException(path.toString(), "Date can only be used to time");
         }
-        basic = new BasicFunctionOperator(ctx.comparisonOperator().type.getType(), path,
-            Long.toString(parseDateExpression(ctx.constant().dateExpression())));
+        values.add(Long.toString(parseDateExpression(constant.dateExpression())));
       } else {
-        basic = new BasicFunctionOperator(ctx.comparisonOperator().type.getType(), path,
-            ctx.constant().getText());
+        values.add(constant.getText());
       }
-      if (!isNotWhereClause && !isAndWhereClause && !isOrWhereClause) {
-        return basic;
+    }
+    return new InOperator(ctx.OPERATOR_IN().getSymbol().getType(), path, not, values);
+  }
+
+  private FilterOperator parseBasicFunctionOperator(PredicateContext ctx, Path path) {
+    BasicFunctionOperator basic;
+    if (ctx.constant().dateExpression() != null) {
+      if (!path.equals(SQLConstant.RESERVED_TIME)) {
+        throw new SQLParserException(path.toString(), "Date can only be used to time");
       }
-      return basic;
+      basic = new BasicFunctionOperator(ctx.comparisonOperator().type.getType(), path,
+          Long.toString(parseDateExpression(ctx.constant().dateExpression())));
+    } else {
+      basic = new BasicFunctionOperator(ctx.comparisonOperator().type.getType(), path,
+          ctx.constant().getText());
     }
+    return basic;
   }
 
   private Path parseSuffixPath(SuffixPathContext ctx) {
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
index 8b4d8f6..8b11890 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
@@ -392,7 +392,7 @@ public class PhysicalGenerator {
       return operator;
     }
     BasicFunctionOperator basicOperator = (BasicFunctionOperator) operator;
-    Path filterPath = basicOperator.getSinglePath();
+    Path filterPath = basicOperator.getPath();
 
     // do nothing in the cases of "where time > 5" or "where root.d1.s1 > 5"
     if (SQLConstant.isReservedPath(filterPath) || filterPath.startWith(SQLConstant.ROOT)) {
@@ -400,7 +400,7 @@ public class PhysicalGenerator {
     }
 
     Path concatPath = filterPath.addPrefixPath(filterPath, prefix);
-    basicOperator.setSinglePath(concatPath);
+    basicOperator.setPath(concatPath);
 
     return basicOperator;
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
index 81e3041..6d5e7f9 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java
@@ -216,8 +216,8 @@ public class ConcatPathOptimizer implements ILogicalOptimizer {
       operator.setChildren(newFilterList);
       return operator;
     }
-    BasicFunctionOperator basicOperator = (BasicFunctionOperator) operator;
-    Path filterPath = basicOperator.getSinglePath();
+    FunctionOperator functionOperator = (FunctionOperator) operator;
+    Path filterPath = functionOperator.getPath();
     // do nothing in the cases of "where time > 5" or "where root.d1.s1 > 5"
     if (SQLConstant.isReservedPath(filterPath) || filterPath.startWith(SQLConstant.ROOT)) {
       return operator;
@@ -228,7 +228,7 @@ public class ConcatPathOptimizer implements ILogicalOptimizer {
     if (noStarPaths.size() == 1) {
       // Transform "select s1 from root.car.* where s1 > 10" to
       // "select s1 from root.car.* where root.car.*.s1 > 10"
-      basicOperator.setSinglePath(noStarPaths.get(0));
+      functionOperator.setPath(noStarPaths.get(0));
       return operator;
     } else {
       // Transform "select s1 from root.car.d1, root.car.d2 where s1 > 10" to
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/MergeSingleFilterOptimizer.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/MergeSingleFilterOptimizer.java
index d2e3046..298eaa9 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/MergeSingleFilterOptimizer.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/MergeSingleFilterOptimizer.java
@@ -57,7 +57,7 @@ public class MergeSingleFilterOptimizer implements IFilterOptimizer {
    */
   private Path mergeSamePathFilter(FilterOperator filter) throws LogicalOptimizeException {
     if (filter.isLeaf()) {
-      return filter.getSinglePath();
+      return filter.getPath();
     }
     List<FilterOperator> children = filter.getChildren();
     checkInnerFilterLen(children);
@@ -73,14 +73,14 @@ public class MergeSingleFilterOptimizer implements IFilterOptimizer {
     }
     if (childPath != null) {
       filter.setIsSingle(true);
-      filter.setSinglePath(childPath);
+      filter.setPath(childPath);
       return childPath;
     }
 
     // sort paths of BasicFunction by their single seriesPath. We don't sort children on non-leaf
     // layer.
     if (!children.isEmpty() && allIsBasic(children)) {
-      children.sort(Comparator.comparing(o -> o.getSinglePath().getFullPath()));
+      children.sort(Comparator.comparing(o -> o.getPath().getFullPath()));
     }
     List<FilterOperator> ret = new ArrayList<>();
     int firstNonSingleIndex = mergeSingleFilters(ret, filter);
@@ -96,7 +96,7 @@ public class MergeSingleFilterOptimizer implements IFilterOptimizer {
     Path childPath = null;
     int firstNonSingleIndex;
     for (firstNonSingleIndex = 0; firstNonSingleIndex < children.size(); firstNonSingleIndex++) {
-      tempPath = children.get(firstNonSingleIndex).getSinglePath();
+      tempPath = children.get(firstNonSingleIndex).getPath();
       // sorted by seriesPath, all non-single filters are in the end
       if (tempPath == null) {
         break;
@@ -124,7 +124,7 @@ public class MergeSingleFilterOptimizer implements IFilterOptimizer {
         } else {
           // add a new inner node
           FilterOperator newFilter = new FilterOperator(filter.getTokenIntType(), true);
-          newFilter.setSinglePath(childPath);
+          newFilter.setPath(childPath);
           newFilter.setChildren(tempExtrNode);
           ret.add(newFilter);
           tempExtrNode = new ArrayList<>();
@@ -140,7 +140,7 @@ public class MergeSingleFilterOptimizer implements IFilterOptimizer {
       } else {
         // add a new inner node
         FilterOperator newFil = new FilterOperator(filter.getTokenIntType(), true);
-        newFil.setSinglePath(childPath);
+        newFil.setPath(childPath);
         newFil.setChildren(tempExtrNode);
         ret.add(newFil);
       }
@@ -157,7 +157,7 @@ public class MergeSingleFilterOptimizer implements IFilterOptimizer {
     if (ret.size() == 1) {
       // all children have same seriesPath, which means this filter node is a single node
       filter.setIsSingle(true);
-      filter.setSinglePath(childPath);
+      filter.setPath(childPath);
       filter.setChildren(ret.get(0).getChildren());
       return childPath;
     } else {
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/RemoveNotOptimizer.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/RemoveNotOptimizer.java
index 9b1fde7..5b85bcf 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/RemoveNotOptimizer.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/RemoveNotOptimizer.java
@@ -28,6 +28,7 @@ import org.apache.iotdb.db.exception.query.LogicalOptimizeException;
 import org.apache.iotdb.db.qp.constant.SQLConstant;
 import org.apache.iotdb.db.qp.logical.crud.BasicFunctionOperator;
 import org.apache.iotdb.db.qp.logical.crud.FilterOperator;
+import org.apache.iotdb.db.qp.logical.crud.FunctionOperator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -78,7 +79,7 @@ public class RemoveNotOptimizer implements IFilterOptimizer {
     int tokenInt = filter.getTokenIntType();
     if (filter.isLeaf()) {
       try {
-        ((BasicFunctionOperator) filter).setReversedTokenIntType();
+        ((FunctionOperator)filter).setReversedTokenIntType();
       } catch (LogicalOperatorException e) {
         logger.error("meet error while converting BasicFunction.", e);
         throw new LogicalOperatorException(
diff --git a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQueryDemoIT.java b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQueryDemoIT.java
index 77191a6..de7832a 100644
--- a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQueryDemoIT.java
+++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBQueryDemoIT.java
@@ -233,7 +233,6 @@ public class IoTDBQueryDemoIT {
           for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
             builder.append(resultSet.getString(i)).append(",");
           }
-          System.out.println(builder.toString());
           Assert.assertEquals(retArray[cnt], builder.toString());
           cnt++;
         }
@@ -251,7 +250,6 @@ public class IoTDBQueryDemoIT {
         for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
           header.append(resultSetMetaData.getColumnName(i)).append(",");
         }
-        System.out.println(header.toString());
         Assert.assertEquals(
             "Time,root.ln.wf01.wt01.status,root.ln.wf01.wt01.temperature,"
                 + "root.ln.wf02.wt02.hardware,root.ln.wf02.wt02.status,root.sgcc.wf03.wt01.status,"
@@ -270,7 +268,96 @@ public class IoTDBQueryDemoIT {
           for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
             builder.append(resultSet.getString(i)).append(",");
           }
-          System.out.println(builder.toString());
+          Assert.assertEquals(retArray[cnt], builder.toString());
+          cnt++;
+        }
+        Assert.assertEquals(5, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void InTest() throws ClassNotFoundException {
+    String[] retArray = new String[]{
+        "1509465780000,false,20.18,v1,false,false,20.18,",
+        "1509465840000,false,21.13,v1,false,false,21.13,",
+        "1509465900000,false,22.72,v1,false,false,22.72,",
+        "1509465960000,false,20.71,v1,false,false,20.71,",
+        "1509466020000,false,21.45,v1,false,false,21.45,",
+    };
+
+    Class.forName(Config.JDBC_DRIVER_NAME);
+    try (Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+
+      // test 1: fetchSize < limitNumber
+      statement.setFetchSize(4);
+      Assert.assertEquals(4, statement.getFetchSize());
+      boolean hasResultSet = statement.execute("select * from root where time in (1509465780000, 1509465840000, 1509465900000, 1509465960000, 1509466020000)");
+      Assert.assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+        StringBuilder header = new StringBuilder();
+        for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+          header.append(resultSetMetaData.getColumnName(i)).append(",");
+        }
+        Assert.assertEquals(
+            "Time,root.ln.wf01.wt01.status,root.ln.wf01.wt01.temperature,"
+                + "root.ln.wf02.wt02.hardware,root.ln.wf02.wt02.status,root.sgcc.wf03.wt01.status,"
+                + "root.sgcc.wf03.wt01.temperature,", header.toString());
+        Assert.assertEquals(Types.TIMESTAMP, resultSetMetaData.getColumnType(1));
+        Assert.assertEquals(Types.BOOLEAN, resultSetMetaData.getColumnType(2));
+        Assert.assertEquals(Types.FLOAT, resultSetMetaData.getColumnType(3));
+        Assert.assertEquals(Types.VARCHAR, resultSetMetaData.getColumnType(4));
+        Assert.assertEquals(Types.BOOLEAN, resultSetMetaData.getColumnType(5));
+        Assert.assertEquals(Types.BOOLEAN, resultSetMetaData.getColumnType(6));
+        Assert.assertEquals(Types.FLOAT, resultSetMetaData.getColumnType(7));
+
+        int cnt = 0;
+        while (resultSet.next()) {
+          StringBuilder builder = new StringBuilder();
+          for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+            builder.append(resultSet.getString(i)).append(",");
+          }
+          Assert.assertEquals(retArray[cnt], builder.toString());
+          cnt++;
+        }
+        Assert.assertEquals(5, cnt);
+      }
+
+      // test 1: fetchSize > limitNumber
+      statement.setFetchSize(10000);
+      Assert.assertEquals(10000, statement.getFetchSize());
+      hasResultSet = statement.execute("select * from root where time>10 limit 5 offset 3");
+      Assert.assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+        StringBuilder header = new StringBuilder();
+        for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+          header.append(resultSetMetaData.getColumnName(i)).append(",");
+        }
+        Assert.assertEquals(
+            "Time,root.ln.wf01.wt01.status,root.ln.wf01.wt01.temperature,"
+                + "root.ln.wf02.wt02.hardware,root.ln.wf02.wt02.status,root.sgcc.wf03.wt01.status,"
+                + "root.sgcc.wf03.wt01.temperature,", header.toString());
+        Assert.assertEquals(Types.TIMESTAMP, resultSetMetaData.getColumnType(1));
+        Assert.assertEquals(Types.BOOLEAN, resultSetMetaData.getColumnType(2));
+        Assert.assertEquals(Types.FLOAT, resultSetMetaData.getColumnType(3));
+        Assert.assertEquals(Types.VARCHAR, resultSetMetaData.getColumnType(4));
+        Assert.assertEquals(Types.BOOLEAN, resultSetMetaData.getColumnType(5));
+        Assert.assertEquals(Types.BOOLEAN, resultSetMetaData.getColumnType(6));
+        Assert.assertEquals(Types.FLOAT, resultSetMetaData.getColumnType(7));
+
+        int cnt = 0;
+        while (resultSet.next()) {
+          StringBuilder builder = new StringBuilder();
+          for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+            builder.append(resultSet.getString(i)).append(",");
+          }
           Assert.assertEquals(retArray[cnt], builder.toString());
           cnt++;
         }
diff --git a/server/src/test/java/org/apache/iotdb/db/qp/plan/PhysicalPlanTest.java b/server/src/test/java/org/apache/iotdb/db/qp/plan/PhysicalPlanTest.java
index 9f38f50..f6f3ce2 100644
--- a/server/src/test/java/org/apache/iotdb/db/qp/plan/PhysicalPlanTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/qp/plan/PhysicalPlanTest.java
@@ -23,6 +23,11 @@ import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import org.apache.iotdb.db.exception.StartupException;
 import org.apache.iotdb.db.exception.metadata.MetadataException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
@@ -510,6 +515,51 @@ public class PhysicalPlanTest {
   }
 
   @Test
+  public void testInOperator() throws QueryProcessException {
+    String sqlStr = "SELECT s1 FROM root.vehicle.d1 WHERE s1 in (25, 30, 40)";
+    PhysicalPlan plan = processor.parseSQLToPhysicalPlan(sqlStr);
+    IExpression queryFilter = ((QueryPlan) plan).getExpression();
+    List<String> valuesList = new ArrayList<>();
+    valuesList.add("25");
+    valuesList.add("30");
+    valuesList.add("40");
+    Collections.sort(valuesList);
+    Set<Float> values = new HashSet<>();
+    values.add(25.0f);
+    values.add(30.0f);
+    values.add(40.0f);
+    IExpression expect = new SingleSeriesExpression(new Path("root.vehicle.d1.s1"),
+        ValueFilter.in(values, false, valuesList.toString()));
+    assertEquals(expect.toString(), queryFilter.toString());
+  }
+
+  @Test
+  public void testNotInOperator() throws QueryProcessException {
+    String sqlStr = "SELECT s1 FROM root.vehicle.d1 WHERE s1 not in (25, 30, 40)";
+    PhysicalPlan plan = processor.parseSQLToPhysicalPlan(sqlStr);
+    IExpression queryFilter = ((QueryPlan) plan).getExpression();
+    List<String> valuesList = new ArrayList<>();
+    valuesList.add("25");
+    valuesList.add("30");
+    valuesList.add("40");
+    Collections.sort(valuesList);
+    Set<Float> values = new HashSet<>();
+    values.add(25.0f);
+    values.add(30.0f);
+    values.add(40.0f);
+    IExpression expect = new SingleSeriesExpression(new Path("root.vehicle.d1.s1"),
+        ValueFilter.in(values, true, valuesList.toString()));
+    assertEquals(expect.toString(), queryFilter.toString());
+
+    sqlStr = "SELECT s1 FROM root.vehicle.d1 WHERE not(s1 not in (25, 30, 40))";
+    plan = processor.parseSQLToPhysicalPlan(sqlStr);
+    queryFilter = ((QueryPlan) plan).getExpression();
+    expect = new SingleSeriesExpression(new Path("root.vehicle.d1.s1"),
+        ValueFilter.in(values, false, valuesList.toString()));
+    assertEquals(expect.toString(), queryFilter.toString());
+  }
+
+  @Test
   public void testGrantWatermarkEmbedding()
       throws QueryProcessException, MetadataException {
     String sqlStr = "GRANT WATERMARK_EMBEDDING to a,b";
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/TimeFilter.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/TimeFilter.java
index d2a663a..007c813 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/TimeFilter.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/TimeFilter.java
@@ -18,11 +18,13 @@
  */
 package org.apache.iotdb.tsfile.read.filter;
 
+import java.util.Set;
 import org.apache.iotdb.tsfile.read.filter.basic.Filter;
 import org.apache.iotdb.tsfile.read.filter.factory.FilterType;
 import org.apache.iotdb.tsfile.read.filter.operator.Eq;
 import org.apache.iotdb.tsfile.read.filter.operator.Gt;
 import org.apache.iotdb.tsfile.read.filter.operator.GtEq;
+import org.apache.iotdb.tsfile.read.filter.operator.In;
 import org.apache.iotdb.tsfile.read.filter.operator.Lt;
 import org.apache.iotdb.tsfile.read.filter.operator.LtEq;
 import org.apache.iotdb.tsfile.read.filter.operator.NotEq;
@@ -58,6 +60,17 @@ public class TimeFilter {
     return new TimeNotEq(value);
   }
 
+  public static TimeIn in(Set<Long> values, boolean not, String valueToString) {
+    return new TimeIn(values, not, valueToString);
+  }
+
+  public static class TimeIn extends In {
+
+    private TimeIn(Set<Long> values, boolean not, String valueToString) {
+      super(values, FilterType.TIME_FILTER,not,  valueToString);
+    }
+  }
+
   public static class TimeEq extends Eq {
 
     private TimeEq(long value) {
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/ValueFilter.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/ValueFilter.java
index 18b1285..fa0c7c4 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/ValueFilter.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/ValueFilter.java
@@ -18,11 +18,13 @@
  */
 package org.apache.iotdb.tsfile.read.filter;
 
+import java.util.Set;
 import org.apache.iotdb.tsfile.read.filter.basic.Filter;
 import org.apache.iotdb.tsfile.read.filter.factory.FilterType;
 import org.apache.iotdb.tsfile.read.filter.operator.Eq;
 import org.apache.iotdb.tsfile.read.filter.operator.Gt;
 import org.apache.iotdb.tsfile.read.filter.operator.GtEq;
+import org.apache.iotdb.tsfile.read.filter.operator.In;
 import org.apache.iotdb.tsfile.read.filter.operator.Lt;
 import org.apache.iotdb.tsfile.read.filter.operator.LtEq;
 import org.apache.iotdb.tsfile.read.filter.operator.NotEq;
@@ -50,6 +52,10 @@ public class ValueFilter {
     return new ValueLtEq(value);
   }
 
+  public static <T extends  Comparable<T>> ValueIn<T> in(Set<T> values, boolean not, String valueToString){
+    return new ValueIn(values, not, valueToString);
+  }
+
   public static ValueNotFilter not(Filter filter) {
     return new ValueNotFilter(filter);
   }
@@ -58,6 +64,13 @@ public class ValueFilter {
     return new ValueNotEq(value);
   }
 
+  public static class ValueIn<T extends Comparable<T>> extends In<T> {
+
+    private ValueIn(Set<T> values, boolean not, String valueToString) {
+      super(values, FilterType.VALUE_FILTER, not, valueToString);
+    }
+  }
+
   public static class ValueEq<T extends Comparable<T>> extends Eq<T> {
 
     private ValueEq(T value) {
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterFactory.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterFactory.java
index 7781e42..07f849a 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterFactory.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterFactory.java
@@ -30,8 +30,6 @@ import org.apache.iotdb.tsfile.read.filter.operator.LtEq;
 import org.apache.iotdb.tsfile.read.filter.operator.NotEq;
 import org.apache.iotdb.tsfile.read.filter.operator.NotFilter;
 import org.apache.iotdb.tsfile.read.filter.operator.OrFilter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class FilterFactory {
 
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterSerializeId.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterSerializeId.java
index 1b20251..a76f1fa 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterSerializeId.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterSerializeId.java
@@ -20,5 +20,5 @@
 package org.apache.iotdb.tsfile.read.filter.factory;
 
 public enum FilterSerializeId {
-  AND, EQ, GROUP_BY, GT, GTEQ, LT, LTEQ, NEQ, NOT, OR
+  AND, EQ, GROUP_BY, GT, GTEQ, LT, LTEQ, NEQ, NOT, OR, IN
 }
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Eq.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Eq.java
index 9a6cc0e..931d046 100755
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Eq.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Eq.java
@@ -18,7 +18,6 @@
  */
 package org.apache.iotdb.tsfile.read.filter.operator;
 
-import java.util.Objects;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics;
 import org.apache.iotdb.tsfile.read.filter.basic.Filter;
@@ -45,7 +44,8 @@ public class Eq<T extends Comparable<T>> extends UnaryFilter<T> {
   @Override
   public boolean satisfy(Statistics statistics) {
     if (filterType == FilterType.TIME_FILTER) {
-      return ((Long) value) >= statistics.getStartTime() && ((Long) value) <= statistics.getEndTime();
+      return ((Long) value) >= statistics.getStartTime() && ((Long) value) <= statistics
+          .getEndTime();
     } else {
       if (statistics.getType() == TSDataType.TEXT || statistics.getType() == TSDataType.BOOLEAN) {
         return true;
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Eq.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/In.java
old mode 100755
new mode 100644
similarity index 50%
copy from tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Eq.java
copy to tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/In.java
index 9a6cc0e..eb2c4c8
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Eq.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/In.java
@@ -18,81 +18,105 @@
  */
 package org.apache.iotdb.tsfile.read.filter.operator;
 
-import java.util.Objects;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.HashSet;
+import java.util.Set;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics;
 import org.apache.iotdb.tsfile.read.filter.basic.Filter;
-import org.apache.iotdb.tsfile.read.filter.basic.UnaryFilter;
 import org.apache.iotdb.tsfile.read.filter.factory.FilterSerializeId;
 import org.apache.iotdb.tsfile.read.filter.factory.FilterType;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
 
 /**
- * Equals.
+ * in clause.
  *
  * @param <T> comparable data type
  */
-public class Eq<T extends Comparable<T>> extends UnaryFilter<T> {
+public class In<T extends Comparable<T>> implements Filter {
 
-  private static final long serialVersionUID = -6668083116644568248L;
+  private static final long serialVersionUID = 8572705136773595399L;
 
-  public Eq() {
+  private Set<T> values;
+
+  private boolean not;
+
+  private String valueToString;
+
+  private FilterType filterType;
+
+  public In() {
   }
 
-  public Eq(T value, FilterType filterType) {
-    super(value, filterType);
+  public In(Set<T> values, FilterType filterType, boolean not, String valueToString) {
+    this.values = values;
+    this.filterType = filterType;
+    this.not = not;
+    this.valueToString = valueToString;
   }
 
   @Override
   public boolean satisfy(Statistics statistics) {
-    if (filterType == FilterType.TIME_FILTER) {
-      return ((Long) value) >= statistics.getStartTime() && ((Long) value) <= statistics.getEndTime();
-    } else {
-      if (statistics.getType() == TSDataType.TEXT || statistics.getType() == TSDataType.BOOLEAN) {
-        return true;
-      }
-      return value.compareTo((T) statistics.getMinValue()) >= 0
-          && value.compareTo((T) statistics.getMaxValue()) <= 0;
-    }
+    return true;
   }
 
   @Override
   public boolean satisfy(long time, Object value) {
     Object v = filterType == FilterType.TIME_FILTER ? time : value;
-    return this.value.equals(v);
+    return this.values.contains(v) != not;
   }
 
   @Override
   public boolean satisfyStartEndTime(long startTime, long endTime) {
-    if (filterType == FilterType.TIME_FILTER) {
-      long time = (Long) value;
-      return time <= endTime && time >= startTime;
-    } else {
-      return true;
-    }
+    return true;
   }
 
   @Override
   public boolean containStartEndTime(long startTime, long endTime) {
-    if (filterType == FilterType.TIME_FILTER) {
-      long time = (Long) value;
-      return time == startTime && time == endTime;
-    } else {
-      return true;
-    }
+    return true;
   }
 
   @Override
   public Filter clone() {
-    return new Eq(value, filterType);
+    return new In(values, filterType, not, valueToString);
+  }
+
+  @Override
+  public void serialize(DataOutputStream outputStream) {
+    try {
+      outputStream.write(getSerializeId().ordinal());
+      outputStream.write(filterType.ordinal());
+      ReadWriteIOUtils.write(not, outputStream);
+      ReadWriteIOUtils.write(valueToString, outputStream);
+      outputStream.write(values.size());
+      for (T value : values) {
+        ReadWriteIOUtils.writeObject(value, outputStream);
+      }
+    } catch (IOException ignored) {
+      // ignored
+    }
+  }
+
+  @Override
+  public void deserialize(ByteBuffer buffer) {
+    filterType = FilterType.values()[buffer.get()];
+    not = ReadWriteIOUtils.readBool(buffer);
+    valueToString = ReadWriteIOUtils.readString(buffer);
+    values = new HashSet<>();
+    for (int i = 0; i < buffer.get(); i++) {
+      values.add((T) ReadWriteIOUtils.readObject(buffer));
+    }
   }
 
   @Override
   public String toString() {
-    return getFilterType() + " == " + value;
+    return filterType + " < " + "reverse: " + not + ", " + valueToString;
   }
 
   @Override
   public FilterSerializeId getSerializeId() {
-    return FilterSerializeId.EQ;
+    return FilterSerializeId.IN;
   }
 }