You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by qi...@apache.org on 2019/09/03 11:45:07 UTC

[incubator-iotdb] branch master updated: [IoTDB-174]Add interfaces for querying device or timeseries number

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 c4b39b7  [IoTDB-174]Add interfaces for querying device or timeseries number
     new 8ead902  Merge pull request #352 from jack870131/query_device_and_node_num
c4b39b7 is described below

commit c4b39b7996be0f82c5419bd05f70fb6d2d6d6b60
Author: jack870131 <ja...@outlook.com>
AuthorDate: Tue Aug 20 16:26:19 2019 +0800

    [IoTDB-174]Add interfaces for querying device or timeseries number
---
 .../UserGuide/6-JDBC API/1-JDBC API.md             |  12 ++
 .../main/java/org/apache/iotdb/jdbc/Constant.java  |   9 ++
 .../apache/iotdb/jdbc/IoTDBDatabaseMetadata.java   |  91 ++++++++++++--
 .../apache/iotdb/jdbc/IoTDBMetadataResultSet.java  | 135 +++++++++++++++------
 .../java/org/apache/iotdb/jdbc/IoTDBStatement.java |  31 +++++
 .../iotdb/jdbc/IoTDBDatabaseMetadataTest.java      | 134 ++++++++++++++++++--
 .../java/org/apache/iotdb/db/metadata/MGraph.java  |   4 +
 .../org/apache/iotdb/db/metadata/MManager.java     |  32 ++---
 .../java/org/apache/iotdb/db/metadata/MNode.java   |  10 ++
 .../java/org/apache/iotdb/db/metadata/MTree.java   |  41 +++++--
 .../org/apache/iotdb/db/service/TSServiceImpl.java |  32 +++++
 .../iotdb/db/integration/IoTDBMetadataFetchIT.java |   4 +-
 service-rpc/src/main/thrift/rpc.thrift             |   3 +
 13 files changed, 460 insertions(+), 78 deletions(-)

diff --git a/docs/Documentation/UserGuide/6-JDBC API/1-JDBC API.md b/docs/Documentation/UserGuide/6-JDBC API/1-JDBC API.md
index d1c865f..e895a06 100644
--- a/docs/Documentation/UserGuide/6-JDBC API/1-JDBC API.md	
+++ b/docs/Documentation/UserGuide/6-JDBC API/1-JDBC API.md	
@@ -63,6 +63,7 @@ Requires that you include the packages containing the JDBC classes needed for da
 import java.sql.*;
 import org.apache.iotdb.jdbc.IoTDBSQLException;
 
+public class JDBCExample {
   /**
    * Before executing a SQL statement with a Statement object, you need to create a Statement object using the createStatement() method of the Connection object.
    * After creating a Statement object, you can use its execute() method to execute a SQL statement
@@ -98,6 +99,16 @@ import org.apache.iotdb.jdbc.IoTDBSQLException;
     //Show time series
     statement.execute("SHOW TIMESERIES root.demo");
     outputResult(statement.getResultSet());
+    //Count time series
+    statement.execute("COUNT TIMESERIES root");
+    outputResult(statement.getResultSet());
+    //Count nodes at the given level
+    statement.execute("COUNT NODES root LEVEL=3");
+    outputResult(statement.getResultSet());
+    //Count timeseries group by each node at the given level
+    statement.execute("COUNT TIMESERIES root GROUP BY LEVEL=3");
+    outputResult(statement.getResultSet());
+    
 
     //Execute insert statements in batch
     statement.addBatch("insert into root.demo(timestamp,s0) values(1,1);");
@@ -187,4 +198,5 @@ import org.apache.iotdb.jdbc.IoTDBSQLException;
       System.out.println("--------------------------\n");
     }
   }
+}
 ```
\ No newline at end of file
diff --git a/jdbc/src/main/java/org/apache/iotdb/jdbc/Constant.java b/jdbc/src/main/java/org/apache/iotdb/jdbc/Constant.java
index faa79c7..0ae18cb 100644
--- a/jdbc/src/main/java/org/apache/iotdb/jdbc/Constant.java
+++ b/jdbc/src/main/java/org/apache/iotdb/jdbc/Constant.java
@@ -32,6 +32,12 @@ public class Constant {
 
   public static final String GLOBAL_SHOW_TIMESERIES_REQ = "SHOW_TIMESERIES";
 
+  public static final String GLOBAL_COUNT_TIMESERIES_REQ = "COUNT_TIMESERIES";
+
+  public static final String GLOBAL_COUNT_NODE_TIMESERIES_REQ = "COUNT_NODE_TIMESERIES";
+
+  public static final String GLOBAL_COUNT_NODES_REQ = "COUNT_NODES";
+
   public static final String GLOBAL_SHOW_STORAGE_GROUP_REQ = "SHOW_STORAGE_GROUP";
 
   public static final String GLOBAL_COLUMNS_REQ = "ALL_COLUMNS";
@@ -41,4 +47,7 @@ public class Constant {
   public static final String CATALOG_TIMESERIES = "ts";
   public static final String CATALOG_STORAGE_GROUP = "sg";
   public static final String CATALOG_DEVICE = "delta";
+  public static final String COUNT_TIMESERIES = "cntts";
+  public static final String COUNT_NODE_TIMESERIES = "cnttsbg";
+  public static final String COUNT_NODES = "cntnode";
 }
diff --git a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadata.java b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadata.java
index 36d01c2..1344c2d 100644
--- a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadata.java
+++ b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadata.java
@@ -18,11 +18,7 @@
  */
 package org.apache.iotdb.jdbc;
 
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.RowIdLifetime;
-import java.sql.SQLException;
+import java.sql.*;
 import java.util.List;
 import java.util.Set;
 import org.apache.iotdb.rpc.IoTDBRPCException;
@@ -89,7 +85,7 @@ public class IoTDBDatabaseMetadata implements DatabaseMetaData {
           } catch (IoTDBRPCException e) {
             throw new IoTDBSQLException(e.getMessage());
           }
-          return new IoTDBMetadataResultSet(resp.getColumnsList(), null, null);
+          return new IoTDBMetadataResultSet(resp.getColumnsList(), IoTDBMetadataResultSet.MetadataType.COLUMN);
         } catch (TException e) {
           throw new TException("Conncetion error when fetching column metadata", e);
         }
