@gftdcojp/ksqldb-orm
Version:
ksqldb-orm - Server-Side TypeScript ORM for ksqlDB with enterprise security extensions
377 lines (296 loc) • 10.8 kB
Markdown
# 列名変換機能 (Column Name Transform)
KsqlDBの列名が強制的に大文字になる仕様に対応するため、TypeScriptのキャメルケースやスネークケースの列名を自動的にKsqlDB用の大文字に変換する機能です。
## 問題の背景
### KsqlDBの仕様
- 引用符なしの識別子(列名)は自動的に大文字に変換される
- `userId` → `USERID`、`email` → `EMAIL`
- これによりTypeScriptコードで小文字の列名を使用するとエラーが発生
### 発生していた問題
```typescript
// ❌ エラーが発生
const { data: userDataFromDb, error: userFetchError } = await ksqldbClient
.from("users")
.eq("email", auth0User.email) // "email" がKsqlDBで認識されない
.single();
// Result: Error - User fetch error in withAuth: null
```
## 解決策
### 自動列名変換機能
この機能により、TypeScriptコードでは小文字の列名を使用し、内部的にKsqlDB用の大文字に自動変換されます。
```typescript
// ✅ 自動変換により正常に動作
const { data: userDataFromDb, error: userFetchError } = await ksqldbClient
.from("users")
.eq("email", auth0User.email) // 内部で "EMAIL" に自動変換
.single();
```
## 基本的な使用方法
### 1. 設定の追加
```typescript
import { createKsqlDbClient, PRESET_COLUMN_TRANSFORM_CONFIGS } from '@gftdcojp/ksqldb-orm';
const ksqldbClient = createKsqlDbClient({
url: 'https://your-ksqldb-server.com:8088',
apiKey: 'your-api-key',
apiSecret: 'your-api-secret',
// 列名変換設定を追加
columnNameTransform: PRESET_COLUMN_TRANSFORM_CONFIGS.CAMEL_CASE
});
```
### 2. クエリでの使用
```typescript
// TypeScriptコードでは小文字の列名を使用
const users = await ksqldbClient
.from('users')
.select('userId, email, firstName, lastName') // 内部で大文字に変換
.eq('email', 'test@example.com') // 内部で "EMAIL" に変換
.order('createdAt', false) // 内部で "CREATEDAT" に変換
.execute();
// レスポンスは自動的にcamelCaseに変換される
console.log(users.data);
// [{ userId: "123", email: "test@example.com", firstName: "John", lastName: "Doe" }]
```
## 設定オプション
### 1. 事前定義された設定パターン
```typescript
import { PRESET_COLUMN_TRANSFORM_CONFIGS } from '@gftdcojp/ksqldb-orm';
// camelCase → UPPERCASE
const camelCaseConfig = PRESET_COLUMN_TRANSFORM_CONFIGS.CAMEL_CASE;
// snake_case → UPPERCASE
const snakeCaseConfig = PRESET_COLUMN_TRANSFORM_CONFIGS.SNAKE_CASE;
// 変換なし
const noneConfig = PRESET_COLUMN_TRANSFORM_CONFIGS.NONE;
```
### 2. カスタム戦略設定
```typescript
import { ColumnNameTransformConfig } from '@gftdcojp/ksqldb-orm';
const customConfig: ColumnNameTransformConfig = {
strategy: 'uppercase', // 'uppercase' | 'lowercase' | 'camelCase' | 'snake_case' | 'none'
preserveExistingUppercase: true
};
```
### 3. 明示的マッピング設定
```typescript
const mappingConfig: ColumnNameTransformConfig = {
strategy: 'uppercase',
columnMappings: {
'userId': 'USER_IDENTIFIER',
'email': 'EMAIL_ADDRESS',
'createdAt': 'CREATION_TIMESTAMP'
}
};
```
### 4. カスタム変換関数
```typescript
const customTransformConfig: ColumnNameTransformConfig = {
customTransform: (columnName: string) => {
// カスタムロジック
if (columnName === 'id') return 'UNIQUE_ID';
return columnName.toUpperCase();
}
};
```
## TypeScript型定義との統合
### 1. インターフェース定義
```typescript
interface User {
userId: string; // TypeScript: camelCase
email: string; // TypeScript: camelCase
accountId: string; // TypeScript: camelCase
firstName: string;
lastName: string;
createdAt: string;
isActive: boolean;
}
// ORMが内部で以下に変換
// KsqlDB: USERID, EMAIL, ACCOUNTID, FIRSTNAME, LASTNAME, CREATEDAT, ISACTIVE
```
### 2. 完全な使用例
```typescript
import { createKsqlDbClient, PRESET_COLUMN_TRANSFORM_CONFIGS } from '@gftdcojp/ksqldb-orm';
// 設定
const ksqldbClient = createKsqlDbClient({
url: process.env.KSQLDB_URL!,
apiKey: process.env.KSQLDB_API_KEY!,
apiSecret: process.env.KSQLDB_API_SECRET!,
columnNameTransform: PRESET_COLUMN_TRANSFORM_CONFIGS.CAMEL_CASE
});
// 認証機能での使用例
async function authenticateUser(email: string): Promise<User | null> {
const { data: userData, error } = await ksqldbClient
.from('users')
.select('userId, email, accountId, firstName, lastName, isActive')
.eq('email', email) // 自動的に "EMAIL" に変換
.eq('isActive', true) // 自動的に "ISACTIVE" に変換
.single();
if (error) {
console.error('Authentication error:', error);
return null;
}
return userData; // { userId: "123", email: "test@example.com", ... }
}
// データ挿入での使用例
async function createUser(userData: Partial<User>): Promise<User | null> {
const { data, error } = await ksqldbClient
.from('users')
.insert({
userId: generateId(),
email: userData.email, // 内部で "EMAIL" に変換
firstName: userData.firstName,// 内部で "FIRSTNAME" に変換
lastName: userData.lastName, // 内部で "LASTNAME" に変換
isActive: true // 内部で "ISACTIVE" に変換
});
return error ? null : data;
}
```
## 高度な使用例
### 1. 複数の変換戦略
```typescript
// 開発環境:変換なし
const devConfig: ColumnNameTransformConfig = {
strategy: 'none'
};
// 本番環境:自動変換
const prodConfig: ColumnNameTransformConfig = {
strategy: 'uppercase',
columnMappings: {
'id': 'UNIQUE_IDENTIFIER',
'timestamp': 'EVENT_TIMESTAMP'
}
};
const config = process.env.NODE_ENV === 'production' ? prodConfig : devConfig;
```
### 2. 動的マッピング
```typescript
const dynamicConfig: ColumnNameTransformConfig = {
customTransform: (columnName: string) => {
// 特別な命名規則
if (columnName.endsWith('Id')) {
return columnName.replace('Id', '_IDENTIFIER').toUpperCase();
}
if (columnName.startsWith('is')) {
return `${columnName.substring(2).toUpperCase()}_FLAG`;
}
return columnName.toUpperCase();
}
};
// userId → USER_IDENTIFIER
// isActive → ACTIVE_FLAG
// email → EMAIL
```
### 3. 条件付き変換
```typescript
const conditionalConfig: ColumnNameTransformConfig = {
strategy: 'uppercase',
customTransform: (columnName: string) => {
// 既に大文字の場合はそのまま
if (columnName === columnName.toUpperCase()) {
return columnName;
}
// 特定のパターンの場合は特別な処理
const specialMappings: Record<string, string> = {
'createdAt': 'CREATION_TIMESTAMP',
'updatedAt': 'MODIFICATION_TIMESTAMP',
'deletedAt': 'DELETION_TIMESTAMP'
};
return specialMappings[columnName] || columnName.toUpperCase();
}
};
```
## バックワード互換性
### 既存コードとの互換性
```typescript
// 既存の大文字指定も引き続きサポート
const legacyQuery = await ksqldbClient
.from('users')
.eq('EMAIL', 'test@example.com') // 既存の大文字指定
.eq('email', 'test@example.com') // 新しい小文字指定(自動変換)
.execute();
```
### 段階的移行
```typescript
// 設定で移行をコントロール
const migrationConfig: ColumnNameTransformConfig = {
strategy: 'uppercase',
preserveExistingUppercase: true, // 既存の大文字を保持
columnMappings: {
// 新しい列名のマッピングのみ追加
'newField': 'NEW_FIELD_NAME'
}
};
```
## トラブルシューティング
### 1. 変換が効かない場合
```typescript
// デバッグ用に変換結果を確認
import { transformColumnNameForKsqlDb } from '@gftdcojp/ksqldb-orm';
const config = { strategy: 'uppercase' as const };
console.log(transformColumnNameForKsqlDb('email', config)); // "EMAIL"
```
### 2. 予期しない変換結果
```typescript
// カスタム変換でデバッグ
const debugConfig: ColumnNameTransformConfig = {
customTransform: (columnName: string) => {
const result = columnName.toUpperCase();
console.log(`Column transform: ${columnName} → ${result}`);
return result;
}
};
```
### 3. パフォーマンス最適化
```typescript
// マッピング辞書を使って高速化
import { COMMON_COLUMN_MAPPINGS } from '@gftdcojp/ksqldb-orm';
const optimizedConfig: ColumnNameTransformConfig = {
strategy: 'uppercase',
columnMappings: {
...COMMON_COLUMN_MAPPINGS.CAMEL_TO_UPPER,
// プロジェクト固有のマッピングを追加
'customField': 'CUSTOM_FIELD_NAME'
}
};
```
## 実装の詳細
### 変換タイミング
1. **クエリ送信時**: `eq()`, `select()`, `order()`などのメソッドで列名を大文字に変換
2. **レスポンス受信時**: KsqlDBからのレスポンスでキーをcamelCaseに変換
### 変換ロジック
1. 明示的マッピング辞書をチェック
2. カスタム変換関数をチェック
3. 既存の大文字列名の保持設定をチェック
4. 設定された戦略に基づいて変換
### パフォーマンス
- 列名変換はメモ化されており、同じ列名の変換は高速
- マッピング辞書による変換は O(1) の時間計算量
- 文字列変換操作は軽量で、パフォーマンスへの影響は最小
## 制限事項
### 1. 複雑な列名
特殊文字や数字を含む列名では、期待と異なる変換結果になる場合があります。この場合は明示的マッピングを使用してください。
### 2. 動的列名
動的に生成される列名(例:`SELECT ${field} FROM table`)は、実行時まで変換できません。
### 3. エイリアス
SQLのエイリアス(AS句)は現在サポートされていません。
## ベストプラクティス
### 1. 事前定義設定の使用
```typescript
// 推奨:事前定義された設定を使用
columnNameTransform: PRESET_COLUMN_TRANSFORM_CONFIGS.CAMEL_CASE
```
### 2. 明示的マッピング
```typescript
// 重要な列名は明示的にマッピング
columnMappings: {
'userId': 'USER_IDENTIFIER',
'email': 'EMAIL_ADDRESS'
}
```
### 3. テスト
```typescript
// 変換結果をテストで検証
describe('Column name transformation', () => {
it('should transform email to EMAIL', () => {
expect(transformColumnNameForKsqlDb('email', config)).toBe('EMAIL');
});
});
```
この機能により、KsqlDBの列名の大文字強制仕様による認証エラーやその他の問題が解決され、TypeScriptコードでより自然な列名を使用できるようになります。