Apache IoTDB Node.js Client - Table Model User Guide

Version: 1.0.0
Last Updated: 2024

Table of Contents

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

AspectTable ModelTree Model
OrganizationRelational tablesHierarchical paths
SchemaExplicit table schemaTimeseries definitions
Query LanguageStandard SQLIoTDB SQL with paths
Use CaseStructured, relational dataHierarchical IoT data
Data ModelTags + Attributes + FieldsDevice + Measurements

2. Installation

2.1 Install from npm

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:

import { TableSessionPool, PoolConfigBuilder, TableTablet, ColumnCategory, TSDataType } from '@iotdb/client';

JavaScript:

const { TableSessionPool, PoolConfigBuilder, TableTablet, ColumnCategory, TSDataType } = require('@iotdb/client');

3. Quick Start

3.1 Basic TableSessionPool Example

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

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)

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)

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)

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:

OptionTypeDefaultDescription
databasestringundefinedDefault 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:

// Open without compression
await pool.open();

// Open with compression
await pool.open(true);
async close(): Promise<void>

Closes all sessions in the pool.

Example:

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:

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:

// 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):

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:

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):

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):

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):

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:

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:

CodeTypeJavaScript TypeUsage in Table Model
0BOOLEANbooleanTags, Attributes, Fields
1INT32numberTags, Attributes, Fields
2INT64number/stringTags, Attributes, Fields
3FLOATnumberAttributes, Fields
4DOUBLEnumberAttributes, Fields
5TEXTstringTags, Attributes, Fields
8TIMESTAMPnumber/DateFields
9DATEnumber/DateFields
10BLOBBufferFields
11STRINGstringTags, Attributes, Fields

6.2 Column Categories

CodeCategoryPurposeIndexedUsage
0TAGIdentifiersYesUse in WHERE clauses for filtering
1ATTRIBUTEMetadataNoDescriptive information
2FIELDMeasurementsNoActual sensor/measurement values

6.3 Using Data Types in insertTablet

Example with Mixed Types:

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

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

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

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

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

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:

// 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:

// 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:

// Prevent loading too much data
SELECT * FROM sensors 
WHERE region_id = 'north'
LIMIT 1000

Use time range filters:

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:

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:

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

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

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:

// 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:

// 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
// 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:

// 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:

process.env.LOG_LEVEL = 'debug';

Check SQL syntax:

try {
  await pool.executeQueryStatement('EXPLAIN SELECT * FROM my_table');
} catch (error) {
  console.error('Invalid SQL:', error.message);
}

Monitor query execution:

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

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 for comprehensive data type documentation.


Version: 1.0.0
Last Updated: January 2024
License: Apache License 2.0