blob: d00580d91b831ef06bc80cbd38fb4429eb01a009 [file]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { TableSessionPool } from "../../src/client/TableSessionPool";
import { ColumnCategory } from "../../src/client/Session";
import { TSDataType } from "../../src/utils/DataTypes";
describe("Table Model All Data Types E2E Tests", () => {
const IOTDB_HOST = process.env.IOTDB_HOST || "localhost";
const IOTDB_PORT = parseInt(process.env.IOTDB_PORT || "6667");
const IOTDB_USER = process.env.IOTDB_USER || "root";
const IOTDB_PASSWORD = process.env.IOTDB_PASSWORD || "root";
let pool: TableSessionPool;
let isConnected = false;
beforeAll(async () => {
pool = new TableSessionPool(IOTDB_HOST, IOTDB_PORT, {
username: IOTDB_USER,
password: IOTDB_PASSWORD,
database: "test",
maxPoolSize: 5,
minPoolSize: 2,
});
try {
await pool.init();
isConnected = true;
console.log("Connected to IoTDB for table model data types test");
// Cleanup from previous runs
try {
await pool.executeNonQueryStatement("DROP DATABASE test");
} catch (e) {
// Ignore if doesn't exist
}
} catch (error) {
console.warn("Could not connect to IoTDB. E2E tests will be skipped.");
console.warn(
"Set IOTDB_HOST, IOTDB_PORT to run E2E tests against a real instance.",
);
try {
await pool.close();
} catch {
// Ignore cleanup errors
}
}
}, 60000);
afterAll(async () => {
if (pool && isConnected) {
// Cleanup test data
try {
await pool.executeNonQueryStatement("DROP DATABASE test");
} catch (error) {
// Ignore cleanup errors
}
await pool.close();
}
}, 60000);
test("Should create database and table with all data types", async () => {
if (!isConnected) {
console.log("Skipping test - no IoTDB connection");
return;
}
// Create database
await pool.executeNonQueryStatement("CREATE DATABASE test");
// Switch to database context
await pool.executeNonQueryStatement("USE test");
// Create table with all 10 data types in relational schema
// Tags: metadata for grouping/filtering (indexed)
// Attributes: static properties (not time-series)
// Fields: actual time-series measurements
await pool.executeNonQueryStatement(
"CREATE TABLE all_types_table(" +
"region STRING TAG, " + // Tag for filtering
"device_id STRING TAG, " + // Tag for device identification
"model STRING ATTRIBUTE, " + // Static attribute
"boolean_field BOOLEAN FIELD, " + // All 10 data types as fields
"int32_field INT32 FIELD, " +
"int64_field INT64 FIELD, " +
"float_field FLOAT FIELD, " +
"double_field DOUBLE FIELD, " +
"text_field TEXT FIELD, " +
"timestamp_field TIMESTAMP FIELD, " +
"date_field DATE FIELD, " +
"blob_field BLOB FIELD, " +
"string_field STRING FIELD) " +
"WITH (TTL=3600000)",
);
// Verify table created
const dataSet = await pool.executeQueryStatement("SHOW TABLES");
const rows = [];
while (await dataSet.hasNext()) {
rows.push(dataSet.next());
}
await dataSet.close();
expect(rows.length).toBeGreaterThan(0);
console.log("Created table with all 10 data types in table model");
}, 60000);
test("Should insert and retrieve all data types in table model", async () => {
if (!isConnected) {
console.log("Skipping test - no IoTDB connection");
return;
}
const now = Date.now();
// Ensure we're in the correct database context
await pool.executeNonQueryStatement("USE test");
// Insert data using insertTablet - table model API
const tablet = {
tableName: "all_types_table", // Table name without database prefix
columnNames: [
"region", // TAG
"device_id", // TAG
"model", // ATTRIBUTE
"boolean_field", // FIELD
"int32_field", // FIELD
"int64_field", // FIELD
"float_field", // FIELD
"double_field", // FIELD
"text_field", // FIELD
"timestamp_field", // FIELD
"date_field", // FIELD
"blob_field", // FIELD
"string_field", // FIELD
],
columnTypes: [
TSDataType.STRING, // region (TAG)
TSDataType.STRING, // device_id (TAG)
TSDataType.STRING, // model (ATTRIBUTE)
TSDataType.BOOLEAN, // boolean_field
TSDataType.INT32, // int32_field
TSDataType.INT64, // int64_field
TSDataType.FLOAT, // float_field
TSDataType.DOUBLE, // double_field
TSDataType.TEXT, // text_field
TSDataType.TIMESTAMP, // timestamp_field
TSDataType.DATE, // date_field
TSDataType.BLOB, // blob_field
TSDataType.STRING, // string_field
],
columnCategories: [
ColumnCategory.TAG, // region
ColumnCategory.TAG, // device_id
ColumnCategory.ATTRIBUTE, // model
ColumnCategory.FIELD, // boolean_field
ColumnCategory.FIELD, // int32_field
ColumnCategory.FIELD, // int64_field
ColumnCategory.FIELD, // float_field
ColumnCategory.FIELD, // double_field
ColumnCategory.FIELD, // text_field
ColumnCategory.FIELD, // timestamp_field
ColumnCategory.FIELD, // date_field
ColumnCategory.FIELD, // blob_field
ColumnCategory.FIELD, // string_field
],
timestamps: [now, now + 1000, now + 2000],
values: [
[
"region1",
"device001",
"model_a",
true,
100,
1000n,
1.23,
4.56,
"text1",
new Date(now),
new Date(now),
Buffer.from([0x01, 0x02]),
"string1",
],
[
"region1",
"device001",
"model_a",
false,
200,
2000n,
2.34,
5.67,
"text2",
new Date(now + 1000),
new Date(now + 1000),
Buffer.from([0x03, 0x04]),
"string2",
],
[
"region1",
"device002",
"model_b",
true,
300,
3000n,
3.45,
6.78,
"text3",
new Date(now + 2000),
new Date(now + 2000),
Buffer.from([0x05, 0x06]),
"string3",
],
],
};
await pool.insertTablet(tablet);
console.log("Inserted data with all 10 data types in table model");
// Query data - table model supports WHERE clauses on tags
const dataSet = await pool.executeQueryStatement(
"SELECT * FROM all_types_table WHERE region = 'region1'",
);
expect(dataSet).toBeDefined();
const rows = [];
while (await dataSet.hasNext()) {
rows.push(dataSet.next());
}
await dataSet.close();
expect(rows.length).toBeGreaterThanOrEqual(3);
console.log(`Retrieved ${rows.length} rows from table model`);
console.log("Sample row:", rows[0]);
// Verify data types in results
const firstRow = rows[0];
expect(firstRow.getFields().length).toBeGreaterThan(10); // timestamp + tags + attributes + fields
console.log("All 10 data types successfully tested in table model");
}, 60000);
test("Should support table model queries", async () => {
if (!isConnected) {
console.log("Skipping test - no IoTDB connection");
return;
}
// Create another table in the same database
await pool.executeNonQueryStatement(
"CREATE TABLE test_table(" +
"id STRING TAG, " +
"value INT32 FIELD) " +
"WITH (TTL=3600000)",
);
// Verify table in current context
const dataSet = await pool.executeQueryStatement("SHOW TABLES");
const rows = [];
while (await dataSet.hasNext()) {
rows.push(dataSet.next());
}
await dataSet.close();
expect(rows.length).toBeGreaterThanOrEqual(2);
console.log("Multiple tables in same database verified");
}, 60000);
test("Should handle queries with aggregations on table model", async () => {
if (!isConnected) {
console.log("Skipping test - no IoTDB connection");
return;
}
// Query with COUNT aggregation
const countDataSet = await pool.executeQueryStatement(
"SELECT COUNT(int32_field), COUNT(text_field) FROM all_types_table",
);
const countRows = [];
while (await countDataSet.hasNext()) {
countRows.push(countDataSet.next());
}
await countDataSet.close();
expect(countRows.length).toBeGreaterThan(0);
console.log("COUNT result:", countRows[0]);
// Query with aggregation on numeric types
const aggDataSet = await pool.executeQueryStatement(
"SELECT AVG(float_field), MAX(int32_field), MIN(double_field) FROM all_types_table",
);
const aggRows = [];
while (await aggDataSet.hasNext()) {
aggRows.push(aggDataSet.next());
}
await aggDataSet.close();
expect(aggRows.length).toBeGreaterThan(0);
console.log("Aggregation result:", aggRows[0]);
}, 60000);
test("Should handle time-based queries in table model", async () => {
if (!isConnected) {
console.log("Skipping test - no IoTDB connection");
return;
}
const now = Date.now();
const futureTime = now + 10000;
// Query with time range
const dataSet = await pool.executeQueryStatement(
`SELECT * FROM all_types_table WHERE time >= ${now} AND time < ${futureTime}`,
);
const rows = [];
while (await dataSet.hasNext()) {
rows.push(dataSet.next());
}
await dataSet.close();
console.log(`Time-based query returned ${rows.length} rows`);
}, 60000);
test("Should demonstrate difference between tree and table models", async () => {
if (!isConnected) {
console.log("Skipping test - no IoTDB connection");
return;
}
console.log("\n=== Tree Model vs Table Model Comparison ===");
console.log("\nTree Model characteristics:");
console.log("- Hierarchical paths: root.database.device.sensor");
console.log("- Each timeseries defined separately");
console.log("- deviceId in insertTablet is a full path");
console.log("- Example: root.test.device1.temperature");
console.log("\nTable Model characteristics:");
console.log("- Relational schema with tags, attributes, fields");
console.log("- Table defines the schema once");
console.log("- deviceId in insertTablet is the table name");
console.log("- Supports WHERE clauses on tags");
console.log("- Example: SELECT * FROM table WHERE tag = 'value'");
// Demonstrate table model query with tags
const dataSet = await pool.executeQueryStatement(
"SELECT device_id, int32_field FROM all_types_table WHERE region = 'region1'",
);
const rows = [];
while (await dataSet.hasNext()) {
rows.push(dataSet.next());
}
await dataSet.close();
console.log(`\nTag-based query returned ${rows.length} rows`);
console.log("This type of query is specific to table model");
}, 60000);
test("Should handle batch inserts with all data types", async () => {
if (!isConnected) {
console.log("Skipping test - no IoTDB connection");
return;
}
const baseTime = Date.now() + 20000;
const batchSize = 20;
const timestamps: number[] = [];
const values: any[][] = [];
for (let i = 0; i < batchSize; i++) {
timestamps.push(baseTime + i * 1000);
values.push([
"region2", // region (TAG)
`device${i % 3}`, // device_id (TAG)
"model_batch", // model (ATTRIBUTE)
i % 2 === 0, // boolean_field
i * 10, // int32_field
BigInt(i * 100), // int64_field
i * 1.5, // float_field
i * 2.5, // double_field
`batch_${i}`, // text_field
new Date(baseTime + i * 1000), // timestamp_field
new Date(baseTime + i * 1000), // date_field
Buffer.from([i % 256]), // blob_field
`string_${i}`, // string_field
]);
}
const tablet = {
tableName: "all_types_table",
columnNames: [
"region",
"device_id",
"model",
"boolean_field",
"int32_field",
"int64_field",
"float_field",
"double_field",
"text_field",
"timestamp_field",
"date_field",
"blob_field",
"string_field",
],
columnTypes: [
TSDataType.STRING,
TSDataType.STRING,
TSDataType.STRING,
TSDataType.BOOLEAN,
TSDataType.INT32,
TSDataType.INT64,
TSDataType.FLOAT,
TSDataType.DOUBLE,
TSDataType.TEXT,
TSDataType.TIMESTAMP,
TSDataType.DATE,
TSDataType.BLOB,
TSDataType.STRING,
],
columnCategories: [
ColumnCategory.TAG,
ColumnCategory.TAG,
ColumnCategory.ATTRIBUTE,
ColumnCategory.FIELD,
ColumnCategory.FIELD,
ColumnCategory.FIELD,
ColumnCategory.FIELD,
ColumnCategory.FIELD,
ColumnCategory.FIELD,
ColumnCategory.FIELD,
ColumnCategory.FIELD,
ColumnCategory.FIELD,
ColumnCategory.FIELD,
],
timestamps,
values,
};
await pool.insertTablet(tablet);
console.log(`Inserted ${batchSize} rows in batch with all data types`);
// Verify batch insert
const dataSet = await pool.executeQueryStatement(
`SELECT COUNT(*) FROM all_types_table WHERE time >= ${baseTime}`,
);
const rows = [];
while (await dataSet.hasNext()) {
rows.push(dataSet.next());
}
await dataSet.close();
expect(rows.length).toBeGreaterThan(0);
console.log("Batch insert verified");
}, 60000);
});