@zhin.js/database 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +93 -0
- package/lib/base/database.d.ts +71 -0
- package/lib/base/database.d.ts.map +1 -0
- package/lib/base/database.js +101 -0
- package/lib/base/database.js.map +1 -0
- package/lib/base/dialect.d.ts +34 -0
- package/lib/base/dialect.d.ts.map +1 -0
- package/lib/base/dialect.js +21 -0
- package/lib/base/dialect.js.map +1 -0
- package/lib/base/index.d.ts +6 -0
- package/lib/base/index.d.ts.map +1 -0
- package/lib/base/index.js +6 -0
- package/lib/base/index.js.map +1 -0
- package/lib/base/model.d.ts +53 -0
- package/lib/base/model.d.ts.map +1 -0
- package/lib/base/model.js +65 -0
- package/lib/base/model.js.map +1 -0
- package/lib/base/query-classes.d.ts +68 -0
- package/lib/base/query-classes.d.ts.map +1 -0
- package/lib/base/query-classes.js +178 -0
- package/lib/base/query-classes.js.map +1 -0
- package/lib/base/thenable.d.ts +15 -0
- package/lib/base/thenable.d.ts.map +1 -0
- package/lib/base/thenable.js +33 -0
- package/lib/base/thenable.js.map +1 -0
- package/lib/dialects/memory.d.ts +52 -0
- package/lib/dialects/memory.d.ts.map +1 -0
- package/lib/dialects/memory.js +772 -0
- package/lib/dialects/memory.js.map +1 -0
- package/lib/dialects/mongodb.d.ts +85 -0
- package/lib/dialects/mongodb.d.ts.map +1 -0
- package/lib/dialects/mongodb.js +461 -0
- package/lib/dialects/mongodb.js.map +1 -0
- package/lib/dialects/mysql.d.ts +33 -0
- package/lib/dialects/mysql.d.ts.map +1 -0
- package/lib/dialects/mysql.js +132 -0
- package/lib/dialects/mysql.js.map +1 -0
- package/lib/dialects/pg.d.ts +33 -0
- package/lib/dialects/pg.d.ts.map +1 -0
- package/lib/dialects/pg.js +132 -0
- package/lib/dialects/pg.js.map +1 -0
- package/lib/dialects/redis.d.ts +87 -0
- package/lib/dialects/redis.d.ts.map +1 -0
- package/lib/dialects/redis.js +500 -0
- package/lib/dialects/redis.js.map +1 -0
- package/lib/dialects/sqlite.d.ts +46 -0
- package/lib/dialects/sqlite.d.ts.map +1 -0
- package/lib/dialects/sqlite.js +201 -0
- package/lib/dialects/sqlite.js.map +1 -0
- package/lib/index.d.ts +18 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +18 -0
- package/lib/index.js.map +1 -0
- package/lib/registry.d.ts +37 -0
- package/lib/registry.d.ts.map +1 -0
- package/lib/registry.js +21 -0
- package/lib/registry.js.map +1 -0
- package/lib/type/document/database.d.ts +60 -0
- package/lib/type/document/database.d.ts.map +1 -0
- package/lib/type/document/database.js +242 -0
- package/lib/type/document/database.js.map +1 -0
- package/lib/type/document/model.d.ts +42 -0
- package/lib/type/document/model.d.ts.map +1 -0
- package/lib/type/document/model.js +65 -0
- package/lib/type/document/model.js.map +1 -0
- package/lib/type/keyvalue/database.d.ts +64 -0
- package/lib/type/keyvalue/database.d.ts.map +1 -0
- package/lib/type/keyvalue/database.js +214 -0
- package/lib/type/keyvalue/database.js.map +1 -0
- package/lib/type/keyvalue/model.d.ts +107 -0
- package/lib/type/keyvalue/model.d.ts.map +1 -0
- package/lib/type/keyvalue/model.js +261 -0
- package/lib/type/keyvalue/model.js.map +1 -0
- package/lib/type/related/database.d.ts +32 -0
- package/lib/type/related/database.d.ts.map +1 -0
- package/lib/type/related/database.js +334 -0
- package/lib/type/related/database.js.map +1 -0
- package/lib/type/related/model.d.ts +43 -0
- package/lib/type/related/model.d.ts.map +1 -0
- package/lib/type/related/model.js +108 -0
- package/lib/type/related/model.js.map +1 -0
- package/lib/types.d.ts +251 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +28 -0
- package/lib/types.js.map +1 -0
- package/package.json +54 -0
- package/src/base/database.ts +128 -0
- package/src/base/dialect.ts +76 -0
- package/src/base/index.ts +5 -0
- package/src/base/model.ts +89 -0
- package/src/base/query-classes.ts +217 -0
- package/src/base/thenable.ts +54 -0
- package/src/dialects/memory.ts +880 -0
- package/src/dialects/mongodb.ts +533 -0
- package/src/dialects/mysql.ts +157 -0
- package/src/dialects/pg.ts +157 -0
- package/src/dialects/redis.ts +598 -0
- package/src/dialects/sqlite.ts +233 -0
- package/src/index.ts +20 -0
- package/src/registry.ts +59 -0
- package/src/type/document/database.ts +283 -0
- package/src/type/document/model.ts +86 -0
- package/src/type/keyvalue/database.ts +261 -0
- package/src/type/keyvalue/model.ts +339 -0
- package/src/type/related/database.ts +392 -0
- package/src/type/related/model.ts +117 -0
- package/src/types.ts +403 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
import {Dialect} from "../base";
|
|
2
|
+
import {KeyValueDatabase} from "../type/keyvalue/database";
|
|
3
|
+
import {Registry} from "../registry";
|
|
4
|
+
import {Database} from "../base";
|
|
5
|
+
import type { RedisClientOptions } from 'redis';
|
|
6
|
+
import {
|
|
7
|
+
BuildQueryResult,
|
|
8
|
+
KeyValueQueryResult,
|
|
9
|
+
DatabaseDialect,
|
|
10
|
+
QueryParams,
|
|
11
|
+
CreateQueryParams,
|
|
12
|
+
SelectQueryParams,
|
|
13
|
+
InsertQueryParams,
|
|
14
|
+
UpdateQueryParams,
|
|
15
|
+
DeleteQueryParams,
|
|
16
|
+
AlterQueryParams,
|
|
17
|
+
DropTableQueryParams,
|
|
18
|
+
DropIndexQueryParams
|
|
19
|
+
} from "../types";
|
|
20
|
+
|
|
21
|
+
export interface RedisDialectConfig extends RedisClientOptions {}
|
|
22
|
+
export class RedisDialect extends Dialect<RedisDialectConfig, KeyValueQueryResult> {
|
|
23
|
+
private client: any = null;
|
|
24
|
+
|
|
25
|
+
constructor(config: RedisDialectConfig) {
|
|
26
|
+
super('redis', config);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 检查是否已连接
|
|
31
|
+
*/
|
|
32
|
+
isConnected(): boolean {
|
|
33
|
+
return this.client !== null && this.client.isReady;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 连接数据库
|
|
38
|
+
*/
|
|
39
|
+
async connect(): Promise<void> {
|
|
40
|
+
return this.start();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 断开连接
|
|
45
|
+
*/
|
|
46
|
+
async disconnect(): Promise<void> {
|
|
47
|
+
return this.stop();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 健康检查
|
|
52
|
+
*/
|
|
53
|
+
async healthCheck(): Promise<boolean> {
|
|
54
|
+
if (!this.isConnected()) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const result = await this.client.ping();
|
|
59
|
+
return result === 'PONG';
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 启动连接
|
|
67
|
+
*/
|
|
68
|
+
async start(): Promise<void> {
|
|
69
|
+
try {
|
|
70
|
+
// 动态导入 redis 客户端
|
|
71
|
+
const { createClient } = await import('redis');
|
|
72
|
+
|
|
73
|
+
this.client = createClient(this.config);
|
|
74
|
+
|
|
75
|
+
this.client.on('error', (err: Error) => {
|
|
76
|
+
console.error('Redis 客户端错误:', err);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
this.client.on('connect', () => {
|
|
80
|
+
console.log('Redis 连接已建立');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.client.on('ready', () => {
|
|
84
|
+
console.log('Redis 客户端已准备就绪');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
this.client.on('end', () => {
|
|
88
|
+
console.log('Redis 连接已关闭');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
await this.client.connect();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('forgot install redis ?');
|
|
94
|
+
throw new Error(`Redis 连接失败: ${error}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 停止连接
|
|
100
|
+
*/
|
|
101
|
+
async stop(): Promise<void> {
|
|
102
|
+
if (this.client) {
|
|
103
|
+
await this.client.quit();
|
|
104
|
+
this.client = null;
|
|
105
|
+
console.log('Redis 连接已关闭');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 释放资源
|
|
111
|
+
*/
|
|
112
|
+
async dispose(): Promise<void> {
|
|
113
|
+
return this.stop();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 执行查询
|
|
118
|
+
*/
|
|
119
|
+
async query<T = any>(query: KeyValueQueryResult, params: any[] = []): Promise<T> {
|
|
120
|
+
if (!this.client) {
|
|
121
|
+
throw new Error('Redis 未连接');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const keyPrefix = `${query.bucket}:`;
|
|
126
|
+
|
|
127
|
+
switch (query.operation) {
|
|
128
|
+
case 'get':
|
|
129
|
+
return await this.executeGet(keyPrefix, query) as T;
|
|
130
|
+
case 'set':
|
|
131
|
+
return await this.executeSet(keyPrefix, query, params) as T;
|
|
132
|
+
case 'delete':
|
|
133
|
+
return await this.executeDelete(keyPrefix, query) as T;
|
|
134
|
+
case 'has':
|
|
135
|
+
return await this.executeHas(keyPrefix, query) as T;
|
|
136
|
+
case 'keys':
|
|
137
|
+
return await this.executeKeys(keyPrefix, query) as T;
|
|
138
|
+
case 'values':
|
|
139
|
+
return await this.executeValues(keyPrefix, query) as T;
|
|
140
|
+
case 'entries':
|
|
141
|
+
return await this.executeEntries(keyPrefix, query) as T;
|
|
142
|
+
case 'clear':
|
|
143
|
+
return await this.executeClear(keyPrefix, query) as T;
|
|
144
|
+
case 'size':
|
|
145
|
+
return await this.executeSize(keyPrefix, query) as T;
|
|
146
|
+
case 'expire':
|
|
147
|
+
return await this.executeExpire(keyPrefix, query, params) as T;
|
|
148
|
+
case 'ttl':
|
|
149
|
+
return await this.executeTtl(keyPrefix, query) as T;
|
|
150
|
+
case 'persist':
|
|
151
|
+
return await this.executePersist(keyPrefix, query) as T;
|
|
152
|
+
case 'cleanup':
|
|
153
|
+
return await this.executeCleanup(keyPrefix, query) as T;
|
|
154
|
+
case 'keysByPattern':
|
|
155
|
+
return await this.executeKeysByPattern(keyPrefix, query, params) as T;
|
|
156
|
+
default:
|
|
157
|
+
throw new Error(`不支持的 Redis 操作: ${query.operation}`);
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw new Error(`Redis 查询执行失败: ${error}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 构建查询
|
|
166
|
+
*/
|
|
167
|
+
buildQuery<U extends object = any>(params: QueryParams<U>): BuildQueryResult<KeyValueQueryResult> {
|
|
168
|
+
switch (params.type) {
|
|
169
|
+
case 'create':
|
|
170
|
+
return this.buildCreateQuery(params as CreateQueryParams<U>);
|
|
171
|
+
case 'select':
|
|
172
|
+
return this.buildSelectQuery(params as SelectQueryParams<U>);
|
|
173
|
+
case 'insert':
|
|
174
|
+
return this.buildInsertQuery(params as InsertQueryParams<U>);
|
|
175
|
+
case 'update':
|
|
176
|
+
return this.buildUpdateQuery(params as UpdateQueryParams<U>);
|
|
177
|
+
case 'delete':
|
|
178
|
+
return this.buildDeleteQuery(params as DeleteQueryParams<U>);
|
|
179
|
+
case 'alter':
|
|
180
|
+
return this.buildAlterQuery(params as AlterQueryParams<U>);
|
|
181
|
+
case 'drop_table':
|
|
182
|
+
return this.buildDropTableQuery(params as DropTableQueryParams<U>);
|
|
183
|
+
case 'drop_index':
|
|
184
|
+
return this.buildDropIndexQuery(params as DropIndexQueryParams);
|
|
185
|
+
default:
|
|
186
|
+
throw new Error(`不支持的查询类型: ${(params as any).type}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 实现 Dialect 接口的所有必需方法
|
|
191
|
+
mapColumnType(type: string): string {
|
|
192
|
+
return type;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
quoteIdentifier(identifier: string): string {
|
|
196
|
+
return identifier;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
getParameterPlaceholder(index: number): string {
|
|
200
|
+
return `$${index}`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
getStatementTerminator(): string {
|
|
204
|
+
return ';';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
formatBoolean(value: boolean): string {
|
|
208
|
+
return value ? 'true' : 'false';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
formatDate(value: Date): string {
|
|
212
|
+
return value.toISOString();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
formatJson(value: any): string {
|
|
216
|
+
return JSON.stringify(value);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
escapeString(value: string): string {
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
formatDefaultValue(value: any): string {
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
formatLimit(limit: number): string {
|
|
228
|
+
return `${limit}`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
formatOffset(offset: number): string {
|
|
232
|
+
return `${offset}`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
formatLimitOffset(limit: number, offset: number): string {
|
|
236
|
+
return `${limit},${offset}`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
formatCreateTable(tableName: string, columns: string[]): string {
|
|
240
|
+
return `CREATE TABLE ${tableName} (${columns.join(',')})`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
formatAlterTable(tableName: string, alterations: string[]): string {
|
|
244
|
+
return `ALTER TABLE ${tableName} ${alterations.join(',')}`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
formatDropTable(tableName: string, ifExists?: boolean): string {
|
|
248
|
+
return `DROP TABLE ${tableName} ${ifExists ? 'IF EXISTS' : ''}`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
formatDropIndex(indexName: string, tableName: string, ifExists?: boolean): string {
|
|
252
|
+
return `DROP INDEX ${indexName} ON ${tableName} ${ifExists ? 'IF EXISTS' : ''}`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Redis 特定的查询构建方法
|
|
256
|
+
private buildCreateQuery<T extends object>(params: CreateQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
|
|
257
|
+
return {
|
|
258
|
+
query: {
|
|
259
|
+
bucket: params.tableName,
|
|
260
|
+
operation: 'keys'
|
|
261
|
+
},
|
|
262
|
+
params: []
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private buildSelectQuery<T extends object>(params: SelectQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
|
|
267
|
+
const query: KeyValueQueryResult = {
|
|
268
|
+
bucket: params.tableName,
|
|
269
|
+
operation: 'keys'
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// 如果有条件,尝试提取键名
|
|
273
|
+
if (params.conditions) {
|
|
274
|
+
const key = this.extractKeyFromCondition(params.conditions);
|
|
275
|
+
if (key) {
|
|
276
|
+
query.operation = 'get';
|
|
277
|
+
query.key = key;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
query,
|
|
283
|
+
params: []
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private buildInsertQuery<T extends object>(params: InsertQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
|
|
288
|
+
const key = this.extractKeyFromData(params.data);
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
query: {
|
|
292
|
+
bucket: params.tableName,
|
|
293
|
+
operation: 'set',
|
|
294
|
+
key: key || 'default',
|
|
295
|
+
value: params.data
|
|
296
|
+
},
|
|
297
|
+
params: [params.data]
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private buildUpdateQuery<T extends object>(params: UpdateQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
|
|
302
|
+
const key = this.extractKeyFromCondition(params.conditions);
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
query: {
|
|
306
|
+
bucket: params.tableName,
|
|
307
|
+
operation: 'set',
|
|
308
|
+
key: key || 'default',
|
|
309
|
+
value: params.update
|
|
310
|
+
},
|
|
311
|
+
params: [params.update]
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private buildDeleteQuery<T extends object>(params: DeleteQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
|
|
316
|
+
const key = this.extractKeyFromCondition(params.conditions);
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
query: {
|
|
320
|
+
bucket: params.tableName,
|
|
321
|
+
operation: 'delete',
|
|
322
|
+
key: key || 'default'
|
|
323
|
+
},
|
|
324
|
+
params: []
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private buildAlterQuery<T extends object>(params: AlterQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
|
|
329
|
+
return {
|
|
330
|
+
query: {
|
|
331
|
+
bucket: params.tableName,
|
|
332
|
+
operation: 'keys'
|
|
333
|
+
},
|
|
334
|
+
params: [params.alterations]
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private buildDropTableQuery<T extends object>(params: DropTableQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
|
|
339
|
+
return {
|
|
340
|
+
query: {
|
|
341
|
+
bucket: params.tableName,
|
|
342
|
+
operation: 'clear'
|
|
343
|
+
},
|
|
344
|
+
params: []
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private buildDropIndexQuery(params: DropIndexQueryParams): BuildQueryResult<KeyValueQueryResult> {
|
|
349
|
+
return {
|
|
350
|
+
query: {
|
|
351
|
+
bucket: params.tableName,
|
|
352
|
+
operation: 'keys'
|
|
353
|
+
},
|
|
354
|
+
params: [params.indexName]
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private extractKeyFromCondition(condition: any): string | null {
|
|
359
|
+
if (typeof condition !== 'object' || condition === null) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 查找 key 字段
|
|
364
|
+
if ('key' in condition) {
|
|
365
|
+
return condition.key;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// 查找 id 字段
|
|
369
|
+
if ('id' in condition) {
|
|
370
|
+
return condition.id;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// 递归查找
|
|
374
|
+
for (const value of Object.values(condition)) {
|
|
375
|
+
if (typeof value === 'object' && value !== null) {
|
|
376
|
+
const result = this.extractKeyFromCondition(value);
|
|
377
|
+
if (result) return result;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private extractKeyFromData(data: any): string | null {
|
|
385
|
+
if (typeof data !== 'object' || data === null) {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// 查找 key 字段
|
|
390
|
+
if ('key' in data) {
|
|
391
|
+
return data.key;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// 查找 id 字段
|
|
395
|
+
if ('id' in data) {
|
|
396
|
+
return data.id;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Redis 执行方法
|
|
403
|
+
private async executeGet(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
404
|
+
if (!query.key) {
|
|
405
|
+
throw new Error('GET 操作需要指定 key');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const key = `${keyPrefix}${query.key}`;
|
|
409
|
+
const value = await this.client.get(key);
|
|
410
|
+
|
|
411
|
+
if (value === null) {
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
return [JSON.parse(value)];
|
|
417
|
+
} catch {
|
|
418
|
+
return [value];
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private async executeSet(keyPrefix: string, query: KeyValueQueryResult, params: any[]): Promise<any[]> {
|
|
423
|
+
if (!query.key) {
|
|
424
|
+
throw new Error('SET 操作需要指定 key');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const key = `${keyPrefix}${query.key}`;
|
|
428
|
+
const value = JSON.stringify(params[0] || query.value);
|
|
429
|
+
|
|
430
|
+
let result: any;
|
|
431
|
+
if (query.ttl) {
|
|
432
|
+
result = await this.client.setEx(key, query.ttl, value);
|
|
433
|
+
} else {
|
|
434
|
+
result = await this.client.set(key, value);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return [{ key: query.key, result }];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private async executeDelete(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
441
|
+
if (!query.key) {
|
|
442
|
+
throw new Error('DELETE 操作需要指定 key');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const key = `${keyPrefix}${query.key}`;
|
|
446
|
+
const result = await this.client.del(key);
|
|
447
|
+
return [{ key: query.key, deleted: result > 0 }];
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
private async executeHas(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
451
|
+
if (!query.key) {
|
|
452
|
+
throw new Error('HAS 操作需要指定 key');
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const key = `${keyPrefix}${query.key}`;
|
|
456
|
+
const result = await this.client.exists(key);
|
|
457
|
+
return [result === 1];
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private async executeKeys(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
461
|
+
const pattern = `${keyPrefix}*`;
|
|
462
|
+
const keys = await this.client.keys(pattern);
|
|
463
|
+
return keys.map((key: string) => key.replace(keyPrefix, ''));
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
private async executeValues(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
467
|
+
const pattern = `${keyPrefix}*`;
|
|
468
|
+
const keys = await this.client.keys(pattern);
|
|
469
|
+
|
|
470
|
+
if (keys.length === 0) {
|
|
471
|
+
return [];
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const values = await this.client.mGet(keys);
|
|
475
|
+
return values.map((value: string) => {
|
|
476
|
+
try {
|
|
477
|
+
return JSON.parse(value);
|
|
478
|
+
} catch {
|
|
479
|
+
return value;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
private async executeEntries(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
485
|
+
const pattern = `${keyPrefix}*`;
|
|
486
|
+
const keys = await this.client.keys(pattern);
|
|
487
|
+
|
|
488
|
+
if (keys.length === 0) {
|
|
489
|
+
return [];
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const values = await this.client.mGet(keys);
|
|
493
|
+
return keys.map((key: string, index: number) => {
|
|
494
|
+
const cleanKey = key.replace(keyPrefix, '');
|
|
495
|
+
const value = values[index];
|
|
496
|
+
try {
|
|
497
|
+
return [cleanKey, JSON.parse(value)];
|
|
498
|
+
} catch {
|
|
499
|
+
return [cleanKey, value];
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private async executeClear(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
505
|
+
const pattern = `${keyPrefix}*`;
|
|
506
|
+
const keys = await this.client.keys(pattern);
|
|
507
|
+
|
|
508
|
+
if (keys.length === 0) {
|
|
509
|
+
return [{ cleared: 0 }];
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const result = await this.client.del(keys);
|
|
513
|
+
return [{ cleared: result }];
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
private async executeSize(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
517
|
+
const pattern = `${keyPrefix}*`;
|
|
518
|
+
const keys = await this.client.keys(pattern);
|
|
519
|
+
return [keys.length];
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
private async executeExpire(keyPrefix: string, query: KeyValueQueryResult, params: any[]): Promise<any[]> {
|
|
523
|
+
if (!query.key) {
|
|
524
|
+
throw new Error('EXPIRE 操作需要指定 key');
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const key = `${keyPrefix}${query.key}`;
|
|
528
|
+
const ttl = params[0] || query.ttl;
|
|
529
|
+
const result = await this.client.expire(key, ttl);
|
|
530
|
+
return [{ key: query.key, result }];
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
private async executeTtl(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
534
|
+
if (!query.key) {
|
|
535
|
+
throw new Error('TTL 操作需要指定 key');
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const key = `${keyPrefix}${query.key}`;
|
|
539
|
+
const ttl = await this.client.ttl(key);
|
|
540
|
+
return [ttl];
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
private async executePersist(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
544
|
+
if (!query.key) {
|
|
545
|
+
throw new Error('PERSIST 操作需要指定 key');
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const key = `${keyPrefix}${query.key}`;
|
|
549
|
+
const result = await this.client.persist(key);
|
|
550
|
+
return [{ key: query.key, result }];
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
private async executeCleanup(keyPrefix: string, query: KeyValueQueryResult): Promise<any[]> {
|
|
554
|
+
// Redis 会自动清理过期的键,这里返回 0
|
|
555
|
+
return [{ cleaned: 0 }];
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private async executeKeysByPattern(keyPrefix: string, query: KeyValueQueryResult, params: any[]): Promise<any[]> {
|
|
559
|
+
const pattern = params[0] || query.pattern || '*';
|
|
560
|
+
const fullPattern = `${keyPrefix}${pattern}`;
|
|
561
|
+
const keys = await this.client.keys(fullPattern);
|
|
562
|
+
return keys.map((key: string) => key.replace(keyPrefix, ''));
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
get dialectInfo(): DatabaseDialect {
|
|
566
|
+
return {
|
|
567
|
+
name: this.name,
|
|
568
|
+
version: '1.0.0',
|
|
569
|
+
features: [
|
|
570
|
+
'key_value_storage',
|
|
571
|
+
'expiration',
|
|
572
|
+
'pub_sub',
|
|
573
|
+
'streams',
|
|
574
|
+
'clustering',
|
|
575
|
+
'persistence'
|
|
576
|
+
],
|
|
577
|
+
dataTypes: {
|
|
578
|
+
'string': 'String',
|
|
579
|
+
'integer': 'Integer',
|
|
580
|
+
'float': 'Float',
|
|
581
|
+
'boolean': 'Boolean',
|
|
582
|
+
'date': 'Timestamp',
|
|
583
|
+
'json': 'JSON'
|
|
584
|
+
},
|
|
585
|
+
identifierQuote: '',
|
|
586
|
+
parameterPlaceholder: '?',
|
|
587
|
+
supportsTransactions: true,
|
|
588
|
+
supportsIndexes: false,
|
|
589
|
+
supportsForeignKeys: false,
|
|
590
|
+
supportsViews: false,
|
|
591
|
+
supportsStoredProcedures: false
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
Registry.register('redis', (config: RedisDialectConfig, schemas?: Database.Schemas<Record<string, object>>) => {
|
|
597
|
+
return new KeyValueDatabase(new RedisDialect(config), schemas);
|
|
598
|
+
});
|