@@ -103,7 +99,7 @@ public class IoTDBDatabaseMetadata implements DatabaseMetaData {
           } catch (IoTDBRPCException e) {
             throw new IoTDBSQLException(e.getMessage());
           }
-          return new IoTDBMetadataResultSet(resp.getColumnsList(), null, null);
+          return new IoTDBMetadataResultSet(resp.getColumnsList(), IoTDBMetadataResultSet.MetadataType.COLUMN);
         } catch (TException e) {
           throw new TException("Conncetion error when fetching delta object metadata", e);
         }
@@ -117,7 +113,7 @@ public class IoTDBDatabaseMetadata implements DatabaseMetaData {
             throw new IoTDBSQLException(e.getMessage());
           }
           Set<String> showStorageGroup = resp.getShowStorageGroups();
-          return new IoTDBMetadataResultSet(null, showStorageGroup, null);
+          return new IoTDBMetadataResultSet(showStorageGroup, IoTDBMetadataResultSet.MetadataType.STORAGE_GROUP);
         } catch (TException e) {
           throw new TException("Conncetion error when fetching storage group metadata", e);
         }
@@ -132,10 +128,87 @@ public class IoTDBDatabaseMetadata implements DatabaseMetaData {
             throw new IoTDBSQLException(e.getMessage());
           }
           List<List<String>> showTimeseriesList = resp.getShowTimeseriesList();
-          return new IoTDBMetadataResultSet(null, null, showTimeseriesList);
+          return new IoTDBMetadataResultSet(showTimeseriesList, IoTDBMetadataResultSet.MetadataType.TIMESERIES);
         } catch (TException e) {
           throw new TException("Conncetion error when fetching timeseries metadata", e);
         }
