UNPKG

@gftdcojp/gftd-orm

Version:

Enterprise-grade real-time data platform with ksqlDB, inspired by Supabase architecture

335 lines 11.4 kB
"use strict"; /** * クライアントサイド用Database Module */ Object.defineProperty(exports, "__esModule", { value: true }); exports.DatabaseClientQueryBuilder = exports.DatabaseClient = void 0; exports.createDatabaseClient = createDatabaseClient; const env_1 = require("./utils/env"); const http_client_1 = require("./http-client"); /** * クライアントサイド用Database クラス */ class DatabaseClient { constructor(config) { this.config = config; this.initialized = false; if (!(0, env_1.isClientSide)()) { throw new Error('DatabaseClient can only be used in browser environment'); } this.ksqlClient = new http_client_1.KsqlDbClientBrowser(config.ksql); } /** * データベースを初期化 */ async initialize() { if (this.initialized) return; // クライアントサイドでの初期化処理 this.initialized = true; } /** * テーブルからデータを取得(Supabaseライク) */ from(table) { return new DatabaseClientQueryBuilder(table, this.ksqlClient); } /** * SQL文を直接実行 */ async sql(query, params) { return this.ksqlClient.executeQuery(query); } /** * ヘルスチェック */ async health() { try { const connected = await this.ksqlClient.isConnected(); return { status: connected ? 'ok' : 'error', details: { connected } }; } catch (error) { return { status: 'error', details: error }; } } } exports.DatabaseClient = DatabaseClient; /** * クライアントサイド用クエリビルダー */ class DatabaseClientQueryBuilder { constructor(tableName, ksqlClient) { this.tableName = tableName; this.ksqlClient = ksqlClient; this.selectFields = ['*']; this.whereConditions = {}; this.orderByConditions = {}; } /** * 選択するフィールドを指定 */ select(fields = '*') { this.selectFields = fields === '*' ? ['*'] : fields.split(',').map(f => f.trim()); return this; } /** * WHERE条件を追加 */ eq(column, value) { this.whereConditions[column] = value; return this; } /** * LIKE条件を追加 */ like(column, pattern) { this.whereConditions[column] = { like: pattern }; return this; } /** * 範囲条件を追加 */ gte(column, value) { this.whereConditions[column] = { gte: value }; return this; } lte(column, value) { this.whereConditions[column] = { lte: value }; return this; } gt(column, value) { this.whereConditions[column] = { gt: value }; return this; } lt(column, value) { this.whereConditions[column] = { lt: value }; return this; } /** * IN条件を追加 */ in(column, values) { this.whereConditions[column] = { in: values }; return this; } /** * ORDER BY を追加 */ order(column, ascending = true) { this.orderByConditions[column] = ascending ? 'asc' : 'desc'; return this; } /** * LIMIT を設定 */ limit(count) { this.limitValue = count; return this; } /** * OFFSET を設定 */ offset(count) { this.offsetValue = count; return this; } /** * クエリを実行してデータを取得 */ async execute() { try { // データ取得はテーブルに対してプルクエリを実行 // テーブル名に_streamが含まれている場合は_tableに変換 const tableNameForQuery = this.tableName.endsWith('_stream') ? this.tableName.replace('_stream', '_table') : this.tableName.endsWith('_table') ? this.tableName : `${this.tableName}_table`; const query = this.buildSelectQuery(tableNameForQuery); console.log(`[DEBUG] DatabaseClientQueryBuilder.execute - Table: ${tableNameForQuery}`); console.log(`[DEBUG] DatabaseClientQueryBuilder.execute - Query: ${query}`); const result = await this.ksqlClient.executePullQuery(query); // 結果をパース(実際の実装では適切にパース) const data = result?.data || result?.rows || []; console.log(`[DEBUG] DatabaseClientQueryBuilder.execute - Result:`, result); console.log(`[DEBUG] DatabaseClientQueryBuilder.execute - Data count: ${data.length}`); return { data }; } catch (error) { console.error(`[ERROR] DatabaseClientQueryBuilder.execute failed:`, error); console.error(`[ERROR] Table name: ${this.tableName}`); console.error(`[ERROR] Query details:`, { selectFields: this.selectFields, whereConditions: this.whereConditions, orderByConditions: this.orderByConditions, limitValue: this.limitValue, offsetValue: this.offsetValue }); return { data: [], error: { message: error.message || 'Unknown error', details: error, tableName: this.tableName, queryType: 'client_pull_query' } }; } } /** * 単一レコードを取得 */ async single() { const result = await this.limit(1).execute(); return { data: result.data.length > 0 ? result.data[0] : null, error: result.error }; } /** * データを挿入(Supabaseライク) */ async insert(values) { try { const query = this.buildInsertQuery(values); await this.ksqlClient.executeQuery(query); return { data: values }; } catch (error) { return { data: null, error }; } } /** * データを更新(Supabaseライク) */ async update(values) { try { const query = this.buildUpdateQuery(values); await this.ksqlClient.executeQuery(query); return { data: [values] }; } catch (error) { return { data: [], error }; } } /** * データを削除(Supabaseライク) */ async delete() { try { const query = this.buildDeleteQuery(); await this.ksqlClient.executeQuery(query); return { data: { deleted: true } }; } catch (error) { return { data: null, error }; } } buildSelectQuery(tableName) { const targetTable = tableName || this.tableName; let query = `SELECT ${this.selectFields.join(', ')} FROM ${targetTable}`; // WHERE条件を追加 const whereConditions = []; for (const [key, value] of Object.entries(this.whereConditions)) { if (typeof value === 'object' && value !== null) { // 複雑な条件の場合 for (const [operator, operatorValue] of Object.entries(value)) { if (operator === 'gte') { whereConditions.push(`${key} >= ${this.formatValue(operatorValue)}`); } else if (operator === 'lte') { whereConditions.push(`${key} <= ${this.formatValue(operatorValue)}`); } else if (operator === 'gt') { whereConditions.push(`${key} > ${this.formatValue(operatorValue)}`); } else if (operator === 'lt') { whereConditions.push(`${key} < ${this.formatValue(operatorValue)}`); } else if (operator === 'like') { whereConditions.push(`${key} LIKE ${this.formatValue(operatorValue)}`); } else if (operator === 'in') { const values = Array.isArray(operatorValue) ? operatorValue : [operatorValue]; const formattedValues = values.map(v => this.formatValue(v)).join(', '); whereConditions.push(`${key} IN (${formattedValues})`); } } } else { whereConditions.push(`${key} = ${this.formatValue(value)}`); } } if (whereConditions.length > 0) { query += ` WHERE ${whereConditions.join(' AND ')}`; } // ORDER BY条件を追加 const orderByConditions = []; for (const [key, direction] of Object.entries(this.orderByConditions)) { const dir = typeof direction === 'string' ? direction.toUpperCase() : 'ASC'; orderByConditions.push(`${key} ${dir}`); } if (orderByConditions.length > 0) { query += ` ORDER BY ${orderByConditions.join(', ')}`; } // LIMIT条件を追加 if (this.limitValue !== undefined) { query += ` LIMIT ${this.limitValue}`; } // OFFSET条件を追加 if (this.offsetValue !== undefined) { query += ` OFFSET ${this.offsetValue}`; } return query + ';'; } formatValue(value) { if (typeof value === 'string') { return `'${value.replace(/'/g, "''")}'`; } else if (typeof value === 'number' || typeof value === 'boolean') { return value.toString(); } else if (value === null) { return 'NULL'; } else { return `'${String(value).replace(/'/g, "''")}'`; } } buildInsertQuery(values) { const columns = Object.keys(values).join(', '); const placeholders = Object.values(values).map(v => `'${v}'`).join(', '); return `INSERT INTO ${this.tableName} (${columns}) VALUES (${placeholders})`; } buildUpdateQuery(values) { const setClause = Object.entries(values) .map(([key, value]) => `${key} = '${value}'`) .join(', '); let query = `UPDATE ${this.tableName} SET ${setClause}`; if (Object.keys(this.whereConditions).length > 0) { const whereClause = Object.entries(this.whereConditions) .map(([key, value]) => `${key} = '${value}'`) .join(' AND '); query += ` WHERE ${whereClause}`; } return query; } buildDeleteQuery() { let query = `DELETE FROM ${this.tableName}`; if (Object.keys(this.whereConditions).length > 0) { const whereClause = Object.entries(this.whereConditions) .map(([key, value]) => `${key} = '${value}'`) .join(' AND '); query += ` WHERE ${whereClause}`; } return query; } } exports.DatabaseClientQueryBuilder = DatabaseClientQueryBuilder; /** * クライアントサイド用データベースインスタンスを作成 */ function createDatabaseClient(config) { return new DatabaseClient(config); } //# sourceMappingURL=database-client.js.map