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

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


The following commit(s) were added to refs/heads/master by this push:
     new 8fc0c7e  fix unstable unit test and polish ReadOnlyTsFile query logic 
(#177)
8fc0c7e is described below

commit 8fc0c7e6ed60bcb4491088b7a636d96d76ebbf36
Author: RUI, LEI <[email protected]>
AuthorDate: Fri May 17 15:44:06 2019 +0800

    fix unstable unit test and polish ReadOnlyTsFile query logic (#177)
    
    * modify logic: add params in ReadOnlyTsFile's query function not in 
constructor
    
    * remove loadMode; update the calculation of remaining time ranges
    
    * fix sonar
---
 .../org/apache/iotdb/tsfile/DefaultSource.scala    |   9 +-
 .../tsfile/common/constant/QueryConstant.java      |   6 -
 .../apache/iotdb/tsfile/read/ReadOnlyTsFile.java   |  17 +-
 .../apache/iotdb/tsfile/read/common/TimeRange.java | 225 +++++++++++--------
 .../tsfile/read/controller/MetadataQuerier.java    |  39 +---
 .../read/controller/MetadataQuerierByFileImpl.java | 189 ++++++++--------
 .../tsfile/read/query/executor/TsFileExecutor.java | 100 ++++-----
 .../iotdb/tsfile/read/ReadInPartitionTest.java     | 244 ++++++++++++---------
 .../iotdb/tsfile/read/common/TimeRangeTest.java    |  42 +++-
 .../controller/MetadataQuerierByFileImplTest.java  | 156 ++++++++-----
 10 files changed, 563 insertions(+), 464 deletions(-)

diff --git a/spark/src/main/scala/org/apache/iotdb/tsfile/DefaultSource.scala 
b/spark/src/main/scala/org/apache/iotdb/tsfile/DefaultSource.scala
index 249f511..eea029a 100755
--- a/spark/src/main/scala/org/apache/iotdb/tsfile/DefaultSource.scala
+++ b/spark/src/main/scala/org/apache/iotdb/tsfile/DefaultSource.scala
@@ -95,10 +95,6 @@ private[tsfile] class DefaultSource extends FileFormat with 
DataSourceRegister {
       }
       }
 
-      val params = new java.util.HashMap[java.lang.String, java.lang.Long]()
-      params.put(QueryConstant.PARTITION_START_OFFSET, 
file.start.asInstanceOf[java.lang.Long])
-      params.put(QueryConstant.PARTITION_END_OFFSET, (file.start + 
file.length).asInstanceOf[java.lang.Long])
-
       val tsFileMetaData = reader.readFileMetadata
 
       // get queriedSchema from requiredSchema
@@ -107,8 +103,9 @@ private[tsfile] class DefaultSource extends FileFormat with 
DataSourceRegister {
       // construct queryExpression based on queriedSchema and filters
       val queryExpression = Converter.toQueryExpression(queriedSchema, filters)
 
-      val readTsFile: ReadOnlyTsFile = new ReadOnlyTsFile(reader, params)
-      val queryDataSet = readTsFile.query(queryExpression)
+      val readTsFile: ReadOnlyTsFile = new ReadOnlyTsFile(reader)
+      val queryDataSet = readTsFile.query(queryExpression, 
file.start.asInstanceOf[java.lang.Long],
+        (file.start + file.length).asInstanceOf[java.lang.Long])
 
       new Iterator[InternalRow] {
         private val rowBuffer = Array.fill[Any](requiredSchema.length)(null)
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/common/constant/QueryConstant.java
 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/common/constant/QueryConstant.java
index fc204c3..99e58dd 100644
--- 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/common/constant/QueryConstant.java
+++ 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/common/constant/QueryConstant.java
@@ -19,12 +19,6 @@
 package org.apache.iotdb.tsfile.common.constant;
 
 public class QueryConstant {
-
-  // The start offset for the partition
-  public static final String PARTITION_START_OFFSET = "partition_start_offset";
-  // The end offset for the partition
-  public static final String PARTITION_END_OFFSET = "partition_end_offset";
-
   public static final String RESERVED_TIME = "time";
 
   public static final String BOOLEAN = "BOOLEAN";
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/ReadOnlyTsFile.java 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/ReadOnlyTsFile.java
index 89aba48..4f6617b 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/ReadOnlyTsFile.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/ReadOnlyTsFile.java
@@ -19,7 +19,6 @@
 package org.apache.iotdb.tsfile.read;
 
 import java.io.IOException;
-import java.util.HashMap;
 import org.apache.iotdb.tsfile.read.controller.ChunkLoader;
 import org.apache.iotdb.tsfile.read.controller.ChunkLoaderImpl;
 import org.apache.iotdb.tsfile.read.controller.MetadataQuerier;
@@ -45,21 +44,15 @@ public class ReadOnlyTsFile implements AutoCloseable {
     tsFileExecutor = new TsFileExecutor(metadataQuerier, chunkLoader);
   }
 
-  /**
-   * constructor, create ReadOnlyTsFile with TsFileSequenceReader.
-   */
-  public ReadOnlyTsFile(TsFileSequenceReader fileReader, HashMap<String, Long> 
params)
-      throws IOException {
-    this.fileReader = fileReader;
-    this.metadataQuerier = new MetadataQuerierByFileImpl(fileReader, params);
-    this.chunkLoader = new ChunkLoaderImpl(fileReader);
-    tsFileExecutor = new TsFileExecutor(metadataQuerier, chunkLoader);
-  }
-
   public QueryDataSet query(QueryExpression queryExpression) throws 
IOException {
     return tsFileExecutor.execute(queryExpression);
   }
 
+  public QueryDataSet query(QueryExpression queryExpression, long 
partitionStartOffset,
+      long partitionEndOffset) throws IOException {
+    return tsFileExecutor.execute(queryExpression, partitionStartOffset, 
partitionEndOffset);
+  }
+
   public void close() throws IOException {
     fileReader.close();
   }
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/TimeRange.java 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/TimeRange.java
index cdff289..792bd51 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/TimeRange.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/TimeRange.java
@@ -19,6 +19,8 @@
 package org.apache.iotdb.tsfile.read.common;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import org.apache.iotdb.tsfile.read.expression.IExpression;
 import org.apache.iotdb.tsfile.read.expression.impl.BinaryExpression;
@@ -46,6 +48,7 @@ public class TimeRange implements Comparable<TimeRange> {
 
   /**
    * Initialize a closed interval [min,max].
+   *
    * @param min the left endpoint of the closed interval
    * @param max the right endpoint of the closed interval
    */
@@ -75,11 +78,18 @@ public class TimeRange implements Comparable<TimeRange> {
     }
   }
 
-  /**
-   * @return true if the value lies between the min and max values, inclusively
-   */
-  public boolean contains(long value) {
-    return min <= value && max >= value;
+  public void setMin(long min) {
+    if (min < 0 || min > this.max) {
+      throw new IllegalArgumentException("Invalid input!");
+    }
+    this.min = min;
+  }
+
+  public void setMax(long max) {
+    if (max < 0 || max < this.min) {
+      throw new IllegalArgumentException("Invalid input!");
+    }
+    this.max = max;
   }
 
   /**
@@ -89,22 +99,19 @@ public class TimeRange implements Comparable<TimeRange> {
     return min <= r.min && max >= r.max;
   }
 
+
   /**
-   * @param min
-   * @param max
+   * Set a closed interval [min,max].
+   *
+   * @param min the left endpoint of the closed interval
+   * @param max the right endpoint of the closed interval
    */
   public void set(long min, long max) {
+    if (min > max) {
+      throw new IllegalArgumentException("min should not be larger than max.");
+    }
     this.min = min;
     this.max = max;
-
-    sort();
-  }
-
-  /**
-   * @param r
-   */
-  public void set(TimeRange r) {
-    set(r.getMin(), r.getMax());
   }
 
   /**
@@ -115,75 +122,67 @@ public class TimeRange implements Comparable<TimeRange> {
   }
 
   /**
-   * @param min
-   */
-  public void setMin(long min) {
-    this.min = min;
-
-    sort();
-  }
-
-  /**
    * @return The upper range boundary
    */
   public long getMax() {
     return max;
   }
 
-  /**
-   * @param max
-   */
-  public void setMax(long max) {
-    this.max = max;
-
-    sort();
-  }
-
-  private void sort() {
-    if (min > max) {
-      long t = min;
-      min = max;
-      max = t;
-    }
-  }
 
   /**
-   * @return <code>true</code> if the intersection exists
+   * Here are some examples.
+   *
+   * [1,3] does not intersect with (4,5].
+   *
+   * [1,3) does not intersect with (3,5]
+   *
+   * [1,3] does not intersect with [5,6].
+   *
+   * [1,3] intersects with [2,5].
+   *
+   * [1,3] intersects with (3,5].
+   *
+   * [1,3) intersects with (2,5].
+   *
+   * @param r the given time range
+   * @return true if the current time range intersects with the given time 
range r
    */
-  public boolean intersection(TimeRange r, TimeRange dest) {
-    if (intersects(r)) {
-      dest.set(Math.max(min, r.min), Math.min(max, r.max));
+  private boolean intersects(TimeRange r) {
+    if ((!leftClose || !r.rightClose) && (r.max < min)) {
+      // e.g., [1,3] does not intersect with (4,5].
+      return false;
+    } else if (!leftClose && !r.rightClose && r.max <= min) {
+      // e.g.,[1,3) does not intersect with (3,5]
+      return false;
+    } else if (leftClose && r.rightClose && r.max <= min - 2) {
+      // e.g.,[1,3] does not intersect with [5,6].
+      return true;
+    } else if ((!rightClose || !r.leftClose) && (r.min > max)) {
+      return false;
+    } else if (!rightClose && r.leftClose && r.min >= max) {
+      return false;
+    } else if (rightClose && r.leftClose && r.min >= max + 2) {
+      return false;
+    } else {
       return true;
     }
-
-    return false;
-  }
-
-  /**
-   * @return <code>true</code> if the ranges have values in common
-   */
-  public boolean intersects(TimeRange r) {
-    return overlaps(min, max, r.min, r.max);
-  }
-
-  /**
-   * @return <code>true</code> if the ranges overlap
-   */
-  public static boolean overlaps(long minA, long maxA, long minB, long maxB) {
-    if (minA > maxA) {
-      throw new IllegalArgumentException("Invalid input: minA should not be 
larger than maxA.");
-    }
-    if (minB > maxB) {
-      throw new IllegalArgumentException("Invalid input: minB should not be 
larger than maxB.");
-    }
-
-    // Because timestamp is long data type, x and x+1 are considered 
continuous.
-    return !(minA >= maxB + 2 || maxA <= minB - 2);
   }
 
   @Override
   public String toString() {
-    return "[ " + min + " : " + max + " ]";
+    StringBuilder res = new StringBuilder();
+    if (leftClose) {
+      res.append("[ ");
+    } else {
+      res.append("( ");
+    }
+    res.append(min).append(" : ").append(max);
+    if (rightClose) {
+      res.append(" ]");
+    } else {
+      res.append(" )");
+    }
+    return res.toString();
   }
 
   // NOTE the primitive timeRange is always a closed interval [min,max] and
@@ -191,11 +190,11 @@ public class TimeRange implements Comparable<TimeRange> {
   private boolean leftClose = true; // default true
   private boolean rightClose = true; // default true
 
-  public void setLeftClose(boolean leftClose) {
+  private void setLeftClose(boolean leftClose) {
     this.leftClose = leftClose;
   }
 
-  public void setRightClose(boolean rightClose) {
+  private void setRightClose(boolean rightClose) {
     this.rightClose = rightClose;
   }
 
@@ -208,64 +207,104 @@ public class TimeRange implements Comparable<TimeRange> {
   }
 
   /**
+   * Return the union of the given time ranges.
+   *
+   * @param unionCandidates time ranges to be merged
+   * @return the union of time ranges
+   */
+  public static List<TimeRange> sortAndMerge(List<TimeRange> unionCandidates) {
+    //sort the time ranges in ascending order of the start time
+    Collections.sort(unionCandidates);
+
+    ArrayList<TimeRange> unionResult = new ArrayList<>();
+    Iterator<TimeRange> iterator = unionCandidates.iterator();
+    TimeRange rangeCurr;
+
+    if (!iterator.hasNext()) {
+      return unionResult;
+    } else {
+      rangeCurr = iterator.next();
+    }
+
+    while (iterator.hasNext()) {
+      TimeRange rangeNext = iterator.next();
+      if (rangeCurr.intersects(rangeNext)) {
+        rangeCurr.set(Math.min(rangeCurr.getMin(), rangeNext.getMin()),
+            Math.max(rangeCurr.getMax(), rangeNext.getMax()));
+      } else {
+        unionResult.add(rangeCurr);
+        rangeCurr = rangeNext;
+      }
+    }
+    unionResult.add(rangeCurr);
+    return unionResult;
+  }
+
+  /**
    * Get the remaining time ranges in the current ranges but not in 
timeRangesPrev.
    *
    * NOTE the primitive timeRange is always a closed interval [min,max] and 
only in this function
    * are leftClose and rightClose changed.
    *
-   * @param timeRangesPrev time ranges union in ascending order
+   * @param timeRangesPrev time ranges union in ascending order of the start 
time
    * @return the remaining time ranges
    */
-  public List<TimeRange> getRemains(ArrayList<TimeRange> timeRangesPrev) {
+  public List<TimeRange> getRemains(List<TimeRange> timeRangesPrev) {
     List<TimeRange> remains = new ArrayList<>();
 
     for (TimeRange prev : timeRangesPrev) {
-      if (prev.min >= max + 2) { // keep consistent with the definition of 
`overlap`
-        break; // break early since timeRangesPrev is sorted
+      // +2 is to keep consistent with the definition of `intersects` of two 
closed intervals
+      if (prev.min >= max + 2) {
+        // break early since timeRangesPrev is sorted
+        break;
       }
 
       if (intersects(prev)) {
         if (prev.contains(this)) {
+          // e.g., this=[3,5], prev=[1,10]
+          // e.g., this=[3,5], prev=[3,5] Note that in this case, prev 
contains this and vice versa.
           return remains;
         } else if (this.contains(prev)) {
           if (prev.min > this.min && prev.max == this.max) {
-            TimeRange r = new TimeRange(this.min, prev.min);
-            r.setLeftClose(this.leftClose);
-            r.setRightClose(false);
-            remains.add(r);
-            return remains; // because timeRangesPrev is sorted
-          } else if (prev.min == this.min) { // && prev.max < this.max
+            // e.g., this=[1,6], prev=[3,6]
+            this.setMax(prev.min);
+            this.setRightClose(false);
+            remains.add(this);
+            // return the final result because timeRangesPrev is sorted
+            return remains;
+          } else if (prev.min == this.min) {
+            // Note prev.max < this.max
+            // e.g., this=[1,10], prev=[1,4]
             min = prev.max;
             leftClose = false;
           } else {
+            // e.g., prev=[3,6], this=[1,10]
             TimeRange r = new TimeRange(this.min, prev.min);
             r.setLeftClose(this.leftClose);
             r.setRightClose(false);
             remains.add(r);
-
             min = prev.max;
             leftClose = false;
           }
-        } else { // intersect without one containing the other
+        } else {
+          // intersect without one containing the other
           if (prev.min < this.min) {
+            // e.g., this=[3,10], prev=[1,6]
             min = prev.max;
             leftClose = false;
           } else {
-            TimeRange r = new TimeRange(this.min, prev.min);
-            r.setLeftClose(this.leftClose);
-            r.setRightClose(false);
-            remains.add(r);
+            // e.g., this=[1,8], prev=[5,12]
+            this.setMax(prev.min);
+            this.setRightClose(false);
+            remains.add(this);
+            // return the final result because timeRangesPrev is sorted
             return remains;
           }
         }
       }
     }
 
-    TimeRange r = new TimeRange(this.min, this.max);
-    r.setLeftClose(this.leftClose);
-    r.setRightClose(this.rightClose);
-    remains.add(r);
-
+    remains.add(this);
     return remains;
   }
 
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerier.java
 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerier.java
index 1a0e17a..2202bb6 100644
--- 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerier.java
+++ 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerier.java
@@ -19,7 +19,6 @@
 package org.apache.iotdb.tsfile.read.controller;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import org.apache.iotdb.tsfile.exception.write.NoMeasurementException;
@@ -54,37 +53,13 @@ public interface MetadataQuerier {
   TSDataType getDataType(String measurement) throws NoMeasurementException;
 
   /**
-   * get time ranges of chunkGroups in or before the current partition and 
return the union result in ascending order
+   * Convert the space partition constraint to the time partition constraint.
    *
-   * @param paths timeseries paths
-   * @param targetMode InPartition or PrevPartition
-   * @return time ranges union in ascending order
-   * @throws IOException
+   * @param paths selected paths in a query expression
+   * @param spacePartitionStartPos the start position of the space partition
+   * @param spacePartitionEndPos the end position of the space partition
+   * @return the converted time partition constraint
    */
-  public ArrayList<TimeRange> getTimeRangeInOrPrev(List<Path> paths, LoadMode 
targetMode)
-      throws IOException;
-
-  /**
-   * get the load mode of the MetadataQuerier
-   *
-   * @return LoadMode enum
-   */
-  LoadMode getLoadMode();
-
-  /**
-   * set the load mode of the MetadataQuerier
-   *
-   * @param mode enum
-   */
-  void setLoadMode(LoadMode mode);
-
-  /**
-   * The load mode of the MetadataQuerier:
-   * NoPartition - load metadata of all chunkgroups in the file
-   * InPartition - load metadata of chunkgroups which fall in the current 
partition
-   * PrevPartition - load metadata of chunkgroups which fall ahead the current 
partition
-   */
-  enum LoadMode {
-    NoPartition, InPartition, PrevPartition
-  }
+  List<TimeRange> convertSpace2TimePartition(List<Path> paths, long 
spacePartitionStartPos,
+      long spacePartitionEndPos) throws IOException;
 }
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerierByFileImpl.java
 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerierByFileImpl.java
index 9f496be..93294d6 100644
--- 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerierByFileImpl.java
+++ 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerierByFileImpl.java
@@ -23,13 +23,11 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 import org.apache.iotdb.tsfile.common.cache.LRUCache;
-import org.apache.iotdb.tsfile.common.constant.QueryConstant;
 import org.apache.iotdb.tsfile.exception.write.NoMeasurementException;
 import org.apache.iotdb.tsfile.file.metadata.ChunkGroupMetaData;
 import org.apache.iotdb.tsfile.file.metadata.ChunkMetaData;
@@ -52,50 +50,12 @@ public class MetadataQuerierByFileImpl implements 
MetadataQuerier {
 
   private TsFileSequenceReader tsFileReader;
 
-  private LoadMode mode;
-
-  private long partitionStartOffset = 0L;
-  private long partitionEndOffset = 0L;
-
-  public LoadMode getLoadMode() {
-    return mode;
-  }
-
-  public void setLoadMode(LoadMode mode) {
-    this.mode = mode;
-  }
-
   /**
    * Constructor of MetadataQuerierByFileImpl.
    */
   public MetadataQuerierByFileImpl(TsFileSequenceReader tsFileReader) throws 
IOException {
     this.tsFileReader = tsFileReader;
     this.fileMetaData = tsFileReader.readFileMetadata();
-    this.mode = LoadMode.NoPartition;
-    chunkMetaDataCache = new LRUCache<Path, 
List<ChunkMetaData>>(CHUNK_METADATA_CACHE_SIZE) {
-      @Override
-      public List<ChunkMetaData> loadObjectByKey(Path key) throws IOException {
-        return loadChunkMetadata(key);
-      }
-    };
-  }
-
-  /**
-   * Constructor of MetadataQuerierByFileImpl.
-   */
-  public MetadataQuerierByFileImpl(TsFileSequenceReader tsFileReader, 
HashMap<String, Long> params)
-      throws IOException {
-    this.tsFileReader = tsFileReader;
-    this.fileMetaData = tsFileReader.readFileMetadata();
-
-    if (!params.containsKey(QueryConstant.PARTITION_START_OFFSET) || !params
-        .containsKey(QueryConstant.PARTITION_END_OFFSET)) {
-      throw new IllegalArgumentException(
-          "Input parameters miss partition_start_offset or 
partition_end_offset");
-    }
-    this.mode = LoadMode.InPartition;
-    this.partitionStartOffset = 
params.get(QueryConstant.PARTITION_START_OFFSET);
-    this.partitionEndOffset = params.get(QueryConstant.PARTITION_END_OFFSET);
     chunkMetaDataCache = new LRUCache<Path, 
List<ChunkMetaData>>(CHUNK_METADATA_CACHE_SIZE) {
       @Override
       public List<ChunkMetaData> loadObjectByKey(Path key) throws IOException {
@@ -168,9 +128,6 @@ public class MetadataQuerierByFileImpl implements 
MetadataQuerier {
           break;
         }
 
-        if (!checkAccess(chunkGroupMetaData)) {
-          continue;
-        }
         // s1, s2
         for (ChunkMetaData chunkMetaData : 
chunkGroupMetaData.getChunkMetaDataList()) {
 
@@ -229,90 +186,120 @@ public class MetadataQuerierByFileImpl implements 
MetadataQuerier {
     // get all ChunkMetaData of this path included in all ChunkGroups of this 
device
     List<ChunkMetaData> chunkMetaDataList = new ArrayList<>();
     for (ChunkGroupMetaData chunkGroupMetaData : 
tsDeviceMetadata.getChunkGroupMetaDataList()) {
-      if (checkAccess(chunkGroupMetaData)) {
-        List<ChunkMetaData> chunkMetaDataListInOneChunkGroup = 
chunkGroupMetaData
-            .getChunkMetaDataList();
-        for (ChunkMetaData chunkMetaData : chunkMetaDataListInOneChunkGroup) {
-          if (path.getMeasurement().equals(chunkMetaData.getMeasurementUid())) 
{
-            chunkMetaData.setVersion(chunkGroupMetaData.getVersion());
-            chunkMetaDataList.add(chunkMetaData);
-          }
+      List<ChunkMetaData> chunkMetaDataListInOneChunkGroup = chunkGroupMetaData
+          .getChunkMetaDataList();
+      for (ChunkMetaData chunkMetaData : chunkMetaDataListInOneChunkGroup) {
+        if (path.getMeasurement().equals(chunkMetaData.getMeasurementUid())) {
+          chunkMetaData.setVersion(chunkGroupMetaData.getVersion());
+          chunkMetaDataList.add(chunkMetaData);
         }
       }
     }
     return chunkMetaDataList;
   }
 
-  public ArrayList<TimeRange> getTimeRangeInOrPrev(List<Path> paths, LoadMode 
targetMode)
-      throws IOException {
-    if (mode == LoadMode.NoPartition) {
-      throw new IOException(
-          "Wrong use of getTimeRangeInOrPrev: should not be in NoPartition 
mode");
+  @Override
+  public List<TimeRange> convertSpace2TimePartition(List<Path> paths, long 
spacePartitionStartPos,
+      long spacePartitionEndPos) throws IOException {
+    if (spacePartitionStartPos > spacePartitionEndPos) {
+      throw new IllegalArgumentException(
+          "'spacePartitionStartPos' should not be larger than 
'spacePartitionEndPos'.");
     }
 
-    // change the mode temporarily to control loadChunkMetadata's checkAccess 
function
-    this.mode = targetMode;
+    // (1) get timeRangesInCandidates and timeRangesBeforeCandidates by 
iterating through the metadata
+    ArrayList<TimeRange> timeRangesInCandidates = new ArrayList<>();
+    ArrayList<TimeRange> timeRangesBeforeCandidates = new ArrayList<>();
 
-    ArrayList<TimeRange> unionCandidates = new ArrayList<>();
+    // group measurements by device
+    TreeMap<String, Set<String>> deviceMeasurementsMap = new TreeMap<>();
     for (Path path : paths) {
-      List<ChunkMetaData> chunkMetaDataList = loadChunkMetadata(path);
-      for (ChunkMetaData chunkMetaData : chunkMetaDataList) {
-        unionCandidates
-            .add(new TimeRange(chunkMetaData.getStartTime(), 
chunkMetaData.getEndTime()));
+      if (!deviceMeasurementsMap.containsKey(path.getDevice())) {
+        deviceMeasurementsMap.put(path.getDevice(), new HashSet<>());
       }
+      deviceMeasurementsMap.get(path.getDevice()).add(path.getMeasurement());
     }
-    Collections.sort(unionCandidates);
+    for (Map.Entry<String, Set<String>> deviceMeasurements : 
deviceMeasurementsMap.entrySet()) {
+      String selectedDevice = deviceMeasurements.getKey();
+      Set<String> selectedMeasurements = deviceMeasurements.getValue();
 
-    // union
-    ArrayList<TimeRange> unionResult = new ArrayList<>();
-    Iterator<TimeRange> iterator = unionCandidates.iterator();
-    TimeRange range_curr = null;
+      TsDeviceMetadataIndex index = 
fileMetaData.getDeviceMetadataIndex(selectedDevice);
+      TsDeviceMetadata tsDeviceMetadata = 
tsFileReader.readTsDeviceMetaData(index);
 
-    if (!iterator.hasNext()) {
-      return unionResult;
-    } else {
-      TimeRange r = iterator.next();
-      range_curr = new TimeRange(r.getMin(), r.getMax());
+      for (ChunkGroupMetaData chunkGroupMetaData : tsDeviceMetadata
+          .getChunkGroupMetaDataList()) {
+        LocateStatus mode = checkLocateStatus(chunkGroupMetaData, 
spacePartitionStartPos,
+            spacePartitionEndPos);
+        if (mode == LocateStatus.after) {
+          continue;
+        }
+        for (ChunkMetaData chunkMetaData : 
chunkGroupMetaData.getChunkMetaDataList()) {
+          String currentMeasurement = chunkMetaData.getMeasurementUid();
+          if (selectedMeasurements.contains(currentMeasurement)) {
+            TimeRange timeRange = new TimeRange(chunkMetaData.getStartTime(),
+                chunkMetaData.getEndTime());
+            if (mode == LocateStatus.in) {
+              timeRangesInCandidates.add(timeRange);
+            } else {
+              timeRangesBeforeCandidates.add(timeRange);
+            }
+          }
+        }
+      }
     }
 
-    while (iterator.hasNext()) {
-      TimeRange range_next = iterator.next();
-      if (range_curr.intersects(range_next)) {
-        range_curr.set(Math.min(range_curr.getMin(), range_next.getMin()),
-            Math.max(range_curr.getMax(), range_next.getMax()));
-      } else {
-        unionResult.add(new TimeRange(range_curr.getMin(), 
range_curr.getMax()));
-        range_curr.set(range_next.getMin(), range_next.getMax());
-      }
+    // (2) sort and merge the timeRangesInCandidates
+    ArrayList<TimeRange> timeRangesIn = new ArrayList<>(
+        TimeRange.sortAndMerge(timeRangesInCandidates));
+    if (timeRangesIn.isEmpty()) {
+      return Collections.emptyList(); // return an empty list
     }
-    unionResult.add(new TimeRange(range_curr.getMin(), range_curr.getMax()));
 
-    this.mode = LoadMode.InPartition; // restore the mode to InPartition
-    return unionResult;
+    // (3) sort and merge the timeRangesBeforeCandidates
+    ArrayList<TimeRange> timeRangesBefore = new ArrayList<>(
+        TimeRange.sortAndMerge(timeRangesBeforeCandidates));
 
+    // (4) calculate the remaining time ranges
+    List<TimeRange> resTimeRanges = new ArrayList<>();
+    for (TimeRange in : timeRangesIn) {
+      ArrayList<TimeRange> remains = new 
ArrayList<>(in.getRemains(timeRangesBefore));
+      resTimeRanges.addAll(remains);
+    }
+
+    return resTimeRanges;
   }
 
   /**
-   * check if the given chunk group can be accessed under the current load mode
-   * @param chunkGroupMetaData a chunk group's metadata
-   * @return True if the chunk group can be accessed. False otherwise.
-   * @throws IOException illegal mode
+   * Check the location of a given chunkGroupMetaData with respect to a space 
partition constraint.
+   *
+   * @param chunkGroupMetaData the given chunkGroupMetaData
+   * @param spacePartitionStartPos the start position of the space partition
+   * @param spacePartitionEndPos the end position of the space partition
+   * @return LocateStatus
    */
-  private boolean checkAccess(ChunkGroupMetaData chunkGroupMetaData) throws 
IOException {
+  private LocateStatus checkLocateStatus(ChunkGroupMetaData chunkGroupMetaData,
+      long spacePartitionStartPos, long spacePartitionEndPos) {
     long startOffsetOfChunkGroup = 
chunkGroupMetaData.getStartOffsetOfChunkGroup();
     long endOffsetOfChunkGroup = chunkGroupMetaData.getEndOffsetOfChunkGroup();
     long middleOffsetOfChunkGroup = (startOffsetOfChunkGroup + 
endOffsetOfChunkGroup) / 2;
-    switch (mode) {
-      case NoPartition:
-        return true; // always true
-      case InPartition:
-        return (partitionStartOffset <= middleOffsetOfChunkGroup
-            && middleOffsetOfChunkGroup < partitionEndOffset);
-      case PrevPartition:
-        return (middleOffsetOfChunkGroup < partitionStartOffset);
-      default:
-        throw new IOException(
-            "unexpected mode! It should be one of {NoPartition, InPartition, 
BeforePartition}");
+    if (spacePartitionStartPos <= middleOffsetOfChunkGroup
+        && middleOffsetOfChunkGroup < spacePartitionEndPos) {
+      return LocateStatus.in;
+    } else if (middleOffsetOfChunkGroup < spacePartitionStartPos) {
+      return LocateStatus.before;
+    } else {
+      return LocateStatus.after;
     }
   }
+
+  /**
+   * The location of a chunkGroupMetaData with respect to a space partition 
constraint.
+   *
+   * in - the middle point of the chunkGroupMetaData is located in the current 
space partition.
+   * before - the middle point of the chunkGroupMetaData is located before the 
current space
+   * partition. after - the middle point of the chunkGroupMetaData is located 
after the current
+   * space partition.
+   */
+  private enum LocateStatus {
+    in, before, after
+  }
 }
diff --git 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/executor/TsFileExecutor.java
 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/executor/TsFileExecutor.java
index 1e18792..13aa278 100644
--- 
a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/executor/TsFileExecutor.java
+++ 
b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/executor/TsFileExecutor.java
@@ -20,6 +20,7 @@ package org.apache.iotdb.tsfile.read.query.executor;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import 
org.apache.iotdb.tsfile.exception.filter.QueryFilterOptimizationException;
 import org.apache.iotdb.tsfile.exception.write.NoMeasurementException;
@@ -29,7 +30,6 @@ import org.apache.iotdb.tsfile.read.common.Path;
 import org.apache.iotdb.tsfile.read.common.TimeRange;
 import org.apache.iotdb.tsfile.read.controller.ChunkLoader;
 import org.apache.iotdb.tsfile.read.controller.MetadataQuerier;
-import org.apache.iotdb.tsfile.read.controller.MetadataQuerier.LoadMode;
 import org.apache.iotdb.tsfile.read.expression.IExpression;
 import org.apache.iotdb.tsfile.read.expression.QueryExpression;
 import org.apache.iotdb.tsfile.read.expression.impl.BinaryExpression;
@@ -54,62 +54,7 @@ public class TsFileExecutor implements QueryExecutor {
 
   @Override
   public QueryDataSet execute(QueryExpression queryExpression) throws 
IOException {
-    LoadMode mode = metadataQuerier.getLoadMode();
-    if (mode == LoadMode.PrevPartition) {
-      throw new IOException("Wrong use of PrevPartition mode.");
-    }
-
-    if (metadataQuerier.getLoadMode() == LoadMode.InPartition) {
-      // (1) get the sorted union covered time ranges of chunkGroups in the 
current partition
-      ArrayList<TimeRange> timeRangesIn = metadataQuerier
-          .getTimeRangeInOrPrev(queryExpression.getSelectedSeries(), 
LoadMode.InPartition);
-
-      // (2) check if null
-      if (timeRangesIn.size() == 0) {
-        return new DataSetWithoutTimeGenerator(new ArrayList<Path>(), new 
ArrayList<TSDataType>(),
-            new ArrayList<FileSeriesReader>()); // return empty QueryDataSet
-      }
-
-      // (3) get the sorted union covered time ranges of chunkGroups before 
the current partition
-      ArrayList<TimeRange> timeRangesPrev = metadataQuerier
-          .getTimeRangeInOrPrev(queryExpression.getSelectedSeries(), 
LoadMode.PrevPartition);
-
-      // (4) calculate the remaining time range
-      ArrayList<TimeRange> timeRangesRemains = new ArrayList<>();
-      for (TimeRange in : timeRangesIn) {
-        ArrayList<TimeRange> remains = new 
ArrayList<>(in.getRemains(timeRangesPrev));
-        timeRangesRemains.addAll(remains);
-      }
-
-      // (5) check if null
-      if (timeRangesRemains.size() == 0) {
-        return new DataSetWithoutTimeGenerator(new ArrayList<Path>(), new 
ArrayList<TSDataType>(),
-            new ArrayList<FileSeriesReader>()); // return empty QueryDataSet
-      }
-
-      // (6) add an additional global time filter based on the remaining time 
range
-      IExpression timeBound = timeRangesRemains.get(0).getExpression();
-      for (int i = 1; i < timeRangesRemains.size(); i++) {
-        timeBound = BinaryExpression
-            .or(timeBound, timeRangesRemains.get(i).getExpression());
-      }
-
-      if (queryExpression.hasQueryFilter()) {
-        IExpression timeBoundExpression = BinaryExpression
-            .and(queryExpression.getExpression(), timeBound);
-        queryExpression.setExpression(timeBoundExpression);
-      } else {
-        queryExpression.setExpression(timeBound);
-      }
-
-      // (7) with global time filters, we can now remove partition constraints
-      metadataQuerier.setLoadMode(LoadMode.NoPartition);
-      metadataQuerier.loadChunkMetaDatas(queryExpression.getSelectedSeries());
-
-    } else { // NoPartition mode
-      metadataQuerier.loadChunkMetaDatas(queryExpression.getSelectedSeries());
-    }
-
+    metadataQuerier.loadChunkMetaDatas(queryExpression.getSelectedSeries());
     if (queryExpression.hasQueryFilter()) {
       try {
         IExpression expression = queryExpression.getExpression();
@@ -137,6 +82,47 @@ public class TsFileExecutor implements QueryExecutor {
   }
 
   /**
+   * Query with the space partition constraint.
+   *
+   * @param queryExpression query expression
+   * @param spacePartitionStartPos the start position of the space partition
+   * @param spacePartitionEndPos the end position of the space partition
+   * @return QueryDataSet
+   */
+  public QueryDataSet execute(QueryExpression queryExpression, long 
spacePartitionStartPos,
+      long spacePartitionEndPos) throws IOException {
+    // convert the space partition constraint to the time partition constraint
+    ArrayList<TimeRange> resTimeRanges = new ArrayList<>(metadataQuerier
+        .convertSpace2TimePartition(queryExpression.getSelectedSeries(), 
spacePartitionStartPos,
+            spacePartitionEndPos));
+
+    // check if resTimeRanges is empty
+    if (resTimeRanges.isEmpty()) {
+      return new DataSetWithoutTimeGenerator(Collections.emptyList(), 
Collections.emptyList(),
+          Collections.emptyList()); // return an empty QueryDataSet
+    }
+
+    // construct an additional time filter based on the time partition 
constraint
+    IExpression addTimeExpression = resTimeRanges.get(0).getExpression();
+    for (int i = 1; i < resTimeRanges.size(); i++) {
+      addTimeExpression = BinaryExpression
+          .or(addTimeExpression, resTimeRanges.get(i).getExpression());
+    }
+
+    // combine the original query expression and the additional time filter
+    if (queryExpression.hasQueryFilter()) {
+      IExpression combinedExpression = BinaryExpression
+          .and(queryExpression.getExpression(), addTimeExpression);
+      queryExpression.setExpression(combinedExpression);
+    } else {
+      queryExpression.setExpression(addTimeExpression);
+    }
+
+    // Having converted the space partition constraint to an additional time 
filter, we can now query as normal.
+    return execute(queryExpression);
+  }
+
+  /**
    * no filter, can use multi-way merge.
    *
    * @param selectedPathList all selected paths
diff --git 
a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/ReadInPartitionTest.java 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/ReadInPartitionTest.java
index a0d1bbf..bb32923 100644
--- a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/ReadInPartitionTest.java
+++ b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/ReadInPartitionTest.java
@@ -18,65 +18,102 @@
  */
 package org.apache.iotdb.tsfile.read;
 
-import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.iotdb.tsfile.common.constant.QueryConstant;
+import 
org.apache.iotdb.tsfile.exception.filter.QueryFilterOptimizationException;
 import org.apache.iotdb.tsfile.exception.write.WriteProcessException;
 import org.apache.iotdb.tsfile.file.metadata.ChunkGroupMetaData;
+import org.apache.iotdb.tsfile.file.metadata.ChunkMetaData;
 import org.apache.iotdb.tsfile.file.metadata.TsDeviceMetadata;
 import org.apache.iotdb.tsfile.file.metadata.TsDeviceMetadataIndex;
 import org.apache.iotdb.tsfile.file.metadata.TsFileMetaData;
 import org.apache.iotdb.tsfile.read.common.Path;
 import org.apache.iotdb.tsfile.read.common.RowRecord;
+import org.apache.iotdb.tsfile.read.common.TimeRange;
+import org.apache.iotdb.tsfile.read.expression.ExpressionType;
+import org.apache.iotdb.tsfile.read.expression.IExpression;
 import org.apache.iotdb.tsfile.read.expression.QueryExpression;
+import org.apache.iotdb.tsfile.read.expression.impl.BinaryExpression;
+import org.apache.iotdb.tsfile.read.expression.impl.GlobalTimeExpression;
+import org.apache.iotdb.tsfile.read.expression.impl.SingleSeriesExpression;
+import org.apache.iotdb.tsfile.read.expression.util.ExpressionOptimizer;
+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.read.query.dataset.QueryDataSet;
 import org.apache.iotdb.tsfile.utils.TsFileGeneratorForTest;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
+/*
+  This test is designed for the TsFileExecutor's execute(queryExpression, 
params) function.
+
+  The test target here is the logic of converting the query partition 
constraint to an additional time filter.
+
+  Note that the correctness of the constructed additional time filter, which 
is guaranteed and tested in
+  MetadataQuerierByFileImplTest and TimeRangeTest, is not the test focus here.
+
+ */
 public class ReadInPartitionTest {
 
   private static final String FILE_PATH = 
TsFileGeneratorForTest.outputDataFile;
-  private TsFileSequenceReader reader;
   private static ReadOnlyTsFile roTsFile = null;
-  private static final Logger LOG = 
LoggerFactory.getLogger(ReadInPartitionTest.class);
+  private ArrayList<TimeRange> d1s6timeRangeList = new ArrayList<>();
+  private ArrayList<TimeRange> d2s1timeRangeList = new ArrayList<>();
+  private ArrayList<long[]> d1chunkGroupMetaDataOffsetList = new ArrayList<>();
+  private ArrayList<long[]> d2chunkGroupMetaDataOffsetList = new ArrayList<>();
+
 
   @Before
   public void before() throws InterruptedException, WriteProcessException, 
IOException {
     TsFileGeneratorForTest.generateFile(1000000, 1024 * 1024, 10000);
-    reader = new TsFileSequenceReader(FILE_PATH);
+    TsFileSequenceReader reader = new TsFileSequenceReader(FILE_PATH);
+    roTsFile = new ReadOnlyTsFile(reader);
 
-    LOG.info("file length: {}", new File(FILE_PATH).length());
-    LOG.info("file magic head: {}", reader.readHeadMagic());
-    LOG.info("file magic tail: {}", reader.readTailMagic());
-    LOG.info("Level 1 metadata position: {}", reader.getFileMetadataPos());
-    LOG.info("Level 1 metadata size: {}", reader.getFileMetadataPos());
+    // Because the size of the generated chunkGroupMetaData may differ under 
different test environments,
+    // we get metadata from the real-time generated TsFile instead of using a 
fixed parameter setting.
     TsFileMetaData metaData = reader.readFileMetadata();
-    System.out.println("[Metadata]");
-    List<TsDeviceMetadataIndex> deviceMetadataIndexList = 
metaData.getDeviceMap().values().stream()
-        .sorted((x, y) -> (int) (x.getOffset() - 
y.getOffset())).collect(Collectors.toList());
-    for (TsDeviceMetadataIndex index : deviceMetadataIndexList) {
-      TsDeviceMetadata deviceMetadata = reader.readTsDeviceMetaData(index);
-      List<ChunkGroupMetaData> chunkGroupMetaDataList = 
deviceMetadata.getChunkGroupMetaDataList();
-      for (ChunkGroupMetaData chunkGroupMetaData : chunkGroupMetaDataList) {
-        LOG.info("t[Device]Device:{}", chunkGroupMetaData.getDeviceID());
-        LOG.info("chunkGroupMetaData.start:{}, end:{}",
-            chunkGroupMetaData.getStartOffsetOfChunkGroup(),
-            chunkGroupMetaData.getEndOffsetOfChunkGroup());
-
-//        for (ChunkMetaData chunkMetadata : 
chunkGroupMetaData.getChunkMetaDataList()) {
-//          System.out.println("\t\tMeasurement:" + 
chunkMetadata.getMeasurementUid());
-//          System.out.println("\t\tFile offset:" + 
chunkMetadata.getOffsetOfChunkHeader());
-//        }
+    TsDeviceMetadataIndex d1MetadataIndex = metaData.getDeviceMap().get("d1");
+    TsDeviceMetadataIndex d2MetadataIndex = metaData.getDeviceMap().get("d2");
+
+    TsDeviceMetadata d1Metadata = reader.readTsDeviceMetaData(d1MetadataIndex);
+    List<ChunkGroupMetaData> d1chunkGroupMetaDataList = 
d1Metadata.getChunkGroupMetaDataList();
+    for (ChunkGroupMetaData chunkGroupMetaData : d1chunkGroupMetaDataList) {
+      // get a series of [startOffsetOfChunkGroup, endOffsetOfChunkGroup] from 
the chunkGroupMetaData of d1
+      long[] chunkGroupMetaDataOffset = new long[2];
+      chunkGroupMetaDataOffset[0] = 
chunkGroupMetaData.getStartOffsetOfChunkGroup();
+      chunkGroupMetaDataOffset[1] = 
chunkGroupMetaData.getEndOffsetOfChunkGroup();
+      d1chunkGroupMetaDataOffsetList.add(chunkGroupMetaDataOffset);
+
+      List<ChunkMetaData> chunkMetaDataList = 
chunkGroupMetaData.getChunkMetaDataList();
+      for (ChunkMetaData chunkMetaData : chunkMetaDataList) {
+        if (chunkMetaData.getMeasurementUid().equals("s6")) {
+          // get a series of [startTime, endTime] of d1.s6 from the 
chunkGroupMetaData of d1
+          d1s6timeRangeList
+              .add(new TimeRange(chunkMetaData.getStartTime(), 
chunkMetaData.getEndTime()));
+        }
+      }
+    }
 
+    TsDeviceMetadata d2Metadata = reader.readTsDeviceMetaData(d2MetadataIndex);
+    List<ChunkGroupMetaData> d2chunkGroupMetaDataList = 
d2Metadata.getChunkGroupMetaDataList();
+    for (ChunkGroupMetaData chunkGroupMetaData : d2chunkGroupMetaDataList) {
+      // get a series of [startOffsetOfChunkGroup, endOffsetOfChunkGroup] from 
the chunkGroupMetaData of d2
+      long[] chunkGroupMetaDataOffset = new long[2];
+      chunkGroupMetaDataOffset[0] = 
chunkGroupMetaData.getStartOffsetOfChunkGroup();
+      chunkGroupMetaDataOffset[1] = 
chunkGroupMetaData.getEndOffsetOfChunkGroup();
+      d2chunkGroupMetaDataOffsetList.add(chunkGroupMetaDataOffset);
+
+      List<ChunkMetaData> chunkMetaDataList = 
chunkGroupMetaData.getChunkMetaDataList();
+      for (ChunkMetaData chunkMetaData : chunkMetaDataList) {
+        if (chunkMetaData.getMeasurementUid().equals("s1")) {
+          // get a series of [startTime, endTime] of d2.s1 from the 
chunkGroupMetaData of d1
+          d2s1timeRangeList
+              .add(new TimeRange(chunkMetaData.getStartTime(), 
chunkMetaData.getEndTime()));
+        }
       }
     }
   }
@@ -88,107 +125,116 @@ public class ReadInPartitionTest {
   }
 
   @Test
-  public void test1() throws IOException {
-    HashMap<String, Long> params = new HashMap<>();
-    params.put(QueryConstant.PARTITION_START_OFFSET, 0L);
-    params.put(QueryConstant.PARTITION_END_OFFSET, 603242L);
-
-    roTsFile = new ReadOnlyTsFile(reader, params);
-
+  public void test0() throws IOException {
     ArrayList<Path> paths = new ArrayList<>();
     paths.add(new Path("d1.s6"));
     paths.add(new Path("d2.s1"));
     QueryExpression queryExpression = QueryExpression.create(paths, null);
 
-    QueryDataSet queryDataSet = roTsFile.query(queryExpression);
+    QueryDataSet queryDataSet = roTsFile.query(queryExpression, 0L, 0L);
 
-    int cnt = 0;
-    while (queryDataSet.hasNext()) {
-      RowRecord r = queryDataSet.next();
-      cnt++;
-      if (cnt == 1) {
-        Assert.assertEquals("1480562618000\t0.0\t1", r.toString());
-      } else if (cnt == 9352) {
-        Assert.assertEquals("1480562664755\tnull\t467551", r.toString());
-      }
-    }
-    Assert.assertEquals(9353, cnt);
+    // test the transformed expression
+    Assert.assertNull(queryExpression.getExpression());
+
+    // test the equivalence of the query result
+    Assert.assertFalse(queryDataSet.hasNext());
   }
 
   @Test
-  public void test2() throws IOException {
-    HashMap<String, Long> params = new HashMap<>();
-    params.put(QueryConstant.PARTITION_START_OFFSET, 603242L);
-    params.put(QueryConstant.PARTITION_END_OFFSET, 993790L);
-
-    roTsFile = new ReadOnlyTsFile(reader, params);
-
+  public void test1() throws IOException, QueryFilterOptimizationException {
     ArrayList<Path> paths = new ArrayList<>();
     paths.add(new Path("d1.s6"));
     paths.add(new Path("d2.s1"));
     QueryExpression queryExpression = QueryExpression.create(paths, null);
 
-    QueryDataSet queryDataSet = roTsFile.query(queryExpression);
+    QueryDataSet queryDataSet = roTsFile
+        .query(queryExpression, d1chunkGroupMetaDataOffsetList.get(0)[0],
+            d1chunkGroupMetaDataOffsetList.get(0)[1]);
+    // get the transformed expression
+    IExpression transformedExpression = queryExpression.getExpression();
 
-    int cnt = 0;
-    while (queryDataSet.hasNext()) {
+    // test the transformed expression
+    Assert.assertEquals(ExpressionType.GLOBAL_TIME, 
transformedExpression.getType());
+
+    IExpression expectedTimeExpression = 
d1s6timeRangeList.get(0).getExpression();
+    String expected = 
ExpressionOptimizer.getInstance().optimize(expectedTimeExpression,
+        queryExpression.getSelectedSeries()).toString();
+    Assert.assertEquals(expected, transformedExpression.toString());
+
+    // test the equivalence of the query result:
+    QueryDataSet queryDataSet_eq = roTsFile.query(queryExpression);
+    while (queryDataSet.hasNext() && queryDataSet_eq.hasNext()) {
       RowRecord r = queryDataSet.next();
-      cnt++;
-      if (cnt == 1) {
-        Assert.assertEquals("1480562664765\tnull\t467651", r.toString());
-      }
+      RowRecord r2 = queryDataSet_eq.next();
+      Assert.assertEquals(r2.toString(), r.toString());
     }
-    Assert.assertEquals(1, cnt);
+    Assert.assertEquals(queryDataSet_eq.hasNext(), queryDataSet.hasNext());
   }
 
   @Test
-  public void test3() throws IOException {
-    HashMap<String, Long> params = new HashMap<>();
-    params.put(QueryConstant.PARTITION_START_OFFSET, 993790L);
-    params.put(QueryConstant.PARTITION_END_OFFSET, 1608255L);
-
-    roTsFile = new ReadOnlyTsFile(reader, params);
-
+  public void test2() throws IOException, QueryFilterOptimizationException {
     ArrayList<Path> paths = new ArrayList<>();
     paths.add(new Path("d1.s6"));
     paths.add(new Path("d2.s1"));
-    QueryExpression queryExpression = QueryExpression.create(paths, null);
-
-    QueryDataSet queryDataSet = roTsFile.query(queryExpression);
-
-    int cnt = 0;
-    while (queryDataSet.hasNext()) {
+    IExpression expression = new GlobalTimeExpression(TimeFilter.gt(50L));
+    QueryExpression queryExpression = QueryExpression.create(paths, 
expression);
+
+    QueryDataSet queryDataSet = roTsFile
+        .query(queryExpression, d1chunkGroupMetaDataOffsetList.get(0)[0],
+            d1chunkGroupMetaDataOffsetList.get(0)[1]);
+    // get the transformed expression
+    IExpression transformedExpression = queryExpression.getExpression();
+
+    // test the transformed expression
+    Assert.assertEquals(ExpressionType.GLOBAL_TIME, 
transformedExpression.getType());
+
+    IExpression expectedTimeExpression = BinaryExpression
+        .and(expression, d1s6timeRangeList.get(0).getExpression());
+    String expected = 
ExpressionOptimizer.getInstance().optimize(expectedTimeExpression,
+        queryExpression.getSelectedSeries()).toString();
+    Assert.assertEquals(expected, transformedExpression.toString());
+
+    // test the equivalence of the query result:
+    QueryDataSet queryDataSet_eq = roTsFile.query(queryExpression);
+    while (queryDataSet.hasNext() && queryDataSet_eq.hasNext()) {
       RowRecord r = queryDataSet.next();
-      cnt++;
-      if (cnt == 1) {
-        Assert.assertEquals("1480562664770\t5196.0\t467701", r.toString());
-      } else if (cnt == 9936) {
-        Assert.assertEquals("1480562711445\tnull\t934451", r.toString());
-      }
+      RowRecord r2 = queryDataSet_eq.next();
+      Assert.assertEquals(r2.toString(), r.toString());
     }
-    Assert.assertEquals(9337, cnt);
+    Assert.assertEquals(queryDataSet_eq.hasNext(), queryDataSet.hasNext());
   }
 
   @Test
-  public void test4() throws IOException {
-    HashMap<String, Long> params = new HashMap<>();
-    params.put(QueryConstant.PARTITION_START_OFFSET, 1608255L);
-    params.put(QueryConstant.PARTITION_END_OFFSET, 1999353L);
-
-    roTsFile = new ReadOnlyTsFile(reader, params);
-
+  public void test3() throws IOException, QueryFilterOptimizationException {
     ArrayList<Path> paths = new ArrayList<>();
     paths.add(new Path("d1.s6"));
     paths.add(new Path("d2.s1"));
-    QueryExpression queryExpression = QueryExpression.create(paths, null);
-
-    QueryDataSet queryDataSet = roTsFile.query(queryExpression);
-
-    int cnt = 0;
-    while (queryDataSet.hasNext()) {
+    Filter filter = ValueFilter.gt(10L);
+    IExpression expression = new SingleSeriesExpression(new Path("d1.s3"), 
filter);
+    QueryExpression queryExpression = QueryExpression.create(paths, 
expression);
+
+    QueryDataSet queryDataSet = roTsFile
+        .query(queryExpression, d1chunkGroupMetaDataOffsetList.get(0)[0],
+            d1chunkGroupMetaDataOffsetList.get(0)[1]);
+    // get the transformed expression
+    IExpression transformedExpression = queryExpression.getExpression();
+
+    // test the transformed expression
+    Assert.assertEquals(ExpressionType.SERIES, 
transformedExpression.getType());
+
+    IExpression expectedTimeExpression = BinaryExpression
+        .and(expression, d1s6timeRangeList.get(0).getExpression());
+    String expected = 
ExpressionOptimizer.getInstance().optimize(expectedTimeExpression,
+        queryExpression.getSelectedSeries()).toString();
+    Assert.assertEquals(expected, transformedExpression.toString());
+
+    // test the equivalence of the query result:
+    QueryDataSet queryDataSet_eq = roTsFile.query(queryExpression);
+    while (queryDataSet.hasNext() && queryDataSet_eq.hasNext()) {
       RowRecord r = queryDataSet.next();
-      cnt++;
+      RowRecord r2 = queryDataSet_eq.next();
+      Assert.assertEquals(r2.toString(), r.toString());
     }
-    Assert.assertEquals(0, cnt);
+    Assert.assertEquals(queryDataSet_eq.hasNext(), queryDataSet.hasNext());
   }
 }
diff --git 
a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/TimeRangeTest.java 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/TimeRangeTest.java
index 12e2d64..514f8e2 100644
--- 
a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/TimeRangeTest.java
+++ 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/TimeRangeTest.java
@@ -21,11 +21,29 @@ package org.apache.iotdb.tsfile.read.common;
 import static org.junit.Assert.assertEquals;
 
 import java.util.ArrayList;
+import org.junit.Assert;
 import org.junit.Test;
 
 public class TimeRangeTest {
 
   @Test
+  public void mergeTest() {
+    ArrayList<TimeRange> unionCandidates = new ArrayList<>();
+    unionCandidates.add(new TimeRange(0L, 10L));
+    unionCandidates.add(new TimeRange(3L, 10L));
+    unionCandidates.add(new TimeRange(100L, 200L));
+    unionCandidates.add(new TimeRange(20L, 30L));
+    unionCandidates.add(new TimeRange(5L, 6L));
+
+    ArrayList<TimeRange> res = new 
ArrayList<>(TimeRange.sortAndMerge(unionCandidates));
+
+    Assert.assertEquals(3, res.size());
+    Assert.assertEquals("[ 0 : 10 ]", res.get(0).toString());
+    Assert.assertEquals("[ 20 : 30 ]", res.get(1).toString());
+    Assert.assertEquals("[ 100 : 200 ]", res.get(2).toString());
+  }
+
+  @Test
   /*
      no overlap
    */
@@ -197,7 +215,7 @@ public class TimeRangeTest {
 
   @Test
   /*
-     more than one timerange in previous ranges
+     more than one time ranges in previous ranges
    */
   public void getRemainsTest9() {
     TimeRange r = new TimeRange(1, 10);
@@ -222,4 +240,26 @@ public class TimeRangeTest {
     assertEquals(remainRanges.get(2).getRightClose(), true);
   }
 
+  @Test
+  /*
+     more than one time ranges in previous ranges
+   */
+  public void getRemainsTest10() {
+    TimeRange r = new TimeRange(1, 10);
+
+    ArrayList<TimeRange> prevRanges = new ArrayList<>();
+    prevRanges.add(new TimeRange(3, 4));
+    prevRanges.add(new TimeRange(11, 20));
+
+    ArrayList<TimeRange> remainRanges = new 
ArrayList<>(r.getRemains(prevRanges));
+    assertEquals(2, remainRanges.size());
+    assertEquals(remainRanges.get(0).getMin(), 1);
+    assertEquals(remainRanges.get(0).getMax(), 3);
+    assertEquals(remainRanges.get(0).getLeftClose(), true);
+    assertEquals(remainRanges.get(0).getRightClose(), false);
+    assertEquals(remainRanges.get(1).getMin(), 4);
+    assertEquals(remainRanges.get(1).getMax(), 11); // NOTE here is the 
technical detail.
+    assertEquals(remainRanges.get(1).getLeftClose(), false);
+    assertEquals(remainRanges.get(1).getRightClose(), false);
+  }
 }
diff --git 
a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerierByFileImplTest.java
 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerierByFileImplTest.java
index be5037b..fe1882d 100644
--- 
a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerierByFileImplTest.java
+++ 
b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/controller/MetadataQuerierByFileImplTest.java
@@ -20,15 +20,16 @@ package org.apache.iotdb.tsfile.read.controller;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import org.apache.iotdb.tsfile.common.constant.QueryConstant;
 import org.apache.iotdb.tsfile.exception.write.WriteProcessException;
+import org.apache.iotdb.tsfile.file.metadata.ChunkGroupMetaData;
 import org.apache.iotdb.tsfile.file.metadata.ChunkMetaData;
+import org.apache.iotdb.tsfile.file.metadata.TsDeviceMetadata;
+import org.apache.iotdb.tsfile.file.metadata.TsDeviceMetadataIndex;
+import org.apache.iotdb.tsfile.file.metadata.TsFileMetaData;
 import org.apache.iotdb.tsfile.read.TsFileSequenceReader;
 import org.apache.iotdb.tsfile.read.common.Path;
 import org.apache.iotdb.tsfile.read.common.TimeRange;
-import org.apache.iotdb.tsfile.read.controller.MetadataQuerier.LoadMode;
 import org.apache.iotdb.tsfile.utils.TsFileGeneratorForTest;
 import org.junit.After;
 import org.junit.Assert;
@@ -38,86 +39,127 @@ import org.junit.Test;
 public class MetadataQuerierByFileImplTest {
 
   private static final String FILE_PATH = 
TsFileGeneratorForTest.outputDataFile;
-  private TsFileSequenceReader fileReader;
+  private TsFileSequenceReader reader;
+  private ArrayList<TimeRange> d1s6timeRangeList = new ArrayList<>();
+  private ArrayList<TimeRange> d2s1timeRangeList = new ArrayList<>();
+  private ArrayList<long[]> d1chunkGroupMetaDataOffsetList = new ArrayList<>();
+  private ArrayList<long[]> d2chunkGroupMetaDataOffsetList = new ArrayList<>();
 
   @Before
   public void before() throws InterruptedException, WriteProcessException, 
IOException {
     TsFileGeneratorForTest.generateFile(1000000, 1024 * 1024, 10000);
+    reader = new TsFileSequenceReader(FILE_PATH);
+
+    // Because the size of the generated chunkGroupMetaData may differ under 
different test environments,
+    // we get metadata from the real-time generated TsFile instead of using a 
fixed parameter setting.
+    TsFileMetaData metaData = reader.readFileMetadata();
+    TsDeviceMetadataIndex d1MetadataIndex = metaData.getDeviceMap().get("d1");
+    TsDeviceMetadataIndex d2MetadataIndex = metaData.getDeviceMap().get("d2");
+
+    TsDeviceMetadata d1Metadata = reader.readTsDeviceMetaData(d1MetadataIndex);
+    List<ChunkGroupMetaData> d1chunkGroupMetaDataList = 
d1Metadata.getChunkGroupMetaDataList();
+    for (ChunkGroupMetaData chunkGroupMetaData : d1chunkGroupMetaDataList) {
+      // get a series of [startOffsetOfChunkGroup, endOffsetOfChunkGroup] from 
the chunkGroupMetaData of d1
+      long[] chunkGroupMetaDataOffset = new long[2];
+      chunkGroupMetaDataOffset[0] = 
chunkGroupMetaData.getStartOffsetOfChunkGroup();
+      chunkGroupMetaDataOffset[1] = 
chunkGroupMetaData.getEndOffsetOfChunkGroup();
+      d1chunkGroupMetaDataOffsetList.add(chunkGroupMetaDataOffset);
+
+      List<ChunkMetaData> chunkMetaDataList = 
chunkGroupMetaData.getChunkMetaDataList();
+      for (ChunkMetaData chunkMetaData : chunkMetaDataList) {
+        if (chunkMetaData.getMeasurementUid().equals("s6")) {
+          // get a series of [startTime, endTime] of d1.s6 from the 
chunkGroupMetaData of d1
+          d1s6timeRangeList
+              .add(new TimeRange(chunkMetaData.getStartTime(), 
chunkMetaData.getEndTime()));
+        }
+      }
+    }
+
+    TsDeviceMetadata d2Metadata = reader.readTsDeviceMetaData(d2MetadataIndex);
+    List<ChunkGroupMetaData> d2chunkGroupMetaDataList = 
d2Metadata.getChunkGroupMetaDataList();
+    for (ChunkGroupMetaData chunkGroupMetaData : d2chunkGroupMetaDataList) {
+      // get a series of [startOffsetOfChunkGroup, endOffsetOfChunkGroup] from 
the chunkGroupMetaData of d2
+      long[] chunkGroupMetaDataOffset = new long[2];
+      chunkGroupMetaDataOffset[0] = 
chunkGroupMetaData.getStartOffsetOfChunkGroup();
+      chunkGroupMetaDataOffset[1] = 
chunkGroupMetaData.getEndOffsetOfChunkGroup();
+      d2chunkGroupMetaDataOffsetList.add(chunkGroupMetaDataOffset);
+
+      List<ChunkMetaData> chunkMetaDataList = 
chunkGroupMetaData.getChunkMetaDataList();
+      for (ChunkMetaData chunkMetaData : chunkMetaDataList) {
+        if (chunkMetaData.getMeasurementUid().equals("s1")) {
+          // get a series of [startTime, endTime] of d2.s1 from the 
chunkGroupMetaData of d1
+          d2s1timeRangeList
+              .add(new TimeRange(chunkMetaData.getStartTime(), 
chunkMetaData.getEndTime()));
+        }
+      }
+    }
   }
 
   @After
   public void after() throws IOException {
-    fileReader.close();
+    reader.close();
     TsFileGeneratorForTest.after();
   }
 
   @Test
-  public void test_NoPartition() throws IOException {
-    fileReader = new TsFileSequenceReader(FILE_PATH);
-
-    MetadataQuerierByFileImpl metadataQuerierByFile = new 
MetadataQuerierByFileImpl(fileReader);
-    List<ChunkMetaData> chunkMetaDataList = metadataQuerierByFile
-        .getChunkMetaDataList(new Path("d2.s1"));
-    for (ChunkMetaData chunkMetaData : chunkMetaDataList) {
-      Assert.assertEquals("s1", chunkMetaData.getMeasurementUid());
-    }
-  }
+  public void testEmpty() throws IOException {
+    MetadataQuerierByFileImpl metadataQuerierByFile = new 
MetadataQuerierByFileImpl(reader);
 
-  @Test
-  public void test_InPartition() throws IOException {
-    fileReader = new TsFileSequenceReader(FILE_PATH);
-
-    HashMap<String, Long> params = new HashMap<>();
-    params.put(QueryConstant.PARTITION_START_OFFSET, 12L);
-    params.put(QueryConstant.PARTITION_END_OFFSET, 1999000L);
-
-    MetadataQuerierByFileImpl metadataQuerierByFile = new 
MetadataQuerierByFileImpl(fileReader,
-        params);
-    List<ChunkMetaData> chunkMetaDataList = metadataQuerierByFile
-        .getChunkMetaDataList(new Path("d2.s1"));
-    Assert.assertEquals(2, chunkMetaDataList.size());
-    // NOTE different systems have different exact split points.
-    // Therefore specific start and end time are not tested.
+    ArrayList<Path> paths = new ArrayList<>();
+    paths.add(new Path("d1.s6"));
+    paths.add(new Path("d2.s1"));
+
+    ArrayList<TimeRange> resTimeRanges = new ArrayList<>(metadataQuerierByFile
+        .convertSpace2TimePartition(paths, 0L, 0L));
+
+    Assert.assertEquals(0, resTimeRanges.size());
   }
 
   @Test
-  public void test_getTimeRangeInPartition() throws IOException {
-    fileReader = new TsFileSequenceReader(FILE_PATH);
-
-    HashMap<String, Long> params = new HashMap<>();
-    params.put(QueryConstant.PARTITION_START_OFFSET, 1608255L);
-    params.put(QueryConstant.PARTITION_END_OFFSET, 3006837L);
+  public void testConvert1() throws IOException {
+    MetadataQuerierByFileImpl metadataQuerierByFile = new 
MetadataQuerierByFileImpl(reader);
 
-    MetadataQuerierByFileImpl metadataQuerierByFile = new 
MetadataQuerierByFileImpl(fileReader,
-        params);
     ArrayList<Path> paths = new ArrayList<>();
     paths.add(new Path("d1.s6"));
     paths.add(new Path("d2.s1"));
-    ArrayList<TimeRange> timeRanges = metadataQuerierByFile
-        .getTimeRangeInOrPrev(paths, LoadMode.InPartition);
-    Assert.assertEquals(2, timeRanges.size());
-    // NOTE different systems have different exact split points.
-    // Therefore specific start and end time are not tested.
+
+    long spacePartitionStartPos = d1chunkGroupMetaDataOffsetList.get(0)[0];
+    long spacePartitionEndPos = d1chunkGroupMetaDataOffsetList.get(1)[1];
+    ArrayList<TimeRange> resTimeRanges = new ArrayList<>(metadataQuerierByFile
+        .convertSpace2TimePartition(paths, spacePartitionStartPos, 
spacePartitionEndPos));
+
+    ArrayList<TimeRange> unionCandidates = new ArrayList<>();
+    unionCandidates.add(d1s6timeRangeList.get(0));
+    unionCandidates.add(d2s1timeRangeList.get(0));
+    unionCandidates.add(d1s6timeRangeList.get(1));
+    ArrayList<TimeRange> expectedRanges = new 
ArrayList<>(TimeRange.sortAndMerge(unionCandidates));
+
+    Assert.assertEquals(expectedRanges.toString(), resTimeRanges.toString());
   }
 
   @Test
-  public void test_getTimeRangePrePartition() throws IOException {
-    fileReader = new TsFileSequenceReader(FILE_PATH);
+  public void testConvert2() throws IOException {
+    MetadataQuerierByFileImpl metadataQuerierByFile = new 
MetadataQuerierByFileImpl(reader);
 
-    HashMap<String, Long> params = new HashMap<>();
-    params.put(QueryConstant.PARTITION_START_OFFSET, 1608255L);
-    params.put(QueryConstant.PARTITION_END_OFFSET, 3006837L);
-
-    MetadataQuerierByFileImpl metadataQuerierByFile = new 
MetadataQuerierByFileImpl(fileReader,
-        params);
     ArrayList<Path> paths = new ArrayList<>();
     paths.add(new Path("d1.s6"));
     paths.add(new Path("d2.s1"));
-    ArrayList<TimeRange> timeRanges = metadataQuerierByFile
-        .getTimeRangeInOrPrev(paths, LoadMode.PrevPartition);
-    Assert.assertEquals(2, timeRanges.size());
-    // NOTE different systems have different exact split points.
-    // Therefore specific start and end time are not tested.
-  }
 
+    long spacePartitionStartPos = d2chunkGroupMetaDataOffsetList.get(0)[0];
+    long spacePartitionEndPos = d2chunkGroupMetaDataOffsetList.get(0)[1];
+    ArrayList<TimeRange> resTimeRanges = new ArrayList<>(metadataQuerierByFile
+        .convertSpace2TimePartition(paths, spacePartitionStartPos, 
spacePartitionEndPos));
+
+    ArrayList<TimeRange> inCandidates = new ArrayList<>();
+    ArrayList<TimeRange> beforeCandidates = new ArrayList<>();
+    inCandidates.add(d2s1timeRangeList.get(0));
+    beforeCandidates.add(d1s6timeRangeList.get(0));
+    ArrayList<TimeRange> expectedRanges = new ArrayList<>();
+    for (TimeRange in : inCandidates) {
+      ArrayList<TimeRange> remains = new 
ArrayList<>(in.getRemains(beforeCandidates));
+      expectedRanges.addAll(remains);
+    }
+
+    Assert.assertEquals(expectedRanges.toString(), resTimeRanges.toString());
+  }
 }

Reply via email to