+      case Constant.COUNT_TIMESERIES:
+        req = new TSFetchMetadataReq(Constant.GLOBAL_COUNT_TIMESERIES_REQ);
+        req.setColumnPath(schemaPattern);
+        try {
+          TSFetchMetadataResp resp = client.fetchMetadata(req);
+          try {
+            RpcUtils.verifySuccess(resp.getStatus());
+          } catch (IoTDBRPCException e) {
+            throw new IoTDBSQLException(e.getMessage());
+          }
+          return new IoTDBMetadataResultSet(resp.getColumnsList().size(), IoTDBMetadataResultSet.MetadataType.COUNT_TIMESERIES);
+        } catch (TException e) {
+          throw new TException("Connection error when fetching timeseries metadata", e);
+        }
+      default:
+        throw new SQLException(catalog + " is not supported. Please refer to the user guide"
+            + " for more details.");
+    }
+  }
+
+  public ResultSet getNodes(String catalog, String schemaPattern, String columnPattern,
+      String devicePattern, String nodeLevel) throws SQLException {
+    try {
+      return getNodesFunc(catalog, nodeLevel);
+    } catch (TException e) {
+      boolean flag = connection.reconnect();
+      this.client = connection.client;
+      if (flag) {
+        try {
+          return getNodesFunc(catalog, nodeLevel);
+        } catch (TException e2) {
+          throw new SQLException(String.format("Fail to get columns catalog=%s, schemaPattern=%s,"
+                  + " columnPattern=%s, devicePattern=%s, nodeLevel=%s after reconnecting."
+                  + " please check server status",
+              catalog, schemaPattern, columnPattern, devicePattern, nodeLevel));
+        }
+      } else {
+        throw new SQLException(String.format(
+            "Fail to reconnect to server when getting columns catalog=%s, schemaPattern=%s,"
+                + " columnPattern=%s, devicePattern=%s, nodeLevel=%s after reconnecting. "
+                + "please check server status",
+            catalog, schemaPattern, columnPattern, devicePattern, nodeLevel));
+      }
+    }
+  }
+
+  private ResultSet getNodesFunc(String catalog, String nodeLevel) throws TException, SQLException {
+    TSFetchMetadataReq req;
+    switch (catalog) {
+      case Constant.COUNT_NODES:
+        req = new TSFetchMetadataReq(Constant.GLOBAL_COUNT_NODES_REQ);
+        req.setNodeLevel(nodeLevel);
+        try {
+          TSFetchMetadataResp resp = client.fetchMetadata(req);
+          try {
+            RpcUtils.verifySuccess(resp.getStatus());
+          } catch (IoTDBRPCException e) {
+            throw new IoTDBSQLException(e.getMessage());
+          }
+          return new IoTDBMetadataResultSet(resp.getNodesList().size(), IoTDBMetadataResultSet.MetadataType.COUNT_NODES);
+        } catch (TException e) {
+          throw new TException("Conncetion error when fetching node metadata", e);
+        }
+      case Constant.COUNT_NODE_TIMESERIES:
+        req = new TSFetchMetadataReq(Constant.GLOBAL_COUNT_NODE_TIMESERIES_REQ);
+        req.setNodeLevel(nodeLevel);
+        try {
+          TSFetchMetadataResp resp = client.fetchMetadata(req);
+          try {
+            RpcUtils.verifySuccess(resp.getStatus());
+          } catch (IoTDBRPCException e) {
+            throw new IoTDBSQLException(e.getMessage());
+          }
+          return new IoTDBMetadataResultSet(resp.getNodeTimeseriesNum(), IoTDBMetadataResultSet.MetadataType.COUNT_NODE_TIMESERIES);
+        } catch (TException e) {
+          throw new TException("Conncetion error when fetching node metadata", e);
+        }
       default:
         throw new SQLException(catalog + " is not supported. Please refer to the user guide"
             + " for more details.");
diff --git a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBMetadataResultSet.java b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBMetadataResultSet.java
index a0ad409..c52d327 100644
--- a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBMetadataResultSet.java
+++ b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBMetadataResultSet.java
@@ -20,19 +20,17 @@ package org.apache.iotdb.jdbc;
 
 import java.math.BigDecimal;
 import java.sql.Date;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.SQLWarning;
-import java.sql.Statement;
-import java.sql.Time;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
+import java.sql.*;
+import java.util.*;
 
 public class IoTDBMetadataResultSet extends IoTDBQueryResultSet {
 
   public static final String GET_STRING_COLUMN = "COLUMN";
   public static final String GET_STRING_STORAGE_GROUP = "STORAGE_GROUP";
+  public static final String GET_STRING_TIMESERIES_NUM = "TIMESERIES_NUM";
+  public static final String GET_STRING_NODES_NUM = "NODE_NUM";
+  public static final String GET_STRING_NODE_PATH = "NODE_PATH";
+  public static final String GET_STRING_NODE_TIMESERIES_NUM = "NODE_TIMESERIES_NUM";
   public static final String GET_STRING_TIMESERIES_NAME = "Timeseries";
   public static final String GET_STRING_TIMESERIES_STORAGE_GROUP = "Storage Group";
   public static final String GET_STRING_TIMESERIES_DATATYPE = "DataType";
@@ -42,6 +40,13 @@ public class IoTDBMetadataResultSet extends IoTDBQueryResultSet {
   private String currentColumn;
   private String currentStorageGroup;
   private List<String> currentTimeseries;
+  private List<String> timeseriesNumList;
+  private List<String> nodesNumList;
+  private Map<String, String> nodeTimeseriesNumMap;
+  private String timeseriesNum;
+  private String nodesNum;
+  private String currentNode;
+  private String currentNodeTimeseriesNum;
   // for display
   private int colCount; // the number of columns for show
   private String[] showLabels; // headers for show
@@ -51,26 +56,52 @@ public class IoTDBMetadataResultSet extends IoTDBQueryResultSet {
   /**
    * Constructor used for the result of DatabaseMetadata.getColumns()
    */
-  public IoTDBMetadataResultSet(List<String> columns, Set<String> storageGroupSet,
-      List<List<String>> showTimeseriesList) throws SQLException {
-    if (columns != null) {
-      type = MetadataType.COLUMN;
-      colCount = 1;
-      showLabels = new String[]{"Column"};
-      columnItr = columns.iterator();
-    } else if (storageGroupSet != null) {
-      type = MetadataType.STORAGE_GROUP;
-      colCount = 1;
-      showLabels = new String[]{"Storage Group"};
-      columnItr = storageGroupSet.iterator();
-    } else if (showTimeseriesList != null) {
-      type = MetadataType.TIMESERIES;
-      colCount = 4;
-      showLabels = new String[]{GET_STRING_TIMESERIES_NAME, GET_STRING_TIMESERIES_STORAGE_GROUP,
+  public IoTDBMetadataResultSet(Object object, MetadataType type) throws SQLException {
+    this.type = type;
+    switch (type) {
+      case COLUMN:
+        List<String> columns = (List<String>) object;
+        colCount = 1;
+        showLabels = new String[]{"column"};
+        columnItr = columns.iterator();
+        break;
+      case STORAGE_GROUP:
+        Set<String> storageGroupSet = (Set<String>) object;
+        colCount = 1;
+        showLabels = new String[]{"Storage Group"};
+        columnItr = storageGroupSet.iterator();
+        break;
+      case TIMESERIES:
+        List<List<String>> showTimeseriesList = (List<List<String>>) object;
+        colCount = 4;
+        showLabels = new String[]{GET_STRING_TIMESERIES_NAME, GET_STRING_TIMESERIES_STORAGE_GROUP,
           GET_STRING_TIMESERIES_DATATYPE, GET_STRING_TIMESERIES_ENCODING};
-      columnItr = showTimeseriesList.iterator();
-    } else {
-      throw new SQLException("TsfileMetadataResultSet constructor is wrongly used.");
+        columnItr = showTimeseriesList.iterator();
+        break;
+      case COUNT_TIMESERIES:
+        String tsNum = (String) object.toString();
+        timeseriesNumList = new ArrayList<>();
+        timeseriesNumList.add(tsNum);
+        colCount = 1;
+        showLabels = new String[]{"count"};
+        columnItr = timeseriesNumList.iterator();
+        break;
+      case COUNT_NODES:
+        String ndNum = (String) object.toString();
+        nodesNumList = new ArrayList<>();
+        nodesNumList.add(ndNum);
+        colCount = 1;
+        showLabels = new String[]{"count"};
+        columnItr = nodesNumList.iterator();
+        break;
+      case COUNT_NODE_TIMESERIES:
+        nodeTimeseriesNumMap = (Map<String, String>) object;
+        colCount = 2;
+        showLabels = new String[]{"column", "count"};
+        columnItr = nodeTimeseriesNumMap.entrySet().iterator();
+        break;
+      default:
+        throw new SQLException("TsfileMetadataResultSet constructor is wrongly used.");
     }
   }
 
@@ -198,12 +229,29 @@ public class IoTDBMetadataResultSet extends IoTDBQueryResultSet {
   public boolean next() throws SQLException {
     boolean hasNext = columnItr.hasNext();
     if (hasNext) {
-      if (type == MetadataType.STORAGE_GROUP) {
-        currentStorageGroup = (String) columnItr.next();
-      } else if (type == MetadataType.COLUMN) {
-        currentColumn = (String) columnItr.next();
-      } else {
-        currentTimeseries = (List<String>) columnItr.next();
+      switch (type) {
+        case STORAGE_GROUP:
+          currentStorageGroup = (String) columnItr.next();
+          break;
+        case TIMESERIES:
+          currentTimeseries = (List<String>) columnItr.next();
+          break;
+        case COLUMN:
+          currentColumn = (String) columnItr.next();
+          break;
+        case COUNT_TIMESERIES:
+          timeseriesNum = (String) columnItr.next();
+          break;
+        case COUNT_NODES:
+          nodesNum = (String) columnItr.next();
+          break;
+        case COUNT_NODE_TIMESERIES:
+          Map.Entry pair = (Map.Entry) columnItr.next();
+          currentNode = (String) pair.getKey();
+          currentNodeTimeseriesNum = (String) pair.getValue();
+          break;
+        default:
+          break;
       }
     }
     return hasNext;
@@ -252,6 +300,17 @@ public class IoTDBMetadataResultSet extends IoTDBQueryResultSet {
           return getString(GET_STRING_COLUMN);
         }
         break;
+      case COUNT_TIMESERIES:
+        if (columnIndex == 1) {
+          return getString(GET_STRING_TIMESERIES_NUM);
+        }
+        break;
+      case COUNT_NODES:
+        if (columnIndex == 1) {
+          return getString(GET_STRING_NODES_NUM);
+        }
+      case COUNT_NODE_TIMESERIES:
+        return columnIndex == 1 ? getString(GET_STRING_NODE_PATH) : getString(GET_STRING_NODE_TIMESERIES_NUM);
       default:
         break;
     }
@@ -274,6 +333,14 @@ public class IoTDBMetadataResultSet extends IoTDBQueryResultSet {
         return currentTimeseries.get(3);
       case GET_STRING_COLUMN:
         return currentColumn;
+      case GET_STRING_TIMESERIES_NUM:
+        return timeseriesNum;
+      case GET_STRING_NODES_NUM:
+        return nodesNum;
+      case GET_STRING_NODE_PATH:
+        return currentNode;
+      case GET_STRING_NODE_TIMESERIES_NUM:
+        return currentNodeTimeseriesNum;
       default:
         break;
     }
@@ -311,6 +378,6 @@ public class IoTDBMetadataResultSet extends IoTDBQueryResultSet {
   }
 
   public enum MetadataType {
-    STORAGE_GROUP, TIMESERIES, COLUMN
+    STORAGE_GROUP, TIMESERIES, COLUMN, COUNT_TIMESERIES, COUNT_NODES, COUNT_NODE_TIMESERIES
   }
 }
diff --git a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java
index b728831..35b2361 100644
--- a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java
+++ b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java
@@ -41,6 +41,8 @@ public class IoTDBStatement implements Statement {
 
   private static final String SHOW_TIMESERIES_COMMAND_LOWERCASE = "show timeseries";
   private static final String SHOW_STORAGE_GROUP_COMMAND_LOWERCASE = "show storage group";
+  private static final String COUNT_TIMESERIES_COMMAND_LOWERCASE = "count timeseries";
+  private static final String COUNT_NODES_COMMAND_LOWERCASE = "count nodes";
   private static final String METHOD_NOT_SUPPORTED_STRING = "Method not supported";
 
   ZoneId zoneId;
@@ -236,6 +238,35 @@ public class IoTDBStatement implements Statement {
       DatabaseMetaData databaseMetaData = connection.getMetaData();
       resultSet = databaseMetaData.getColumns(Constant.CATALOG_STORAGE_GROUP, null, null, null);
       return true;
+    } else if (sqlToLowerCase.startsWith(COUNT_TIMESERIES_COMMAND_LOWERCASE)) {
+      String[] cmdSplited = sqlToLowerCase.split("\\s+", 4);
+      if (cmdSplited.length != 3 && !(cmdSplited.length == 4 && cmdSplited[3].startsWith("group by level"))) {
+        throw new SQLException(
+                "Error format of \'COUNT TIMESERIES <PATH>\' or \'COUNT TIMESERIES <PATH> GROUP BY LEVEL=<INTEGER>\'");
+      }
+      if (cmdSplited.length == 3) {
+        String path = cmdSplited[2];
+        DatabaseMetaData databaseMetaData = connection.getMetaData();
+        resultSet = databaseMetaData.getColumns(Constant.COUNT_TIMESERIES, path, null, null);
+        return true;
+      } else {
+        String path = cmdSplited[2];
+        String level = cmdSplited[3].replaceAll(" ", "").substring(13);
+        IoTDBDatabaseMetadata databaseMetadata = (IoTDBDatabaseMetadata) connection.getMetaData();
+        resultSet = databaseMetadata.getNodes(Constant.COUNT_NODE_TIMESERIES, path, null, null, level);
+        return true;
+      }
+    } else if (sqlToLowerCase.startsWith(COUNT_NODES_COMMAND_LOWERCASE)) {
+      String[] cmdSplited = sql.split("\\s+", 4);
+      if (cmdSplited.length != 4 && !(cmdSplited[3].startsWith("level"))) {
+        throw new SQLException("Error format of \'COUNT NODES LEVEL=<INTEGER>\'");
+      } else {
+        String path = cmdSplited[2];
+        String level = cmdSplited[3].replaceAll(" ", "").substring(6);
+        IoTDBDatabaseMetadata databaseMetaData = (IoTDBDatabaseMetadata) connection.getMetaData();
+        resultSet = databaseMetaData.getNodes(Constant.COUNT_NODES, path, null, null, level);
+        return true;
+      }
     } else {
       TSExecuteStatementReq execReq = new TSExecuteStatementReq(sessionHandle, sql);
       TSExecuteStatementResp execResp = client.executeStatement(execReq);
diff --git a/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadataTest.java b/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadataTest.java
index 2fdb406..f0a20a1 100644
--- a/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadataTest.java
+++ b/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadataTest.java
@@ -21,16 +21,11 @@ package org.apache.iotdb.jdbc;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.when;
-
 import java.sql.DatabaseMetaData;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
+import java.util.*;
 import org.apache.iotdb.rpc.TSStatusType;
 import org.apache.iotdb.service.rpc.thrift.*;
 import org.junit.Assert;
@@ -94,8 +89,124 @@ public class IoTDBDatabaseMetadataTest {
     when(fetchMetadataResp.getColumnsList()).thenReturn(columnList);
 
     String standard =
-        "Column,\n" + "root.vehicle.d0.s0,\n" + "root.vehicle.d0.s1,\n" + "root.vehicle.d0.s2,\n";
-    try (ResultSet resultSet = databaseMetaData.getColumns(Constant.CATALOG_COLUMN, "root", null, null)) {
+        "column,\n" + "root.vehicle.d0.s0,\n" + "root.vehicle.d0.s1,\n" + "root.vehicle.d0.s2,\n";
+    try {
+      ResultSet resultSet = databaseMetaData.getColumns(Constant.CATALOG_COLUMN, "root", null, null);
+      ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+      int colCount = resultSetMetaData.getColumnCount();
+      StringBuilder resultStr = new StringBuilder();
+      for (int i = 1; i < colCount + 1; i++) {
+        resultStr.append(resultSetMetaData.getColumnName(i)).append(",");
+      }
+      resultStr.append("\n");
+      while (resultSet.next()) {
+        for (int i = 1; i <= colCount; i++) {
+          resultStr.append(resultSet.getString(i)).append(",");
+        }
+        resultStr.append("\n");
+      }
+      Assert.assertEquals(resultStr.toString(), standard);
+    } catch (SQLException e) {
+      System.out.println(e);
+    }
+  }
+
+  /**
+   * get the timeseries number under a given path
+   */
+  @SuppressWarnings("resource")
+  @Test
+  public void CountTimeseries() throws Exception {
+    List<String> columnList = new ArrayList<>();
+    columnList.add("root.vehicle.d0.s0");
+    columnList.add("root.vehicle.d0.s1");
+    columnList.add("root.vehicle.d0.s2");
+
+    when(fetchMetadataResp.getColumnsList()).thenReturn(columnList);
+
+    String standard = "count,\n" + "3,\n";
+    try {
+      ResultSet resultSet = databaseMetaData.getColumns(Constant.COUNT_TIMESERIES, "root", null, null);
+      ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+      int colCount = resultSetMetaData.getColumnCount();
+      StringBuilder resultStr = new StringBuilder();
+      for (int i = 1; i < colCount + 1; i++) {
+        resultStr.append(resultSetMetaData.getColumnName(i)).append(",");
+      }
+      resultStr.append("\n");
+      while (resultSet.next()) {
+        for (int i = 1; i <= colCount; i++) {
+          resultStr.append(resultSet.getString(i)).append(",");
+        }
+        resultStr.append("\n");
+      }
+      Assert.assertEquals(resultStr.toString(), standard);
+    } catch (SQLException e) {
+      System.out.println(e);
+    }
+  }
+
+  /**
+   * get node number under a given node level
+   */
+  @SuppressWarnings("resource")
+  @Test
+  public void CountNodes() throws Exception {
+    List<String> nodes = new ArrayList<>();
+    nodes.add("root.vehicle1.d1");
+    nodes.add("root.vehicle1.d2");
+    nodes.add("root.vehicle2.d3");
+    nodes.add("root.vehicle2.d4");
+
+    when(fetchMetadataResp.getNodesList()).thenReturn(nodes);
+
+    String standard = "count,\n" + "4,\n";
+    try {
+      IoTDBDatabaseMetadata metadata = (IoTDBDatabaseMetadata) databaseMetaData;
+      String level = "3";
+      ResultSet resultSet = metadata.getNodes(Constant.COUNT_NODES, "root", null, null, level);
+      ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+      int colCount = resultSetMetaData.getColumnCount();
+      StringBuilder resultStr = new StringBuilder();
+      for (int i = 1; i < colCount + 1; i++) {
+        resultStr.append(resultSetMetaData.getColumnName(i)).append(",");
+      }
+      resultStr.append("\n");
+      while (resultSet.next()) {
+        for (int i = 1; i <= colCount; i++) {
+          resultStr.append(resultSet.getString(i)).append(",");
+        }
+        resultStr.append("\n");
+      }
+      Assert.assertEquals(resultStr.toString(), standard);
+    } catch (SQLException e) {
+      System.out.println(e);
+    }
+  }
+
+  /**
+   * get the timeseries number under a given node level
+   */
+  @SuppressWarnings("resource")
+  @Test
+  public void CountNodeTimeseries() throws Exception {
+    Map<String, String> nodeTimeseriesNum = new LinkedHashMap<>();
+    nodeTimeseriesNum.put("root.vehicle.d1", "3");
+    nodeTimeseriesNum.put("root.vehicle.d2", "2");
+    nodeTimeseriesNum.put("root.vehicle.d3", "4");
+    nodeTimeseriesNum.put("root.vehicle.d4", "2");
+
+    when(fetchMetadataResp.getNodeTimeseriesNum()).thenReturn(nodeTimeseriesNum);
+
+    String standard = "column,count,\n"
+            + "root.vehicle.d1,3,\n"
+            + "root.vehicle.d2,2,\n"
+            + "root.vehicle.d3,4,\n"
+            + "root.vehicle.d4,2,\n";
+    try {
+      IoTDBDatabaseMetadata metadata = (IoTDBDatabaseMetadata) databaseMetaData;
+      String level = "3";
+      ResultSet resultSet = metadata.getNodes(Constant.COUNT_NODE_TIMESERIES, "root", null, null, level);
       ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
       int colCount = resultSetMetaData.getColumnCount();
       StringBuilder resultStr = new StringBuilder();
@@ -126,9 +237,10 @@ public class IoTDBDatabaseMetadataTest {
 
     when(fetchMetadataResp.getColumnsList()).thenReturn(columnList);
 
-    String standard = "Column,\n" + "root.vehicle.d0,\n";
-    try (ResultSet resultSet = databaseMetaData
-        .getColumns(Constant.CATALOG_DEVICE, "vehicle", null, null)) {
+    String standard = "column,\n" + "root.vehicle.d0,\n";
+    try {
+      ResultSet resultSet = databaseMetaData
+          .getColumns(Constant.CATALOG_DEVICE, "vehicle", null, null);
       ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
       int colCount = resultSetMetaData.getColumnCount();
       StringBuilder resultStr = new StringBuilder();
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MGraph.java b/server/src/main/java/org/apache/iotdb/db/metadata/MGraph.java
index 64d10d5..c35e710 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MGraph.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MGraph.java
@@ -247,6 +247,10 @@ public class MGraph implements Serializable {
     return mtree.getAllStorageGroup();
   }
 
+  List<String> getNodesList(String nodeLevel) {
+    return mtree.getNodesList(nodeLevel);
+  }
+
   List<String> getLeafNodePathInNextLevel(String path) throws PathErrorException {
     return mtree.getLeafNodePathInNextLevel(path);
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java b/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
index 1116ff9..2da2f6c 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
@@ -18,23 +18,12 @@
  */
 package org.apache.iotdb.db.metadata;
 
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.io.*;
+import java.util.*;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.conf.adapter.IoTDBConfigDynamicAdapter;
-import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.exception.ConfigAdjusterException;
 import org.apache.iotdb.db.exception.MetadataErrorException;
 import org.apache.iotdb.db.exception.PathErrorException;
@@ -771,6 +760,21 @@ public class MManager {
   }
 
   /**
+   * Get all nodes from the given level
+   *
+   * @return A List instance which stores all node at given level
+   */
+  public List<String> getNodesList(String nodeLevel) {
+
+    lock.readLock().lock();
+    try {
+      return mgraph.getNodesList(nodeLevel);
+    } finally {
+      lock.readLock().unlock();
+    }
+  }
+
+  /**
    * @param path A seriesPath represented one Delta object
    * @return a list contains all column schema
    * @deprecated Get all MeasurementSchemas for given delta object type.
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MNode.java b/server/src/main/java/org/apache/iotdb/db/metadata/MNode.java
index 13cfe0b..433b7d5 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MNode.java
@@ -106,6 +106,16 @@ public class MNode implements Serializable {
   }
 
   /**
+   * function for checking whether the mnode has child mnode.
+   */
+  public boolean hasChildren() {
+    if (!isLeaf) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
    * function for checking whether mnode's children contain the given key.
    */
   public boolean hasChild(String key) {
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MTree.java b/server/src/main/java/org/apache/iotdb/db/metadata/MTree.java
index 3c63144..df0032b 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MTree.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MTree.java
@@ -18,17 +18,12 @@
  */
 package org.apache.iotdb.db.metadata;
 
+import java.io.Serializable;
+import java.util.*;
+
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.serializer.SerializerFeature;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
 import org.apache.iotdb.db.exception.PathErrorException;
 import org.apache.iotdb.tsfile.common.conf.TSFileConfig;
 import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
@@ -765,6 +760,36 @@ public class MTree implements Serializable {
   }
 
   /**
+   * Get all nodes at the given level in current Metadata Tree.
+   *
+   * @return a list contains all nodes at the given level
+   */
+  List<String> getNodesList(String nodeLevel) {
+    List<String> res = new ArrayList<>();
+    int level = Integer.parseInt(nodeLevel);
+    MNode rootNode;
+    if ((rootNode = getRoot()) != null) {
+      findNodes(rootNode, "root", res, level);
+    }
+    return res;
+  }
+
+  private void findNodes(MNode node, String path, List<String> res, int targetLevel) {
+    if (node == null) {
+      return;
+    }
+    if (targetLevel == 1) {
+      res.add(path);
+      return;
+    }
+    if (node.hasChildren()) {
+      for (MNode child : node.getChildren().values()) {
+        findNodes(child, path + "." + child.toString(), res, targetLevel - 1);
+      }
+    }
+  }
+
+  /**
    * Get all delta objects for given type.
    *
    * @param type device Type
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 493331c..1c27656 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
@@ -18,6 +18,17 @@
  */
 package org.apache.iotdb.db.service;
 
+import static org.apache.iotdb.db.conf.IoTDBConstant.*;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.time.ZoneId;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Pattern;
 import org.apache.iotdb.db.auth.AuthException;
 import org.apache.iotdb.db.auth.AuthorityChecker;
 import org.apache.iotdb.db.auth.authorizer.IAuthorizer;
@@ -281,10 +292,19 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
           resp.setDataType(getSeriesType(req.getColumnPath()).toString());
           status = new TS_Status(getStatus(TSStatusType.SUCCESS_STATUS));
           break;
+        case "COUNT_TIMESERIES":
         case "ALL_COLUMNS":
           resp.setColumnsList(getPaths(req.getColumnPath()));
           status = new TS_Status(getStatus(TSStatusType.SUCCESS_STATUS));
           break;
+        case "COUNT_NODES":
+          resp.setNodesList(getNodesList(req.getNodeLevel()));
+          status = new TS_Status(getStatus(TSStatusType.SUCCESS_STATUS));
+          break;
+        case "COUNT_NODE_TIMESERIES":
+          resp.setNodeTimeseriesNum(getNodeTimeseriesNum(getNodesList(req.getNodeLevel())));
+          status = new TS_Status(getStatus(TSStatusType.SUCCESS_STATUS));
+          break;
         default:
           status = getStatus(TSStatusType.FETCH_METADATA_ERROR, req.getType());
           break;
@@ -302,6 +322,18 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
     return resp;
   }
 
+  private Map<String, String> getNodeTimeseriesNum(List<String> nodes) throws MetadataErrorException {
+    Map<String, String> nodeColumnsNum = new HashMap<>();
+    for (String columnPath : nodes) {
+      nodeColumnsNum.put(columnPath, Integer.toString(getPaths(columnPath).size()));
+    }
+    return nodeColumnsNum;
+  }
+
+  private List<String> getNodesList(String level) throws PathErrorException {
+    return MManager.getInstance().getNodesList(level);
+  }
+
   private Set<String> getAllStorageGroups() throws PathErrorException {
     return MManager.getInstance().getAllStorageGroup();
   }
diff --git a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBMetadataFetchIT.java b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBMetadataFetchIT.java
index acdf8af..f50ab02 100644
--- a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBMetadataFetchIT.java
+++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBMetadataFetchIT.java
@@ -214,7 +214,7 @@ public class IoTDBMetadataFetchIT {
    */
   private void allColumns() throws SQLException {
     String standard =
-        "Column,\n" + "root.ln.wf01.wt01.status,\n" + "root.ln.wf01.wt01.temperature,\n";
+        "column,\n" + "root.ln.wf01.wt01.status,\n" + "root.ln.wf01.wt01.temperature,\n";
 
     try (ResultSet resultSet = databaseMetaData.getColumns(Constant.CATALOG_COLUMN, "root", null, null);) {
       ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
@@ -238,7 +238,7 @@ public class IoTDBMetadataFetchIT {
    * get all delta objects under a given column
    */
   private void device() throws SQLException {
-    String standard = "Column,\n" + "root.ln.wf01.wt01,\n";
+    String standard = "column,\n" + "root.ln.wf01.wt01,\n";
 
 
     try (ResultSet resultSet = databaseMetaData.getColumns(Constant.CATALOG_DEVICE, "ln", null,
diff --git a/service-rpc/src/main/thrift/rpc.thrift b/service-rpc/src/main/thrift/rpc.thrift
index ec05a27..7727a66 100644
--- a/service-rpc/src/main/thrift/rpc.thrift
+++ b/service-rpc/src/main/thrift/rpc.thrift
@@ -196,11 +196,14 @@ struct TSFetchMetadataResp{
 		4: optional string dataType
 		5: optional list<list<string>> showTimeseriesList
 		7: optional set<string> showStorageGroups
+		8: optional list<string> nodesList
+		9: optional map<string, string> nodeTimeseriesNum
 }
 
 struct TSFetchMetadataReq{
 		1: required string type
 		2: optional string columnPath
+		3: optional string nodeLevel
 }
 
 struct TSColumnSchema{