| # Apache IoTDB Node.js Client - Table Model User Guide |
| |
| > **Version**: 1.0.0 |
| > **Last Updated**: 2024 |
| |
| ## Table of Contents |
| |
| - [1. Introduction](#1-introduction) |
| - [2. Installation](#2-installation) |
| - [3. Quick Start](#3-quick-start) |
| - [4. TableSessionPool API](#4-tablesessionpool-api) |
| - [5. Configuration Builder](#5-configuration-builder) |
| - [6. Data Types](#6-data-types) |
| - [7. Code Examples](#7-code-examples) |
| - [8. Best Practices](#8-best-practices) |
| - [9. Troubleshooting](#9-troubleshooting) |
| |
| ## 1. Introduction |
| |
| ### 1.1 Overview |
| |
| The Apache IoTDB Node.js Client provides native support for the table model (relational data model), enabling efficient management of structured data using SQL-like table operations. This guide covers the TableSessionPool API for table model operations. |
| |
| ### 1.2 Table Model Features |
| |
| The table model in IoTDB organizes data in a relational format: |
| |
| - **Database-based Organization**: Create and manage databases containing multiple tables |
| - **Table Schema**: Define tables with tags, attributes, and fields |
| - **SQL Operations**: Use familiar SQL syntax for queries and data manipulation |
| - **Connection Pooling**: Built-in pool for high-concurrency scenarios |
| - **Automatic Context**: Database context management with `USE DATABASE` |
| |
| ### 1.3 Key Concepts |
| |
| - **Database**: Logical grouping of related tables |
| - **Table**: Schema definition with columns and column categories |
| - **Tags**: Identifiers for time series (indexed, used in WHERE clauses) |
| - **Attributes**: Metadata for time series (not indexed) |
| - **Fields**: Actual measurement values |
| |
| ### 1.4 Table Model vs Tree Model |
| |
| | Aspect | Table Model | Tree Model | |
| |--------|-------------|------------| |
| | Organization | Relational tables | Hierarchical paths | |
| | Schema | Explicit table schema | Timeseries definitions | |
| | Query Language | Standard SQL | IoTDB SQL with paths | |
| | Use Case | Structured, relational data | Hierarchical IoT data | |
| | Data Model | Tags + Attributes + Fields | Device + Measurements | |
| |
| ## 2. Installation |
| |
| ### 2.1 Install from npm |
| |
| ```bash |
| npm install @iotdb/client |
| ``` |
| |
| **Requirements:** |
| - Node.js >= 14.0.0 |
| - Apache IoTDB >= 1.0.0 (with table model support) |
| |
| ### 2.2 Import in Your Project |
| |
| **TypeScript:** |
| ```typescript |
| import { TableSessionPool, PoolConfigBuilder, TableTablet, ColumnCategory, TSDataType } from '@iotdb/client'; |
| ``` |
| |
| **JavaScript:** |
| ```javascript |
| const { TableSessionPool, PoolConfigBuilder, TableTablet, ColumnCategory, TSDataType } = require('@iotdb/client'); |
| ``` |
| |
| ## 3. Quick Start |
| |
| ### 3.1 Basic TableSessionPool Example |
| |
| ```typescript |
| import { TableSessionPool, TableTablet, ColumnCategory } from '@iotdb/client'; |
| |
| async function quickStart() { |
| // Create and initialize table session pool |
| const pool = new TableSessionPool('localhost', 6667, { |
| username: 'root', |
| password: 'root', |
| database: 'test_db', // Optional: set default database |
| maxPoolSize: 10, |
| minPoolSize: 2, |
| }); |
| |
| await pool.open(); |
| |
| try { |
| // Create database |
| await pool.executeNonQueryStatement('CREATE DATABASE test_db'); |
| |
| // Use database |
| await pool.executeNonQueryStatement('USE test_db'); |
| |
| // Create table |
| await pool.executeNonQueryStatement(` |
| CREATE TABLE sensor_data ( |
| region_id STRING TAG, |
| device_id STRING TAG, |
| model STRING ATTRIBUTE, |
| temperature FLOAT FIELD, |
| humidity DOUBLE FIELD |
| ) WITH (TTL=3600000) |
| `); |
| |
| // Insert data using TableTablet class with addRow |
| const tablet = new TableTablet( |
| 'sensor_data', |
| ['region_id', 'device_id', 'model', 'temperature', 'humidity'], |
| [5, 5, 5, 3, 4], // STRING, STRING, STRING, FLOAT, DOUBLE |
| [ColumnCategory.TAG, ColumnCategory.TAG, ColumnCategory.ATTRIBUTE, ColumnCategory.FIELD, ColumnCategory.FIELD] |
| ); |
| tablet.addRow(Date.now(), ['region1', 'device001', 'ModelA', 25.5, 60.0]); |
| |
| await pool.insertTablet(tablet); |
| |
| // Query data |
| const dataSet = await pool.executeQueryStatement(` |
| SELECT * FROM sensor_data |
| WHERE region_id = 'region1' AND device_id = 'device001' |
| `); |
| |
| while (await dataSet.hasNext()) { |
| const row = dataSet.next(); |
| console.log(`Temperature: ${row.getFloat('temperature')}°C, Humidity: ${row.getDouble('humidity')}%`); |
| } |
| |
| await dataSet.close(); |
| } finally { |
| await pool.close(); |
| } |
| } |
| |
| quickStart(); |
| ``` |
| |
| ### 3.2 With Database Context |
| |
| ```typescript |
| async function withDatabaseContext() { |
| // Create pool with database pre-configured |
| const pool = new TableSessionPool('localhost', 6667, { |
| username: 'root', |
| password: 'root', |
| database: 'production_db', // Automatically executes USE DATABASE |
| maxPoolSize: 20, |
| }); |
| |
| await pool.open(); |
| |
| try { |
| // No need for explicit USE DATABASE |
| // Already in 'production_db' context |
| |
| const dataSet = await pool.executeQueryStatement('SHOW TABLES'); |
| |
| while (await dataSet.hasNext()) { |
| const row = dataSet.next(); |
| console.log('Table:', row.getFields()); |
| } |
| |
| await dataSet.close(); |
| } finally { |
| await pool.close(); |
| } |
| } |
| ``` |
| |
| ## 4. TableSessionPool API |
| |
| ### 4.1 Overview |
| |
| TableSessionPool is a specialized connection pool for table model operations. It extends the base SessionPool functionality with table-specific features and automatic database context management. |
| |
| **Key Features:** |
| - Same connection pooling as SessionPool |
| - Automatic `USE DATABASE` when configured with database |
| - Table-specific insertTablet with column categories |
| - SQL-based operations |
| - Round-robin load balancing |
| |
| ### 4.2 Constructor |
| |
| #### Option 1: Traditional API (Same Port) |
| |
| ```typescript |
| const pool = new TableSessionPool( |
| 'localhost', // Host |
| 6667, // Port |
| { |
| username: 'root', |
| password: 'root', |
| database: 'my_database', // Optional |
| maxPoolSize: 20, |
| minPoolSize: 5, |
| } |
| ); |
| ``` |
| |
| #### Option 2: Using nodeUrls (Different Ports) |
| |
| ```typescript |
| const pool = new TableSessionPool({ |
| nodeUrls: [ |
| 'node1:6667', |
| 'node2:6668', |
| 'node3:6669', |
| ], |
| username: 'root', |
| password: 'root', |
| database: 'my_database', |
| maxPoolSize: 20, |
| minPoolSize: 5, |
| }); |
| ``` |
| |
| #### Option 3: Using Builder Pattern (Recommended) |
| |
| ```typescript |
| import { PoolConfigBuilder } from '@iotdb/client'; |
| |
| const pool = new TableSessionPool( |
| new PoolConfigBuilder() |
| .nodeUrls(['node1:6667', 'node2:6667']) |
| .username('root') |
| .password('root') |
| .database('my_database') |
| .maxPoolSize(20) |
| .minPoolSize(5) |
| .maxIdleTime(60000) |
| .waitTimeout(60000) |
| .build() |
| ); |
| ``` |
| |
| ### 4.3 Configuration Options |
| |
| All SessionPool options plus: |
| |
| | Option | Type | Default | Description | |
| |--------|------|---------|-------------| |
| | `database` | string | `undefined` | Default database for table operations | |
| |
| ### 4.4 Methods |
| |
| #### 4.4.1 Connection Management |
| |
| ##### `async open(enableRpcCompression?: boolean): Promise<void>` |
| |
| Opens the connection pool. Optionally enables RPC compression. |
| |
| **Parameters:** |
| - `enableRpcCompression`: Enable RPC compression (default: false) |
| |
| **Example:** |
| ```typescript |
| // Open without compression |
| await pool.open(); |
| |
| // Open with compression |
| await pool.open(true); |
| ``` |
| |
| ##### `async close(): Promise<void>` |
| |
| Closes all sessions in the pool. |
| |
| **Example:** |
| ```typescript |
| await pool.close(); |
| ``` |
| |
| #### 4.4.2 Query Operations |
| |
| ##### `async executeQueryStatement(sql: string, timeoutMs?: number): Promise<SessionDataSet>` |
| |
| Executes a SQL query statement. |
| |
| **Parameters:** |
| - `sql`: SQL query statement |
| - `timeoutMs`: Query timeout in milliseconds (default: 60000) |
| |
| **Returns:** SessionDataSet for iterating results |
| |
| **Example:** |
| ```typescript |
| const dataSet = await pool.executeQueryStatement(` |
| SELECT temperature, humidity |
| FROM sensor_data |
| WHERE region_id = 'region1' |
| LIMIT 100 |
| `); |
| |
| while (await dataSet.hasNext()) { |
| const row = dataSet.next(); |
| console.log(row.getTimestamp(), row.getFloat('temperature')); |
| } |
| |
| await dataSet.close(); |
| ``` |
| |
| #### 4.4.3 Non-Query Operations |
| |
| ##### `async executeNonQueryStatement(sql: string): Promise<void>` |
| |
| Executes DDL or DML statements. |
| |
| **Parameters:** |
| - `sql`: SQL statement |
| |
| **Example:** |
| ```typescript |
| // Create database |
| await pool.executeNonQueryStatement('CREATE DATABASE my_db'); |
| |
| // Use database |
| await pool.executeNonQueryStatement('USE my_db'); |
| |
| // Create table |
| await pool.executeNonQueryStatement(` |
| CREATE TABLE devices ( |
| device_id STRING TAG, |
| location STRING ATTRIBUTE, |
| value FLOAT FIELD |
| ) |
| `); |
| |
| // Drop table |
| await pool.executeNonQueryStatement('DROP TABLE devices'); |
| |
| // Drop database |
| await pool.executeNonQueryStatement('DROP DATABASE my_db'); |
| ``` |
| |
| #### 4.4.4 Data Insertion |
| |
| ##### `async insertTablet(tablet: TableTablet | ITableTablet): Promise<void>` |
| |
| Inserts data into a table using tablet format. |
| |
| **Parameters:** |
| - `tablet`: TableTablet object or plain object containing table data |
| |
| **TableTablet Interface (for plain objects):** |
| ```typescript |
| interface ITableTablet { |
| tableName: string; // Table name |
| columnNames: string[]; // Column names |
| columnTypes: number[]; // Data type codes (TSDataType) |
| columnCategories: ColumnCategory[]; // Column categories |
| timestamps: number[]; // Timestamps in milliseconds |
| values: any[][]; // 2D array: [rows][columns] |
| } |
| ``` |
| |
| **ColumnCategory Enum:** |
| ```typescript |
| enum ColumnCategory { |
| TAG = 0, // Tag column - indexed for WHERE clause filtering (e.g., device_id, region_id) |
| FIELD = 2, // Field column - measurement values (e.g., temperature, humidity) |
| ATTRIBUTE = 1, // Attribute column - metadata not indexed (e.g., model, firmware_version) |
| TIME = 3, // Time column (reserved for internal use only) |
| } |
| ``` |
| |
| **Column Categories Explained:** |
| - `TAG` (0) - Indexed columns used for filtering in WHERE clauses (e.g., device_id, region_id) |
| - `FIELD` (2) - Measurement values (e.g., temperature, humidity) |
| - `ATTRIBUTE` (1) - Metadata not used for filtering (e.g., device_model, firmware_version) |
| - `TIME` (3) - Reserved for internal use. **Do not use** in columnCategories array - timestamps are handled separately via the timestamps array |
| |
| **TableTablet Class (with helper methods - recommended):** |
| ```typescript |
| import { TableTablet, ColumnCategory, TSDataType } from '@iotdb/client'; |
| |
| // Create a tablet |
| const tablet = new TableTablet( |
| 'sensor_data', |
| ['region_id', 'device_id', 'model', 'temperature', 'humidity'], |
| [TSDataType.TEXT, TSDataType.TEXT, TSDataType.TEXT, TSDataType.FLOAT, TSDataType.DOUBLE], |
| [ColumnCategory.TAG, ColumnCategory.TAG, ColumnCategory.ATTRIBUTE, ColumnCategory.FIELD, ColumnCategory.FIELD] |
| ); |
| |
| // Add rows one at a time using addRow method |
| tablet.addRow(Date.now(), ['region1', 'device001', 'ModelA', 25.5, 60.0]); |
| tablet.addRow(Date.now() + 1000, ['region1', 'device001', 'ModelA', 26.0, 61.5]); |
| tablet.addRow(Date.now() + 2000, ['region1', 'device002', 'ModelB', 24.8, 58.5]); |
| |
| // Insert the tablet |
| await pool.insertTablet(tablet); |
| ``` |
| |
| **Alternative: Plain object approach (still supported):** |
| ```typescript |
| import { ColumnCategory, TSDataType } from '@iotdb/client'; |
| |
| await pool.insertTablet({ |
| tableName: 'sensor_data', |
| columnNames: ['region_id', 'device_id', 'model', 'temperature', 'humidity'], |
| columnTypes: [TSDataType.TEXT, TSDataType.TEXT, TSDataType.TEXT, TSDataType.FLOAT, TSDataType.DOUBLE], |
| columnCategories: [ |
| ColumnCategory.TAG, // region_id - indexed tag |
| ColumnCategory.TAG, // device_id - indexed tag |
| ColumnCategory.ATTRIBUTE, // model - metadata |
| ColumnCategory.FIELD, // temperature - measurement |
| ColumnCategory.FIELD, // humidity - measurement |
| ], |
| timestamps: [ |
| Date.now(), |
| Date.now() + 1000, |
| Date.now() + 2000, |
| ], |
| values: [ |
| ['region1', 'device001', 'ModelA', 25.5, 60.0], |
| ['region1', 'device001', 'ModelA', 26.0, 61.5], |
| ['region1', 'device002', 'ModelB', 24.8, 58.5], |
| ], |
| }); |
| ``` |
| |
| **Example with numeric values (also supported):** |
| ```typescript |
| await pool.insertTablet({ |
| tableName: 'sensor_data', |
| columnNames: ['region_id', 'device_id', 'model', 'temperature', 'humidity'], |
| columnTypes: [5, 5, 5, 3, 4], // TEXT, TEXT, TEXT, FLOAT, DOUBLE |
| columnCategories: [0, 0, 1, 2, 2], // TAG, TAG, ATTRIBUTE, FIELD, FIELD |
| timestamps: [Date.now()], |
| values: [['region1', 'device001', 'ModelA', 25.5, 60.0]], |
| }); |
| ``` |
| |
| **Benefits of TableTablet class:** |
| - ✅ **Convenient**: `addRow()` method simplifies adding data row-by-row |
| - ✅ **Type-safe**: Constructor validates parameter lengths |
| - ✅ **Validated**: Automatic checking that values match columns count |
| - ✅ **Streaming-friendly**: Easy to add rows as data arrives |
| |
| ## 5. Configuration Builder |
| |
| ### 5.1 PoolConfigBuilder for Table Model |
| |
| The PoolConfigBuilder is used to create TableSessionPool configurations. |
| |
| **Available Methods:** |
| - `host(host: string): this` |
| - `port(port: number): this` |
| - `nodeUrls(urls: string[]): this` |
| - `username(username: string): this` |
| - `password(password: string): this` |
| - `database(database: string): this` - **Important for table model** |
| - `timezone(timezone: string): this` |
| - `fetchSize(size: number): this` |
| - `maxPoolSize(size: number): this` |
| - `minPoolSize(size: number): this` |
| - `maxIdleTime(time: number): this` |
| - `waitTimeout(timeout: number): this` |
| - `enableSSL(enable: boolean): this` |
| - `sslOptions(options: SSLOptions): this` |
| - `build(): PoolConfig` |
| |
| **Example:** |
| ```typescript |
| const config = new PoolConfigBuilder() |
| .nodeUrls(['iotdb1:6667', 'iotdb2:6667', 'iotdb3:6667']) |
| .username('root') |
| .password('root') |
| .database('production_db') |
| .fetchSize(2048) |
| .maxPoolSize(30) |
| .minPoolSize(10) |
| .maxIdleTime(60000) |
| .waitTimeout(60000) |
| .build(); |
| |
| const pool = new TableSessionPool(config); |
| await pool.open(); |
| ``` |
| |
| ## 6. Data Types |
| |
| ### 6.1 Supported Data Types |
| |
| The table model supports all IoTDB data types: |
| |
| | Code | Type | JavaScript Type | Usage in Table Model | |
| |------|------|-----------------|---------------------| |
| | 0 | BOOLEAN | boolean | Tags, Attributes, Fields | |
| | 1 | INT32 | number | Tags, Attributes, Fields | |
| | 2 | INT64 | number/string | Tags, Attributes, Fields | |
| | 3 | FLOAT | number | Attributes, Fields | |
| | 4 | DOUBLE | number | Attributes, Fields | |
| | 5 | TEXT | string | Tags, Attributes, Fields | |
| | 8 | TIMESTAMP | number/Date | Fields | |
| | 9 | DATE | number/Date | Fields | |
| | 10 | BLOB | Buffer | Fields | |
| | 11 | STRING | string | Tags, Attributes, Fields | |
| |
| ### 6.2 Column Categories |
| |
| | Code | Category | Purpose | Indexed | Usage | |
| |------|----------|---------|---------|-------| |
| | 0 | TAG | Identifiers | Yes | Use in WHERE clauses for filtering | |
| | 1 | ATTRIBUTE | Metadata | No | Descriptive information | |
| | 2 | FIELD | Measurements | No | Actual sensor/measurement values | |
| |
| ### 6.3 Using Data Types in insertTablet |
| |
| **Example with Mixed Types:** |
| ```typescript |
| await pool.insertTablet({ |
| tableName: 'equipment_metrics', |
| columnNames: [ |
| 'factory_id', // TAG |
| 'equipment_id', // TAG |
| 'manufacturer', // ATTRIBUTE |
| 'model', // ATTRIBUTE |
| 'temperature', // FIELD |
| 'pressure', // FIELD |
| 'is_active', // FIELD |
| 'last_check', // FIELD |
| ], |
| columnTypes: [5, 5, 5, 5, 3, 4, 0, 8], // STRING, STRING, STRING, STRING, FLOAT, DOUBLE, BOOLEAN, TIMESTAMP |
| columnCategories: [0, 0, 1, 1, 2, 2, 2, 2], // TAG, TAG, ATTR, ATTR, FIELD, FIELD, FIELD, FIELD |
| timestamps: [Date.now()], |
| values: [[ |
| 'factory01', |
| 'equip123', |
| 'ManufacturerA', |
| 'ModelX', |
| 75.5, |
| 101.325, |
| true, |
| Date.now(), |
| ]], |
| }); |
| ``` |
| |
| ## 7. Code Examples |
| |
| ### 7.1 Complete Database and Table Setup |
| |
| ```typescript |
| import { TableSessionPool, PoolConfigBuilder } from '@iotdb/client'; |
| |
| async function setupDatabase() { |
| const pool = new TableSessionPool( |
| new PoolConfigBuilder() |
| .host('localhost') |
| .port(6667) |
| .username('root') |
| .password('root') |
| .maxPoolSize(10) |
| .build() |
| ); |
| |
| await pool.open(); |
| |
| try { |
| // Create database |
| await pool.executeNonQueryStatement('CREATE DATABASE iot_platform'); |
| |
| // Use the database |
| await pool.executeNonQueryStatement('USE iot_platform'); |
| |
| // Create table with TTL |
| await pool.executeNonQueryStatement(` |
| CREATE TABLE sensor_readings ( |
| region_id STRING TAG, |
| building_id STRING TAG, |
| floor INT32 TAG, |
| device_id STRING TAG, |
| device_type STRING ATTRIBUTE, |
| location STRING ATTRIBUTE, |
| temperature FLOAT FIELD, |
| humidity FLOAT FIELD, |
| co2_level INT32 FIELD, |
| timestamp TIMESTAMP FIELD |
| ) WITH (TTL=7776000000) |
| `); |
| |
| console.log('Database and table created successfully'); |
| |
| // Show tables |
| const dataSet = await pool.executeQueryStatement('SHOW TABLES'); |
| console.log('Tables in database:'); |
| while (await dataSet.hasNext()) { |
| console.log(dataSet.next().getFields()); |
| } |
| await dataSet.close(); |
| |
| } finally { |
| await pool.close(); |
| } |
| } |
| |
| setupDatabase(); |
| ``` |
| |
| ### 7.2 Batch Insert Multiple Records |
| |
| ```typescript |
| async function batchInsert(pool: TableSessionPool) { |
| const regionIds = ['north', 'south', 'east', 'west']; |
| const deviceIds = ['dev001', 'dev002', 'dev003']; |
| |
| const timestamps = []; |
| const values = []; |
| const now = Date.now(); |
| |
| // Generate 100 records |
| for (let i = 0; i < 100; i++) { |
| timestamps.push(now + i * 1000); |
| |
| const region = regionIds[i % regionIds.length]; |
| const device = deviceIds[i % deviceIds.length]; |
| |
| values.push([ |
| region, // region_id (TAG) |
| device, // device_id (TAG) |
| 'SensorModelA', // model (ATTRIBUTE) |
| 20 + Math.random() * 10, // temperature (FIELD) |
| 50 + Math.random() * 30, // humidity (FIELD) |
| ]); |
| } |
| |
| await pool.insertTablet({ |
| tableName: 'sensor_readings', |
| columnNames: ['region_id', 'device_id', 'model', 'temperature', 'humidity'], |
| columnTypes: [5, 5, 5, 3, 3], |
| columnCategories: [0, 0, 1, 2, 2], |
| timestamps, |
| values, |
| }); |
| |
| console.log(`Inserted ${timestamps.length} records`); |
| } |
| ``` |
| |
| ### 7.3 Query with Filtering |
| |
| ```typescript |
| async function queryWithFilters(pool: TableSessionPool) { |
| // Query by TAG (indexed, efficient) |
| const dataSet = await pool.executeQueryStatement(` |
| SELECT |
| device_id, |
| temperature, |
| humidity, |
| timestamp |
| FROM sensor_readings |
| WHERE |
| region_id = 'north' |
| AND device_id IN ('dev001', 'dev002') |
| AND temperature > 25.0 |
| ORDER BY timestamp DESC |
| LIMIT 100 |
| `); |
| |
| const results = []; |
| while (await dataSet.hasNext()) { |
| const row = dataSet.next(); |
| results.push({ |
| deviceId: row.getString('device_id'), |
| temperature: row.getFloat('temperature'), |
| humidity: row.getFloat('humidity'), |
| timestamp: new Date(row.getTimestamp()), |
| }); |
| } |
| |
| await dataSet.close(); |
| |
| console.log(`Found ${results.length} matching records`); |
| return results; |
| } |
| ``` |
| |
| ### 7.4 Aggregation Queries |
| |
| ```typescript |
| async function aggregationQuery(pool: TableSessionPool) { |
| const dataSet = await pool.executeQueryStatement(` |
| SELECT |
| region_id, |
| device_id, |
| AVG(temperature) as avg_temp, |
| MAX(temperature) as max_temp, |
| MIN(temperature) as min_temp, |
| COUNT(*) as record_count |
| FROM sensor_readings |
| WHERE timestamp >= ${Date.now() - 3600000} |
| GROUP BY region_id, device_id |
| `); |
| |
| console.log('Aggregation Results:'); |
| while (await dataSet.hasNext()) { |
| const row = dataSet.next(); |
| console.log(`Region: ${row.getString('region_id')}, Device: ${row.getString('device_id')}`); |
| console.log(` Avg Temp: ${row.getFloat('avg_temp').toFixed(2)}°C`); |
| console.log(` Max Temp: ${row.getFloat('max_temp').toFixed(2)}°C`); |
| console.log(` Min Temp: ${row.getFloat('min_temp').toFixed(2)}°C`); |
| console.log(` Records: ${row.getInt('record_count')}`); |
| } |
| |
| await dataSet.close(); |
| } |
| ``` |
| |
| ### 7.5 Multi-Database Operations |
| |
| ```typescript |
| async function multiDatabaseOps(pool: TableSessionPool) { |
| await pool.open(); |
| |
| try { |
| // Create multiple databases |
| await pool.executeNonQueryStatement('CREATE DATABASE production'); |
| await pool.executeNonQueryStatement('CREATE DATABASE staging'); |
| |
| // Work with production database |
| await pool.executeNonQueryStatement('USE production'); |
| await pool.executeNonQueryStatement(` |
| CREATE TABLE metrics ( |
| device_id STRING TAG, |
| value DOUBLE FIELD |
| ) |
| `); |
| |
| // Switch to staging database |
| await pool.executeNonQueryStatement('USE staging'); |
| await pool.executeNonQueryStatement(` |
| CREATE TABLE test_metrics ( |
| device_id STRING TAG, |
| value DOUBLE FIELD |
| ) |
| `); |
| |
| // Query across databases using fully qualified names |
| const prodData = await pool.executeQueryStatement('SELECT * FROM production.metrics LIMIT 10'); |
| const stagingData = await pool.executeQueryStatement('SELECT * FROM staging.test_metrics LIMIT 10'); |
| |
| await prodData.close(); |
| await stagingData.close(); |
| |
| } finally { |
| await pool.close(); |
| } |
| } |
| ``` |
| |
| ## 8. Best Practices |
| |
| ### 8.1 Table Design |
| |
| **Use TAGs effectively:** |
| - Use TAGs for columns frequently used in WHERE clauses |
| - TAGs are indexed, enabling fast queries |
| - Keep TAG cardinality reasonable (avoid millions of unique values) |
| |
| **Use ATTRIBUTEs for metadata:** |
| - Descriptive information that doesn't need indexing |
| - Device model, manufacturer, location, etc. |
| - Not used in WHERE clauses |
| |
| **Use FIELDs for measurements:** |
| - Actual sensor readings and metrics |
| - Time-series data values |
| |
| **Example:** |
| ```typescript |
| // Good table design |
| CREATE TABLE sensor_data ( |
| region_id STRING TAG, // Indexed, used in WHERE |
| device_id STRING TAG, // Indexed, used in WHERE |
| manufacturer STRING ATTRIBUTE, // Metadata, not indexed |
| model STRING ATTRIBUTE, // Metadata, not indexed |
| temperature FLOAT FIELD, // Measurement value |
| humidity FLOAT FIELD // Measurement value |
| ) |
| |
| // Poor design - using FIELD for identifiers |
| CREATE TABLE sensor_data ( |
| temperature FLOAT FIELD, |
| humidity FLOAT FIELD, |
| device_id STRING FIELD // Should be TAG! |
| ) |
| ``` |
| |
| ### 8.2 Query Optimization |
| |
| **Filter by TAGs in WHERE clause:** |
| ```typescript |
| // Good: Uses indexed TAGs |
| SELECT * FROM sensors |
| WHERE region_id = 'north' AND device_id = 'dev001' |
| |
| // Poor: Filter by non-indexed FIELD |
| SELECT * FROM sensors |
| WHERE temperature > 25.0 // No TAG filtering |
| ``` |
| |
| **Use appropriate LIMIT:** |
| ```typescript |
| // Prevent loading too much data |
| SELECT * FROM sensors |
| WHERE region_id = 'north' |
| LIMIT 1000 |
| ``` |
| |
| **Use time range filters:** |
| ```typescript |
| SELECT * FROM sensors |
| WHERE region_id = 'north' |
| AND timestamp >= ${Date.now() - 3600000} |
| AND timestamp <= ${Date.now()} |
| ``` |
| |
| ### 8.3 Connection Pool Management |
| |
| **Size the pool appropriately:** |
| ```typescript |
| const pool = new TableSessionPool({ |
| nodeUrls: ['localhost:6667'], |
| maxPoolSize: 50, // Peak concurrent queries |
| minPoolSize: 10, // Keep warm connections |
| maxIdleTime: 60000, // Clean up after 1 minute idle |
| waitTimeout: 30000, // Wait max 30s for connection |
| }); |
| ``` |
| |
| **Monitor pool health:** |
| ```typescript |
| setInterval(() => { |
| console.log('Pool Stats:'); |
| console.log(` Total: ${pool.getPoolSize()}`); |
| console.log(` Available: ${pool.getAvailableSize()}`); |
| console.log(` In Use: ${pool.getInUseSize()}`); |
| }, 60000); // Every minute |
| ``` |
| |
| ### 8.4 Error Handling |
| |
| ```typescript |
| async function robustInsert(pool: TableSessionPool, data: any) { |
| try { |
| await pool.insertTablet(data); |
| console.log('Insert successful'); |
| } catch (error) { |
| if (error.message.includes('Table does not exist')) { |
| console.log('Creating table...'); |
| await createTable(pool); |
| await pool.insertTablet(data); |
| } else if (error.message.includes('Database does not exist')) { |
| console.log('Creating database...'); |
| await createDatabase(pool); |
| await createTable(pool); |
| await pool.insertTablet(data); |
| } else { |
| console.error('Insert failed:', error); |
| throw error; |
| } |
| } |
| } |
| ``` |
| |
| ### 8.5 Resource Cleanup |
| |
| ```typescript |
| async function properCleanup() { |
| const pool = new TableSessionPool('localhost', 6667, { |
| username: 'root', |
| password: 'root', |
| }); |
| |
| await pool.open(); |
| |
| try { |
| const dataSet = await pool.executeQueryStatement('SELECT * FROM table1'); |
| try { |
| while (await dataSet.hasNext()) { |
| // Process results |
| } |
| } finally { |
| await dataSet.close(); // Always close DataSet |
| } |
| } finally { |
| await pool.close(); // Always close pool |
| } |
| } |
| ``` |
| |
| ## 9. Troubleshooting |
| |
| ### 9.1 Common Issues |
| |
| #### Database Does Not Exist |
| |
| **Symptoms:** |
| ``` |
| Error: Database 'my_db' does not exist |
| ``` |
| |
| **Solutions:** |
| ```typescript |
| // Create database first |
| await pool.executeNonQueryStatement('CREATE DATABASE my_db'); |
| await pool.executeNonQueryStatement('USE my_db'); |
| |
| // Or configure pool with existing database |
| const pool = new TableSessionPool('localhost', 6667, { |
| database: 'my_db', // Must exist |
| }); |
| ``` |
| |
| #### Table Does Not Exist |
| |
| **Symptoms:** |
| ``` |
| Error: Table 'my_table' does not exist |
| ``` |
| |
| **Solutions:** |
| ```typescript |
| // Check if table exists |
| const dataSet = await pool.executeQueryStatement('SHOW TABLES'); |
| // ... verify table exists |
| |
| // Create table if needed |
| await pool.executeNonQueryStatement(` |
| CREATE TABLE my_table (...) |
| `); |
| ``` |
| |
| #### Column Mismatch |
| |
| **Symptoms:** |
| ``` |
| Error: Column count mismatch |
| ``` |
| |
| **Solutions:** |
| - Ensure `columnNames`, `columnTypes`, and `columnCategories` have same length |
| - Verify `values` array matches column count |
| - Check table schema matches your data |
| |
| ```typescript |
| // Verify schema |
| const dataSet = await pool.executeQueryStatement('DESCRIBE my_table'); |
| while (await dataSet.hasNext()) { |
| console.log(dataSet.next().getFields()); |
| } |
| ``` |
| |
| #### TTL Issues |
| |
| **Symptoms:** |
| Data automatically deleted after some time |
| |
| **Solutions:** |
| ```typescript |
| // Check TTL setting |
| const dataSet = await pool.executeQueryStatement('SHOW TABLES'); |
| // Look for TTL in table properties |
| |
| // Modify TTL |
| await pool.executeNonQueryStatement(` |
| ALTER TABLE my_table SET PROPERTIES TTL=31536000000 |
| `); // 1 year in milliseconds |
| ``` |
| |
| ### 9.2 Performance Issues |
| |
| **Slow Queries:** |
| 1. Add indexes by using TAGs appropriately |
| 2. Use time range filters |
| 3. Add LIMIT clauses |
| 4. Consider table partitioning |
| |
| **Slow Inserts:** |
| 1. Increase batch size (100-1000 rows) |
| 2. Use connection pooling |
| 3. Consider multiple concurrent writers |
| 4. Monitor server resources |
| |
| ### 9.3 Debugging |
| |
| **Enable debug logging:** |
| ```typescript |
| process.env.LOG_LEVEL = 'debug'; |
| ``` |
| |
| **Check SQL syntax:** |
| ```typescript |
| try { |
| await pool.executeQueryStatement('EXPLAIN SELECT * FROM my_table'); |
| } catch (error) { |
| console.error('Invalid SQL:', error.message); |
| } |
| ``` |
| |
| **Monitor query execution:** |
| ```typescript |
| const start = Date.now(); |
| const dataSet = await pool.executeQueryStatement('SELECT ...'); |
| console.log(`Query took ${Date.now() - start}ms`); |
| |
| let rowCount = 0; |
| while (await dataSet.hasNext()) { |
| dataSet.next(); |
| rowCount++; |
| } |
| console.log(`Returned ${rowCount} rows`); |
| ``` |
| |
| ### 9.4 Getting Help |
| |
| - **Documentation**: [IoTDB Table Model Docs](https://iotdb.apache.org/) |
| - **GitHub Issues**: [Report bugs](https://github.com/CritasWang/@iotdb/client/issues) |
| - **Community**: dev@iotdb.apache.org |
| |
| ## Appendix A: Complete API Reference |
| |
| ### TableSessionPool Methods |
| - `open(enableRpcCompression?)` - Open connection pool |
| - `close()` - Close all sessions |
| - `executeQueryStatement(sql, timeout?)` - Execute SQL query |
| - `executeNonQueryStatement(sql)` - Execute DDL/DML |
| - `insertTablet(tablet)` - Batch insert into table |
| - `getPoolSize()` - Total sessions |
| - `getAvailableSize()` - Available sessions |
| - `getInUseSize()` - Active sessions |
| |
| ### SQL Statements |
| - `CREATE DATABASE database_name` |
| - `DROP DATABASE database_name` |
| - `USE database_name` |
| - `SHOW DATABASES` |
| - `SHOW TABLES` |
| - `CREATE TABLE table_name (...)` |
| - `DROP TABLE table_name` |
| - `ALTER TABLE table_name SET PROPERTIES TTL=<ms>` |
| - `SELECT ... FROM table_name WHERE ... LIMIT ...` |
| - `DESCRIBE table_name` |
| |
| ## Appendix B: Data Type Reference |
| |
| See [data-types.md](data-types.md) for comprehensive data type documentation. |
| |
| --- |
| |
| **Version**: 1.0.0 |
| **Last Updated**: January 2024 |
| **License**: Apache License 2.0 |