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 cbd9c99  Add ResultSet Constrction Doc (#1114)
cbd9c99 is described below

commit cbd9c994e8c9ff0bfa5bee8f217122efdab47379
Author: Xiangwei Wei <[email protected]>
AuthorDate: Wed May 6 16:53:37 2020 +0800

    Add ResultSet Constrction Doc (#1114)
    
    * Add Query Header Constrction Doc
---
 .../2-QueryEngine/4-ResultSetConstruction.md       | 177 +++++++++++++++++++++
 .../2-QueryEngine/4-ResultSetConstruction.md       | 175 ++++++++++++++++++++
 .../iotdb/db/qp/physical/crud/QueryPlan.java       |   2 +-
 .../iotdb/db/qp/strategy/PhysicalGenerator.java    |  49 +++---
 .../org/apache/iotdb/db/service/TSServiceImpl.java |   4 +-
 site/src/main/.vuepress/config.js                  |   6 +-
 6 files changed, 379 insertions(+), 34 deletions(-)

diff --git a/docs/SystemDesign/2-QueryEngine/4-ResultSetConstruction.md 
b/docs/SystemDesign/2-QueryEngine/4-ResultSetConstruction.md
new file mode 100644
index 0000000..681c641
--- /dev/null
+++ b/docs/SystemDesign/2-QueryEngine/4-ResultSetConstruction.md
@@ -0,0 +1,177 @@
+<!--
+
+    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.
+
+-->
+
+# Query Result Set Construction
+
+## Introduction
+
+This text mainly introduces the construction principle of query result set, 
including three parts: header construction, generating non repeated result set 
and restoring complete result set.
+
+## Header construction
+
+Next Introduce the first part: including the result set header construction 
way of RawDataQuery, AlignByDeviceQuery and LastQuery. e.g. DownsamplingQuery 
and FillQuery etc. will be introduced as subquery in these three queries.
+
+### Raw data query
+
+The header construction logic of raw data query is in the 
`getWideQueryHeaders()` method.
+
+- org.apache.iotdb.db.service.TSServiceImpl.getWideQueryHeaders
+
+For the construction of each header, you need to provide the column name and 
the corresponding data type of the column.
+
+- Ordinary raw data query (including FillQuery) only needs to obtain 
timeseries paths **not de duplicated** from the physical query plan, which are 
the column names, and use these paths to obtain the corresponding data type to 
generate the result set header.
+
+- If the raw data query contains aggregate functions (including AggregateQuery 
and DownsamplingQuery), the time column will be ignored and **aggregate 
function and timeseries path will be combined to form the column name**, and 
when obtaining the data type, the type of aggregate function will prevail. For 
example, `root.sg.d1.s1` is of FLOAT type, while `count(root.sg.d1.s1)` shoule 
be of LONG type.
+
+Next, we will give examples:
+
+Assuming that all timeseries in the query below exist, the result set headers 
generated by the following two queries are respectively:
+
+SQL1:`SELECT s1, s2 FROM root.sg.d1;`  ->
+
+| Time | root.sg.d1.s1 | root.sg.d1.s2 |
+| ---- | ------------- | ------------- |
+|      |               |               |
+
+SQL2:`SELECT count(s1), max_time(s1) FROM root.sg.d1;` ->
+
+| count(root.sg.d1.s1) | max_time(root.sg.d1.s2) |
+| -------------------- | ----------------------- |
+|                      |                         |
+
+### Align by device query
+
+The header construction logic of align by device query is in the 
`getAlignByDeviceQueryHeaders()` method.
+
+- org.apache.iotdb.db.service.TSServiceImpl.getAlignByDeviceQueryHeaders
+
+The result set construction of the AlignByDeviceQuery depends on the list of 
**measurements not deduplicated** generated in the physical query plan. For a 
brief introduction, the measurements list is a list generated by the suffix 
path (including wildcards) in the SELECT clause, including three types, namely 
constant, exist and nonexist. For details, please refer to [Align by device 
query](/SystemDesign/5-DataQuery/8-AlignByDeviceQuery.html)
+
+Since the structure of relation table is used for AlignByDeviceQuery, the 
device column is added to the header first, and its corresponding data type is 
text type.
+
+Then get the list of measurements, take each measurement as the column name, 
and get the corresponding data type depending on the measurements' type. If it 
is a Constant or NonExist type, the data type is directly set as Text type. If 
it's an Exist type, then get the corresponding type from the 
`measurementdatatypemap`, which is stored in the physical query plan.
+
+Note that in the case of an AggregationQuery, the measuremnts in the list here 
will contain aggregate functions, so they can be processed together.
+
+Next, we will give an example:
+
+Assuming there are two timeseries now: `root.sg.d1.s1`, `root.sg.d1.s2`, then 
the result set header generated by the query below is:
+
+SQL:`SELECT '111', s1, s2, *, s5 FROM root.sg.d1 ALIGN BY DEVICE;`
+
+-> measurements list: ['111', s1, s2, s1, s2, s5]
+
+-> header
+
+| Time | Device | 111 | s1  | s2  | s1  | s2  | s5  |
+| ---- | ------ | --- | --- | --- | --- | --- | --- |
+|      |        |     |     |     |     |     |     |
+
+### LastQuery
+
+The header construction logic of last query is in the static method 
`LAST_RESP`.
+
+- org.apache.iotdb.db.service.StaticResps.LAST_RESP
+
+The latest data query calculates the result with the largest timestamp of the 
timeseries to be queried and displays it in three columns: time, timeseries and 
the corresponding value.
+
+Next, we will give an example:
+
+Assuming there are two timeseries now: `root.sg.d1.s1`, `root.sg.d1.s2`, then 
the result set header generated by the query below is:
+
+SQL:`SELECT last s1, s2 FROM root.sg.d1;`
+
+| Time | timeseries    | value |
+| ---- | ------------- | ----- |
+| ...  | root.sg.d1.s1 | ...   |
+| ...  | root.sg.d1.s2 | ...   |
+
+## Generate non repeated result set
+
+Unlike the header construction, we do not need to query duplicate data when 
executing the physical query plan. For example, for the query `select s1, s1 
from root.sg.d1`, we only need to query the value of the timeseries 
`root.sg.d1.s1` once. Therefore, after the header is constructed, we need to 
generate a non repeated result set on the server side.
+
+In addition to AlignByDeviceQuery, the deduplication logic of **RawDataQuery, 
AggregateQuery, LastQuery** etc. is in the `duplicate()` method.
+
+- org.apache.iotdb.db.qp.strategy.PhysicalGenerator.deduplicate()
+
+The deduplication logic is relatively simple: first, get the path not 
deduplicated from the query plan, and then create a `Set` structure to 
deduplicate during traversal.
+
+It is worth noting that the timeseries paths of the query are sorted by device 
before the RawDataQuery and AggregateQuery are deduplicated, in order to reduce 
I/O and deserialization operations and speed up the query.
+Here an additional data structure `pathToIndex` is calculated to record the 
position of each path in the query.
+
+Because only one set of data needs to be calculated for the LastQuery, there 
is no need to sort the paths. Its `pathToIndex` will be null.
+
+The deduplication logic of **AlignByDeviceQuery** is in the  
`hasNextWithoutConstraint()` method of its result set.
+
+- 
org.apache.iotdb.db.query.dataset.AlignByDeviceDataSet.hasNextWithoutConstraint()
+
+Because AlignByDeviceQuery need to organize their query plans by device, each 
device query may not have the same path, and it is allowed to contain constant 
columns and nonexistent timeseries, so it cannot simply be deduplicated with 
other queries. Deduplication requires **removing not only the repeated 
timeseries path, but also the constant columns appearing in the query and the 
timeseries that do not exist in the current device**.
+The implementation logic can be referred to [Align by device 
query](/SystemDesign/5-DataQuery/8-AlignByDeviceQuery.html).
+
+After the deduplication paths in the query plan are completed, the query 
executor of IoTDB can be called to execute the query and obtain the 
deduplication result set.
+
+## Restore the complete result set
+
+The construction of headers and the generation of non-repeating result set 
above are done on the server side and then returned to the client side. After 
the client restores the non-repeating result set based on the original header, 
the complete result set is presented to the user. To distinguish two result 
sets, they are called **query result set** and **final result set** 
respectively.
+
+To explain simply, an example is given first:
+
+SQL: `SELECT s2, s1, s2 FROM root.sg.d1;`
+
+The list of column names `columnNameList` in the header which has been 
calculated using the steps above is (Timestamp will be contained later if need 
be):
+
+`[root.sg.d1.s2, root.sg.d1.s1, root.sg.d1.s2].`
+
+The position of the timeseries path in the query `pathToIndex` is(sorted by 
device):
+
+`root.sg.d1.s1 -> 0, root.sg.d1.s2 -> 1;`
+
+Then query result set is:
+
+| Time | root.sg.d1.s1 | root.sg.d1.s2 |
+| ---- | ------------- | ------------- |
+|      |               |               |
+
+To restore the final result set, we need to construct a mapping set 
`columnOrdinalMap` with the column name to its position in the query result 
set, which is aimed at fetching the corresponding result of a column from the 
query result set. This part of logic is completed in the constructor of the new 
result set `IoTDBQueryResultSet`.
+
+- org.apache.iotdb.jdbc.AbstractIoTDBResultSet.AbstractIoTDBResultSet()
+
+In order to construct metadata information in final result set, a complete 
column name list is needed. The `columnnamelist` given above does not contain a 
timestamp. Therefore, it's necessary to determine whether a timestamp needs to 
be printed. If so, add the `Time` column to the header to form a complete 
header.
+
+The complete header in example is:
+
+| Time | root.sg.d1.s2 | root.sg.d1.s1 | root.sg.d1.s2 |
+| ---- | ------------- | ------------- | ------------- |
+|      |               |               |               |
+
+Then calculating `columnordinalmap`, judge whether to print a timestamp first. 
If so, record the timestamp as the first column.
+
+Then traverse the column name list in the header and then check whether 
`columnnameindex` is initialized. This field comes from `pathtoindex` 
calculated during deduplication, which records the location of each timeseries 
path in the query. If it is initialized, record the position + 2 as its 
position in the result set. If not, record the positions in traversal order, 
which is consistent with the query order in server side.
+
+The `columnOrdinalMap` in example is:
+
+`Time -> 1, root.sg.d1.s2 -> 3, root.sg.d1.s1 -> 2`
+
+Next, traverse the column names in the header, and then fill in the complete 
result set according to the mapping set. Its logic is in the `cacheResult()` 
method.
+
+- org.apache.iotdb.cli.AbstractCli.cacheResult()
+
+For example, the second column in the final result set is `root.sg.d1.s2`, 
therefore the result of the third column will be taken as its value from the 
result set. Repeat the process to fill the results of each row until there is 
no next result in the query result set or the maximum number of output rows is 
reached.
diff --git a/docs/zh/SystemDesign/2-QueryEngine/4-ResultSetConstruction.md 
b/docs/zh/SystemDesign/2-QueryEngine/4-ResultSetConstruction.md
new file mode 100644
index 0000000..da79fab
--- /dev/null
+++ b/docs/zh/SystemDesign/2-QueryEngine/4-ResultSetConstruction.md
@@ -0,0 +1,175 @@
+<!--
+
+    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.
+
+-->
+
+# 查询结果集构造
+
+## 内容简介
+
+本文主要介绍查询结果集的构造原理,包括三部分:表头构造、生成非重复的结果集、还原完整结果集。
+
+## 表头构造
+
+接下来介绍第一部分,包括原始数据查询(RawDataQuery)、按设备对齐查询(AlignByDeviceQuery)、最新数据查询(LastQuery)三种查询的结果集表头构造方法。如降频聚合查询、空值填充查询等查询将作为子查询在该三类查询中讲解。
+
+### 原始数据查询
+
+原始数据查询的结果集表头构造逻辑主要在 `getWideQueryHeaders()` 方法中。
+
+- org.apache.iotdb.db.service.TSServiceImpl.getWideQueryHeaders
+
+对于每个结果集表头的构造,需要提供列名及该列对应的数据类型。
+
+- 
普通原始数据查询(包括空值填充查询)只需要从物理查询计划中取得**未去重**的时间序列路径,该时间序列路径即作为列名,并使用该路径取得时间序列对应的数据类型,即可生成结果集表头。
+
+- 
而如果原始数据查询中包含聚合函数(包括一般聚合查询和降频聚合查询),将忽略时间列并使用**聚合函数和时间序列路径共同构成列名**,且取得数据类型时将以聚合函数的类型为准,如
 `root.sg.d1.s1` 是 FLOAT 类型,而 `count(root.sg.d1.s1)` 应为 LONG 类型。
+
+接下来将举例说明:
+
+假设查询中出现的所有时间序列均存在,则以下两个查询生成的结果集表头分别为:
+
+SQL1:`SELECT s1, s1, s2 FROM root.sg.d1;`  ->
+
+| Time | root.sg.d1.s1 | root.sg.d1.s1 | root.sg.d1.s2 |
+| ---- | ------------- | ------------- | ------------- |
+|      |               |               |               |
+
+SQL2:`SELECT count(s1), max_time(s1) FROM root.sg.d1;` ->
+
+| count(root.sg.d1.s1) | max_time(root.sg.d1.s1) |
+| -------------------- | ----------------------- |
+|                      |                         |
+
+### 按设备对齐查询
+
+原始数据查询的结果集表头构造逻辑主要在 `getAlignByDeviceQueryHeaders()` 方法中。
+
+- org.apache.iotdb.db.service.TSServiceImpl.getAlignByDeviceQueryHeaders
+
+按设备对齐查询的结果集构造依赖于物理查询计划中生成的**未去重**的度量(Measurements)列表。在此作简单介绍,度量列表是由 SELECT 
子句中的后缀路径(包括通配符)生成的列表,其中共有三种类型,分别为常量(Constant)、存在的时间序列(Exist)以及不存在的时间序列(NonExist)。详细可以参考
 [Align by device query](/SystemDesign/5-DataQuery/8-AlignByDeviceQuery.html)
+
+由于按设备对齐查询采用关系表结构,因此首先在表头中加入设备列,其对应的数据类型为文本类型。
+
+然后取得度量列表,将每个度量作为列名,并根据度量类型选择相应的数据类型。如果是常量或不存在的时间序列类型,则直接设数据类型为文本类型;如果是存在的时间序列,则根据物理查询计划中存储的
 `measurementDataTypeMap` 取得该度量对应的数据类型。
+
+注意如果是聚合查询,此处度量列表中的度量会包含聚合函数,因此可以一并处理。
+
+接下来举例说明:
+
+假设目前共有 `root.sg.d1.s1`, `root.sg.d1.s2` 两条时间序列,则以下查询语句生成的表头为:
+
+SQL:`SELECT '111', s1, s2, *, s5 FROM root.sg.d1 ALIGN BY DEVICE;`
+
+-> 度量列表 ['111', s1, s2, s1, s2, s5]
+
+-> 表头
+
+| Time | Device | 111 | s1  | s2  | s1  | s2  | s5  |
+| ---- | ------ | --- | --- | --- | --- | --- | --- |
+|      |        |     |     |     |     |     |     |
+
+### 最新数据查询
+
+最新数据查询的结果集表头构造逻辑主要在静态方法 `LAST_RESP` 中。
+
+- org.apache.iotdb.db.service.StaticResps.LAST_RESP
+
+最新数据查询计算出需要查询的时间序列具有最大时间戳的结果并以时间(Time)、时间序列(timeseries)及值(value)三列进行展示。
+
+接下来举例说明:
+
+假设目前共有 `root.sg.d1.s1`, `root.sg.d1.s2` 两条时间序列,则以下查询语句生成的表头为:
+
+SQL:`SELECT last s1, s2 FROM root.sg.d1;`
+
+| Time | timeseries    | value |
+| ---- | ------------- | ----- |
+| ...  | root.sg.d1.s1 | ...   |
+| ...  | root.sg.d1.s2 | ...   |
+
+## 生成非重复结果集
+
+与表头构造不同,我们不需要在执行物理查询计划时查询重复的数据,例如对于查询 `SELECT s1, s1 FROM 
root.sg.d1`,我们只需要查询一次时间序列 `root.sg.d1.s1` 的值即可。因此在表头构造完成后,我们需要在服务器端先生成非重复的结果集。
+
+除按设备对齐查询外,**原始数据查询、聚合查询、最新数据查询** 等查询的去重逻辑均在 `deduplicate()` 方法中。
+
+- org.apache.iotdb.db.qp.strategy.PhysicalGenerator.deduplicate()
+
+去重逻辑比较简单:首先从查询计划中取得未去重的路径,然后在遍历时创建一个 Set 集合用于去重即可。
+
+值得注意的是:在原始数据查询和聚合查询去重前,先**将查询的时间序列路径按设备进行排序**,目的是为了查询时减少 I/O 
和反序列化操作,加快查询速度。此处额外计算出一个数据结构 `pathToIndex` 用于记录排序后每个路径在查询中的位置。
+
+由于最新数据查询只需要计算一组数据,因此不需要对路径进行排序,其 `pathToIndex` 为 null。
+
+**按设备对齐查询**的去重逻辑在其结果集的 `hasNextWithoutConstraint()` 方法中。
+
+- 
org.apache.iotdb.db.query.dataset.AlignByDeviceDataSet.hasNextWithoutConstraint()
+
+由于按设备对齐查询需要按设备依次组织其查询计划,每个设备查询的路径未必相同,且允许包含常量列以及不存在的时间序列,因此不能简单地与其他查询一起去重。去重时**不仅需要去除重复查询的时间序列路径,还需要去除查询中出现的常量列以及当前设备中不存在的时间序列**。实现方法可以参考
 [Align by device query](/SystemDesign/5-DataQuery/8-AlignByDeviceQuery.html).
+
+在查询计划中的去重路径构造完成后,即可调用 IoTDB 的查询执行器来执行查询,并得到去重后的结果集。
+
+## 还原完整结果集
+
+以上构造表头和生成非重复结果集均在服务器端完成,然后返回给客户端。客户端根据原始的表头信息对非重复的结果集进行还原后,展示给用户。为了区分两个结果集,下面分别称之为**查询结果集**和**最终结果集**。
+
+为了方便讲解,首先给出一个简单的示例:
+
+SQL: `SELECT s2, s1, s2 FROM root.sg.d1;`
+
+通过上面的步骤已经计算出表头中的列名列表 `columnNameList` 为(时间戳将之后计算):
+
+`[root.sg.d1.s2, root.sg.d1.s1, root.sg.d1.s2].`
+
+时间序列路径在查询中的位置 `pathToIndex` 为(按设备排过序):
+
+`root.sg.d1.s1 -> 0, root.sg.d1.s2 -> 1;`
+
+则查询结果集为:
+
+| Time | root.sg.d1.s1 | root.sg.d1.s2 |
+| ---- | ------------- | ------------- |
+|      |               |               |
+
+为了还原最终结果集,需要构造一个列名到其在查询结果集中位置的映射集 
`columnOrdinalMap`,方便从查询结果集中取出某一列对应的结果,该部分逻辑在新建最终结果集 `IoTDBQueryResultSet` 
的构造函数内完成.
+
+- org.apache.iotdb.jdbc.AbstractIoTDBResultSet.AbstractIoTDBResultSet()
+
+为了构造最终结果集中的元数据信息,需要构造完整的列名列表,由于上面给出的 `columnNameList` 
中不包含时间戳,因此,如果需要打印时间戳则在表头中加入 `Time` 列构成完整的表头。
+
+示例中为:
+
+| Time | root.sg.d1.s2 | root.sg.d1.s1 | root.sg.d1.s2 |
+| ---- | ------------- | ------------- | ------------- |
+|      |               |               |               |
+
+接下来计算 `columnOrdinalMap` ,首先判断是否需要打印时间戳,如果需要则将时间戳记录为第一列。
+
+然后遍历表头中的列名列表,并检查 `columnNameIndex` 是否被初始化,该字段来源于去重时计算的 
`pathToIndex`,记录了每个时间序列路径在查询中的位置。如果被初始化,则其中的位置 + 2 
即为在结果集中的位置;若未被初始化,则按遍历顺序记录位置即可,该顺序与服务器端去重后的查询顺序保持一致。
+
+示例中 `columnOrdinalMap` 应为:
+
+`Time -> 1, root.sg.d1.s2 -> 3, root.sg.d1.s1 -> 2`
+
+接下来遍历完整的表头信息,然后根据该映射集填充完整的结果集即可,其逻辑在 `cacheResult()` 方法中。
+
+- org.apache.iotdb.cli.AbstractCli.cacheResult()
+
+例如最终结果集表头中第二列为 
`root.sg.d1.s2`,则从查询结果集中取出第三列的结果作为其值。重复该过程,填满每一行的结果,直到查询结果集中没有下一结果或达到最大输出行数。
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/QueryPlan.java 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/QueryPlan.java
index c809b23..58b4c13 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/QueryPlan.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/QueryPlan.java
@@ -92,7 +92,7 @@ public abstract class QueryPlan extends PhysicalPlan {
     alignByTime = align;
   }
 
-  public void addColumn(String columnName, Integer index) {
+  public void addPathToIndex(String columnName, Integer index) {
     pathToIndex.put(columnName, index);
   }
 
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 72e66b4..d64413a 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
@@ -515,35 +515,11 @@ public class PhysicalGenerator {
     List<TSDataType> dataTypes = getSeriesTypes(paths);
     queryPlan.setDataTypes(dataTypes);
 
-    List<Pair<Path, Integer>> indexedPaths = new ArrayList<>();
-    for (int i = 0; i < paths.size(); i++) {
-      indexedPaths.add(new Pair<>(paths.get(i), i));
-    }
-    indexedPaths.sort(Comparator.comparing(pair -> pair.left));
-
     // deduplicate from here
     if (queryPlan instanceof AlignByDevicePlan) {
       return;
     }
-    if (queryPlan instanceof AggregationPlan) {
-      AggregationPlan aggregationPlan = (AggregationPlan) queryPlan;
-      List<String> aggregations = aggregationPlan.getAggregations();
-      Set<String> columnSet = new HashSet<>();
-      int index = 0;
-      for (Pair<Path, Integer> indexedPath : indexedPaths) {
-        String column =
-            aggregations.get(indexedPath.right) + "(" + 
indexedPath.left.toString() + ")";
-        if (!columnSet.contains(column)) {
-          aggregationPlan.addDeduplicatedPaths(indexedPath.left);
-          TSDataType seriesType = dataTypes.get(indexedPath.right);
-          aggregationPlan.addDeduplicatedDataTypes(seriesType);
-          
aggregationPlan.addDeduplicatedAggregations(aggregations.get(indexedPath.right));
-          columnSet.add(column);
-          aggregationPlan.addColumn(column, index++);
-        }
-      }
-      return;
-    }
+
     RawDataQueryPlan rawDataQueryPlan = (RawDataQueryPlan) queryPlan;
     Set<String> columnSet = new HashSet<>();
     // if it's a last query, no need to sort by device
@@ -561,16 +537,31 @@ public class PhysicalGenerator {
       return;
     }
 
+    // sort path by device
+    List<Pair<Path, Integer>> indexedPaths = new ArrayList<>();
+    for (int i = 0; i < paths.size(); i++) {
+      indexedPaths.add(new Pair<>(paths.get(i), i));
+    }
+    indexedPaths.sort(Comparator.comparing(pair -> pair.left));
+
     int index = 0;
     for (Pair<Path, Integer> indexedPath : indexedPaths) {
-      Path path = indexedPath.left;
-      String column = path.toString();
+      String column;
+      if (queryPlan instanceof AggregationPlan) {
+        column = queryPlan.getAggregations().get(indexedPath.right) + "(" + 
indexedPath.left.toString() + ")";
+      } else {
+        column = indexedPath.left.toString();
+      }
       if (!columnSet.contains(column)) {
         TSDataType seriesType = dataTypes.get(indexedPath.right);
-        rawDataQueryPlan.addDeduplicatedPaths(path);
+        rawDataQueryPlan.addDeduplicatedPaths(indexedPath.left);
         rawDataQueryPlan.addDeduplicatedDataTypes(seriesType);
         columnSet.add(column);
-        rawDataQueryPlan.addColumn(column, index++);
+        rawDataQueryPlan.addPathToIndex(column, index++);
+        if (queryPlan instanceof AggregationPlan){
+          ((AggregationPlan) queryPlan)
+              
.addDeduplicatedAggregations(queryPlan.getAggregations().get(indexedPath.right));
+        }
       }
     }
   }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java 
b/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
index f8a36c2..b774ba3 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
@@ -761,7 +761,7 @@ public class TSServiceImpl implements TSIService.Iface, 
ServerContext {
     deduplicatedColumnsType.add(TSDataType.TEXT); // the DEVICE column of 
ALIGN_BY_DEVICE result
 
     Set<String> deduplicatedMeasurements = new LinkedHashSet<>();
-    Map<String, TSDataType> checker = plan.getMeasurementDataTypeMap();
+    Map<String, TSDataType> measurementDataTypeMap = 
plan.getMeasurementDataTypeMap();
 
     // build column header with constant and non exist column and deduplication
     List<String> measurements = plan.getMeasurements();
@@ -770,7 +770,7 @@ public class TSServiceImpl implements TSIService.Iface, 
ServerContext {
       TSDataType type = null;
       switch (measurementTypeMap.get(measurement)) {
         case Exist:
-          type = checker.get(measurement);
+          type = measurementDataTypeMap.get(measurement);
           break;
         case NonExist:
         case Constant:
diff --git a/site/src/main/.vuepress/config.js 
b/site/src/main/.vuepress/config.js
index eff5ed5..c058a9d 100644
--- a/site/src/main/.vuepress/config.js
+++ b/site/src/main/.vuepress/config.js
@@ -413,7 +413,8 @@ var config = {
                                                children: [
                                                        
['2-QueryEngine/1-QueryEngine','QueryEngine'],
                                                        
['2-QueryEngine/2-Planner','Planner'],
-                                                       
['2-QueryEngine/3-PlanExecutor','PlanExecutor']
+                                                       
['2-QueryEngine/3-PlanExecutor','PlanExecutor'],
+                                                       
['2-QueryEngine/4-ResultSetConstruction','ResultSetConstruction']
                                                ]
                                        },
                                        {
@@ -827,7 +828,8 @@ var config = {
                                                children: [
                                                        
['2-QueryEngine/1-QueryEngine','查询引擎'],
                                                        
['2-QueryEngine/2-Planner','执行计划生成器'],
-                                                       
['2-QueryEngine/3-PlanExecutor','计划执行器']
+                                                       
['2-QueryEngine/3-PlanExecutor','计划执行器'],
+                                                       
['2-QueryEngine/4-ResultSetConstruction','结果集构造']
                                                ]
                                        },
                                        {

Reply via email to