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:42 UTC

[incubator-iotdb] branch add_in_operation created (now 6b6bfe4)

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

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


      at 6b6bfe4  add IN operation

This branch includes the following new commits:

     new 6b6bfe4  add IN operation

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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

Posted by lt...@apache.org.
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;
   }
 }