@gftdcojp/gftd-orm
Version:
Enterprise-grade real-time data platform with ksqlDB, inspired by Supabase architecture
335 lines • 11.4 kB
JavaScript
;
/**
* クライアントサイド用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