@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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/lib/base/database.d.ts +71 -0
  4. package/lib/base/database.d.ts.map +1 -0
  5. package/lib/base/database.js +101 -0
  6. package/lib/base/database.js.map +1 -0
  7. package/lib/base/dialect.d.ts +34 -0
  8. package/lib/base/dialect.d.ts.map +1 -0
  9. package/lib/base/dialect.js +21 -0
  10. package/lib/base/dialect.js.map +1 -0
  11. package/lib/base/index.d.ts +6 -0
  12. package/lib/base/index.d.ts.map +1 -0
  13. package/lib/base/index.js +6 -0
  14. package/lib/base/index.js.map +1 -0
  15. package/lib/base/model.d.ts +53 -0
  16. package/lib/base/model.d.ts.map +1 -0
  17. package/lib/base/model.js +65 -0
  18. package/lib/base/model.js.map +1 -0
  19. package/lib/base/query-classes.d.ts +68 -0
  20. package/lib/base/query-classes.d.ts.map +1 -0
  21. package/lib/base/query-classes.js +178 -0
  22. package/lib/base/query-classes.js.map +1 -0
  23. package/lib/base/thenable.d.ts +15 -0
  24. package/lib/base/thenable.d.ts.map +1 -0
  25. package/lib/base/thenable.js +33 -0
  26. package/lib/base/thenable.js.map +1 -0
  27. package/lib/dialects/memory.d.ts +52 -0
  28. package/lib/dialects/memory.d.ts.map +1 -0
  29. package/lib/dialects/memory.js +772 -0
  30. package/lib/dialects/memory.js.map +1 -0
  31. package/lib/dialects/mongodb.d.ts +85 -0
  32. package/lib/dialects/mongodb.d.ts.map +1 -0
  33. package/lib/dialects/mongodb.js +461 -0
  34. package/lib/dialects/mongodb.js.map +1 -0
  35. package/lib/dialects/mysql.d.ts +33 -0
  36. package/lib/dialects/mysql.d.ts.map +1 -0
  37. package/lib/dialects/mysql.js +132 -0
  38. package/lib/dialects/mysql.js.map +1 -0
  39. package/lib/dialects/pg.d.ts +33 -0
  40. package/lib/dialects/pg.d.ts.map +1 -0
  41. package/lib/dialects/pg.js +132 -0
  42. package/lib/dialects/pg.js.map +1 -0
  43. package/lib/dialects/redis.d.ts +87 -0
  44. package/lib/dialects/redis.d.ts.map +1 -0
  45. package/lib/dialects/redis.js +500 -0
  46. package/lib/dialects/redis.js.map +1 -0
  47. package/lib/dialects/sqlite.d.ts +46 -0
  48. package/lib/dialects/sqlite.d.ts.map +1 -0
  49. package/lib/dialects/sqlite.js +201 -0
  50. package/lib/dialects/sqlite.js.map +1 -0
  51. package/lib/index.d.ts +18 -0
  52. package/lib/index.d.ts.map +1 -0
  53. package/lib/index.js +18 -0
  54. package/lib/index.js.map +1 -0
  55. package/lib/registry.d.ts +37 -0
  56. package/lib/registry.d.ts.map +1 -0
  57. package/lib/registry.js +21 -0
  58. package/lib/registry.js.map +1 -0
  59. package/lib/type/document/database.d.ts +60 -0
  60. package/lib/type/document/database.d.ts.map +1 -0
  61. package/lib/type/document/database.js +242 -0
  62. package/lib/type/document/database.js.map +1 -0
  63. package/lib/type/document/model.d.ts +42 -0
  64. package/lib/type/document/model.d.ts.map +1 -0
  65. package/lib/type/document/model.js +65 -0
  66. package/lib/type/document/model.js.map +1 -0
  67. package/lib/type/keyvalue/database.d.ts +64 -0
  68. package/lib/type/keyvalue/database.d.ts.map +1 -0
  69. package/lib/type/keyvalue/database.js +214 -0
  70. package/lib/type/keyvalue/database.js.map +1 -0
  71. package/lib/type/keyvalue/model.d.ts +107 -0
  72. package/lib/type/keyvalue/model.d.ts.map +1 -0
  73. package/lib/type/keyvalue/model.js +261 -0
  74. package/lib/type/keyvalue/model.js.map +1 -0
  75. package/lib/type/related/database.d.ts +32 -0
  76. package/lib/type/related/database.d.ts.map +1 -0
  77. package/lib/type/related/database.js +334 -0
  78. package/lib/type/related/database.js.map +1 -0
  79. package/lib/type/related/model.d.ts +43 -0
  80. package/lib/type/related/model.d.ts.map +1 -0
  81. package/lib/type/related/model.js +108 -0
  82. package/lib/type/related/model.js.map +1 -0
  83. package/lib/types.d.ts +251 -0
  84. package/lib/types.d.ts.map +1 -0
  85. package/lib/types.js +28 -0
  86. package/lib/types.js.map +1 -0
  87. package/package.json +54 -0
  88. package/src/base/database.ts +128 -0
  89. package/src/base/dialect.ts +76 -0
  90. package/src/base/index.ts +5 -0
  91. package/src/base/model.ts +89 -0
  92. package/src/base/query-classes.ts +217 -0
  93. package/src/base/thenable.ts +54 -0
  94. package/src/dialects/memory.ts +880 -0
  95. package/src/dialects/mongodb.ts +533 -0
  96. package/src/dialects/mysql.ts +157 -0
  97. package/src/dialects/pg.ts +157 -0
  98. package/src/dialects/redis.ts +598 -0
  99. package/src/dialects/sqlite.ts +233 -0
  100. package/src/index.ts +20 -0
  101. package/src/registry.ts +59 -0
  102. package/src/type/document/database.ts +283 -0
  103. package/src/type/document/model.ts +86 -0
  104. package/src/type/keyvalue/database.ts +261 -0
  105. package/src/type/keyvalue/model.ts +339 -0
  106. package/src/type/related/database.ts +392 -0
  107. package/src/type/related/model.ts +117 -0
  108. package/src/types.ts +403 -0
  109. package/tsconfig.json +24 -0
@@ -0,0 +1,261 @@
1
+ import { Database, Dialect } from '../../base';
2
+ import { KeyValueModel } from './model.js';
3
+ import {
4
+ QueryParams,
5
+ BuildQueryResult,
6
+ KeyValueQueryResult,
7
+ KeyValueOperationResult,
8
+ CreateQueryParams,
9
+ SelectQueryParams,
10
+ InsertQueryParams,
11
+ UpdateQueryParams,
12
+ DeleteQueryParams,
13
+ AlterQueryParams,
14
+ DropTableQueryParams,
15
+ DropIndexQueryParams
16
+ } from '../../types.js';
17
+
18
+ /**
19
+ * 键值数据库类
20
+ * 支持简单的键值对存储
21
+ */
22
+ export class KeyValueDatabase<
23
+ D=any,
24
+ S extends Record<string, object> = Record<string, object>
25
+ > extends Database<D, S, KeyValueQueryResult> {
26
+
27
+ constructor(
28
+ dialect: Dialect<D,KeyValueQueryResult>,
29
+ schemas?: Database.Schemas<S>,
30
+ ) {
31
+ super(dialect, schemas);
32
+ }
33
+
34
+ protected async initialize(): Promise<void> {
35
+ // 键值数据库不需要预定义表结构
36
+ // 桶会在第一次使用时自动创建
37
+ }
38
+
39
+ /**
40
+ * 构建查询(重写基类方法)
41
+ */
42
+ buildQuery<U extends object = any>(params: QueryParams<U>): BuildQueryResult<KeyValueQueryResult> {
43
+ switch (params.type) {
44
+ case 'create':
45
+ return this.buildCreateQuery(params as CreateQueryParams<U>);
46
+ case 'select':
47
+ return this.buildSelectQuery(params as SelectQueryParams<U>);
48
+ case 'insert':
49
+ return this.buildInsertQuery(params as InsertQueryParams<U>);
50
+ case 'update':
51
+ return this.buildUpdateQuery(params as UpdateQueryParams<U>);
52
+ case 'delete':
53
+ return this.buildDeleteQuery(params as DeleteQueryParams<U>);
54
+ case 'alter':
55
+ return this.buildAlterQuery(params as AlterQueryParams<U>);
56
+ case 'drop_table':
57
+ return this.buildDropTableQuery(params as DropTableQueryParams<U>);
58
+ case 'drop_index':
59
+ return this.buildDropIndexQuery(params as DropIndexQueryParams);
60
+ default:
61
+ throw new Error(`Unsupported query type: ${(params as any).type}`);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * 构建创建桶查询
67
+ */
68
+ protected buildCreateQuery<T extends object>(params: CreateQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
69
+ return {
70
+ query: {
71
+ bucket: params.tableName,
72
+ operation: 'keys'
73
+ },
74
+ params: []
75
+ };
76
+ }
77
+
78
+ /**
79
+ * 构建查询键值查询
80
+ */
81
+ protected buildSelectQuery<T extends object>(params: SelectQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
82
+ // 键值数据库的查询通常是获取所有键或特定键
83
+ const query: KeyValueQueryResult = {
84
+ bucket: params.tableName,
85
+ operation: 'keys'
86
+ };
87
+
88
+ // 如果有条件,尝试提取键名
89
+ if (params.conditions) {
90
+ const key = this.extractKeyFromCondition(params.conditions);
91
+ if (key) {
92
+ query.operation = 'get';
93
+ query.key = key;
94
+ }
95
+ }
96
+
97
+ return {
98
+ query,
99
+ params: []
100
+ };
101
+ }
102
+
103
+ /**
104
+ * 构建插入键值查询
105
+ */
106
+ protected buildInsertQuery<T extends object>(params: InsertQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
107
+ const key = this.extractKeyFromData(params.data);
108
+
109
+ return {
110
+ query: {
111
+ bucket: params.tableName,
112
+ operation: 'set',
113
+ key: key || 'default',
114
+ value: params.data
115
+ },
116
+ params: [params.data]
117
+ };
118
+ }
119
+
120
+ /**
121
+ * 构建更新键值查询
122
+ */
123
+ protected buildUpdateQuery<T extends object>(params: UpdateQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
124
+ const key = this.extractKeyFromCondition(params.conditions);
125
+
126
+ return {
127
+ query: {
128
+ bucket: params.tableName,
129
+ operation: 'set',
130
+ key: key || 'default',
131
+ value: params.update
132
+ },
133
+ params: [params.update]
134
+ };
135
+ }
136
+
137
+ /**
138
+ * 构建删除键值查询
139
+ */
140
+ protected buildDeleteQuery<T extends object>(params: DeleteQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
141
+ const key = this.extractKeyFromCondition(params.conditions);
142
+
143
+ return {
144
+ query: {
145
+ bucket: params.tableName,
146
+ operation: 'delete',
147
+ key: key || 'default'
148
+ },
149
+ params: []
150
+ };
151
+ }
152
+
153
+ /**
154
+ * 构建修改桶查询
155
+ */
156
+ protected buildAlterQuery<T extends object>(params: AlterQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
157
+ return {
158
+ query: {
159
+ bucket: params.tableName,
160
+ operation: 'keys'
161
+ },
162
+ params: [params.alterations]
163
+ };
164
+ }
165
+
166
+ /**
167
+ * 构建删除桶查询
168
+ */
169
+ protected buildDropTableQuery<T extends object>(params: DropTableQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
170
+ return {
171
+ query: {
172
+ bucket: params.tableName,
173
+ operation: 'clear'
174
+ },
175
+ params: []
176
+ };
177
+ }
178
+
179
+ /**
180
+ * 构建删除索引查询
181
+ */
182
+ protected buildDropIndexQuery(params: DropIndexQueryParams): BuildQueryResult<KeyValueQueryResult> {
183
+ return {
184
+ query: {
185
+ bucket: params.tableName,
186
+ operation: 'keys'
187
+ },
188
+ params: [params.indexName]
189
+ };
190
+ }
191
+
192
+ /**
193
+ * 从条件中提取键名
194
+ */
195
+ private extractKeyFromCondition(condition: any): string | null {
196
+ if (typeof condition !== 'object' || condition === null) {
197
+ return null;
198
+ }
199
+
200
+ // 查找 key 字段
201
+ if ('key' in condition) {
202
+ return condition.key;
203
+ }
204
+
205
+ // 查找 id 字段
206
+ if ('id' in condition) {
207
+ return condition.id;
208
+ }
209
+
210
+ // 递归查找
211
+ for (const value of Object.values(condition)) {
212
+ if (typeof value === 'object' && value !== null) {
213
+ const result = this.extractKeyFromCondition(value);
214
+ if (result) return result;
215
+ }
216
+ }
217
+
218
+ return null;
219
+ }
220
+
221
+ /**
222
+ * 从数据中提取键名
223
+ */
224
+ private extractKeyFromData(data: any): string | null {
225
+ if (typeof data !== 'object' || data === null) {
226
+ return null;
227
+ }
228
+
229
+ // 查找 key 字段
230
+ if ('key' in data) {
231
+ return data.key;
232
+ }
233
+
234
+ // 查找 id 字段
235
+ if ('id' in data) {
236
+ return data.id;
237
+ }
238
+
239
+ return null;
240
+ }
241
+
242
+ /**
243
+ * 获取模型
244
+ */
245
+ model<T extends keyof S>(name: T): KeyValueModel<S[T], D> {
246
+ let model = this.models.get(name as string);
247
+ if (!model) {
248
+ model = new KeyValueModel(this as unknown as KeyValueDatabase<D>, name as string);
249
+ this.models.set(name as string, model);
250
+ }
251
+ return model as unknown as KeyValueModel<S[T], D>;
252
+ }
253
+
254
+ /**
255
+ * 获取所有模型名称
256
+ */
257
+ getModelNames(): string[] {
258
+ return Object.keys(this.schemas || {});
259
+ }
260
+ }
261
+
@@ -0,0 +1,339 @@
1
+ import { Model, Dialect, Database } from '../../base';
2
+ import { KeyValueDatabase } from './database.js';
3
+ import { Condition, KeyValueQueryResult } from '../../types.js';
4
+
5
+ /**
6
+ * 键值模型类
7
+ * 继承自 Model,提供键值数据库特有的操作
8
+ */
9
+ export class KeyValueModel<T extends object = object, D=any> extends Model<D, T, KeyValueQueryResult> {
10
+ constructor(
11
+ database: KeyValueDatabase<D>,
12
+ name: string
13
+ ) {
14
+ super(database, name);
15
+ }
16
+
17
+ /**
18
+ * 设置键值对
19
+ */
20
+ async set(key: string, value: any, ttl?: number): Promise<void> {
21
+ await this.dialect.query({
22
+ operation: 'set',
23
+ bucket: this.name,
24
+ key,
25
+ value,
26
+ ttl,
27
+ });
28
+ }
29
+
30
+ /**
31
+ * 获取值
32
+ */
33
+ async get<V = any>(key: string): Promise<V | null> {
34
+ const results = await this.dialect.query(
35
+ {
36
+ operation: 'get',
37
+ bucket: this.name,
38
+ key,
39
+ },
40
+ [key]
41
+ );
42
+
43
+ if (results.length === 0) {
44
+ return null;
45
+ }
46
+
47
+ const row = results[0];
48
+
49
+ // 检查是否过期
50
+ if (row.expires_at && Date.now() > row.expires_at) {
51
+ await this.deleteByKey(key);
52
+ return null;
53
+ }
54
+
55
+ return JSON.parse(row.value);
56
+ }
57
+
58
+ /**
59
+ * 删除键
60
+ */
61
+ async deleteByKey(key: string): Promise<boolean> {
62
+ const result = await this.dialect.query(
63
+ {
64
+ operation: 'delete',
65
+ bucket: this.name,
66
+ key,
67
+ },
68
+ [key]
69
+ );
70
+ return result.affectedRows > 0;
71
+ }
72
+
73
+ /**
74
+ * 检查键是否存在
75
+ */
76
+ async has(key: string): Promise<boolean> {
77
+ const results = await this.dialect.query(
78
+ {
79
+ operation: 'has',
80
+ bucket: this.name,
81
+ key,
82
+ },
83
+ [key, Date.now()]
84
+ );
85
+ return results.length > 0;
86
+ }
87
+
88
+ /**
89
+ * 获取所有键
90
+ */
91
+ async keys(): Promise<string[]> {
92
+ const results = await this.dialect.query(
93
+ {
94
+ operation: 'keys',
95
+ bucket: this.name,
96
+ },
97
+ [Date.now()]
98
+ );
99
+ return results.map((row: any) => row.key);
100
+ }
101
+
102
+ /**
103
+ * 获取所有值
104
+ */
105
+ async values<V = any>(): Promise<V[]> {
106
+ const results = await this.dialect.query(
107
+ {
108
+ operation: 'values',
109
+ bucket: this.name,
110
+ },
111
+ [Date.now()]
112
+ );
113
+ return results.map((row: any) => JSON.parse(row.value));
114
+ }
115
+
116
+ /**
117
+ * 获取所有键值对
118
+ */
119
+ async entries<V = any>(): Promise<Array<[string, V]>> {
120
+ const results = await this.dialect.query(
121
+ {
122
+ operation: 'entries',
123
+ bucket: this.name,
124
+ },
125
+ [Date.now()]
126
+ );
127
+ return results.map((row: any) => [row.key, JSON.parse(row.value)]);
128
+ }
129
+
130
+ /**
131
+ * 清空桶
132
+ */
133
+ async clear(): Promise<void> {
134
+ await this.dialect.query({
135
+ operation: 'clear',
136
+ bucket: this.name,
137
+ });
138
+ }
139
+
140
+ /**
141
+ * 获取桶大小
142
+ */
143
+ async size(): Promise<number> {
144
+ const results = await this.dialect.query(
145
+ {
146
+ operation: 'size',
147
+ bucket: this.name,
148
+ },
149
+ [Date.now()]
150
+ );
151
+ return results[0]?.count || 0;
152
+ }
153
+
154
+ /**
155
+ * 批量设置
156
+ */
157
+ async setMany(entries: Array<[string, any]>, ttl?: number): Promise<void> {
158
+ const expiresAt = ttl ? Date.now() + ttl * 1000 : null;
159
+
160
+ for (const [key, value] of entries) {
161
+ await this.dialect.query(
162
+ {
163
+ operation: 'set',
164
+ bucket: this.name,
165
+ key,
166
+ value,
167
+ ttl,
168
+ },
169
+ [key, JSON.stringify(value), expiresAt]
170
+ );
171
+ }
172
+ }
173
+
174
+ /**
175
+ * 设置过期时间
176
+ */
177
+ async expire(key: string, ttl: number): Promise<boolean> {
178
+ const expiresAt = Date.now() + ttl * 1000;
179
+ const result = await this.dialect.query(
180
+ {
181
+ operation: 'expire',
182
+ bucket: this.name,
183
+ key,
184
+ ttl,
185
+ },
186
+ [expiresAt, key]
187
+ );
188
+ return result.affectedRows > 0;
189
+ }
190
+
191
+ /**
192
+ * 获取剩余过期时间(秒)
193
+ */
194
+ async ttl(key: string): Promise<number | null> {
195
+ const results = await this.dialect.query(
196
+ {
197
+ operation: 'ttl',
198
+ bucket: this.name,
199
+ key,
200
+ },
201
+ [key]
202
+ );
203
+
204
+ if (results.length === 0) {
205
+ return null;
206
+ }
207
+
208
+ const expiresAt = results[0].expires_at;
209
+ if (!expiresAt) {
210
+ return -1; // 永不过期
211
+ }
212
+
213
+ const remaining = Math.ceil((expiresAt - Date.now()) / 1000);
214
+ return remaining > 0 ? remaining : 0;
215
+ }
216
+
217
+ /**
218
+ * 移除过期时间
219
+ */
220
+ async persist(key: string): Promise<boolean> {
221
+ const result = await this.dialect.query(
222
+ {
223
+ operation: 'persist',
224
+ bucket: this.name,
225
+ key,
226
+ },
227
+ [key]
228
+ );
229
+ return result.affectedRows > 0;
230
+ }
231
+
232
+ /**
233
+ * 清理过期键
234
+ */
235
+ async cleanup(): Promise<number> {
236
+ const result = await this.dialect.query(
237
+ {
238
+ operation: 'cleanup',
239
+ bucket: this.name,
240
+ },
241
+ [Date.now()]
242
+ );
243
+ return result.affectedRows;
244
+ }
245
+
246
+ /**
247
+ * 获取键的模式匹配
248
+ */
249
+ async keysByPattern(pattern: string): Promise<string[]> {
250
+ // 简单的通配符匹配,将 * 转换为 SQL 的 %
251
+ const sqlPattern = pattern.replace(/\*/g, '%');
252
+ const results = await this.dialect.query(
253
+ {
254
+ operation: 'keysByPattern',
255
+ bucket: this.name,
256
+ pattern: sqlPattern,
257
+ },
258
+ [sqlPattern, Date.now()]
259
+ );
260
+ return results.map((row: any) => row.key);
261
+ }
262
+
263
+ /**
264
+ * 原子操作:如果不存在则设置
265
+ */
266
+ async setIfNotExists(key: string, value: any, ttl?: number): Promise<boolean> {
267
+ const exists = await this.has(key);
268
+ if (exists) {
269
+ return false;
270
+ }
271
+
272
+ await this.set(key, value, ttl);
273
+ return true;
274
+ }
275
+
276
+ /**
277
+ * 原子操作:如果存在则设置
278
+ */
279
+ async setIfExists(key: string, value: any, ttl?: number): Promise<boolean> {
280
+ const exists = await this.has(key);
281
+ if (!exists) {
282
+ return false;
283
+ }
284
+
285
+ await this.set(key, value, ttl);
286
+ return true;
287
+ }
288
+
289
+ /**
290
+ * 原子操作:获取并设置
291
+ */
292
+ async getAndSet<V = any>(key: string, value: any, ttl?: number): Promise<V | null> {
293
+ const oldValue = await this.get<V>(key);
294
+ await this.set(key, value, ttl);
295
+ return oldValue;
296
+ }
297
+
298
+ /**
299
+ * 原子操作:删除并获取
300
+ */
301
+ async deleteAndGet<V = any>(key: string): Promise<V | null> {
302
+ const value = await this.get<V>(key);
303
+ if (value !== null) {
304
+ await this.deleteByKey(key);
305
+ }
306
+ return value;
307
+ }
308
+
309
+ // 实现 Model 的抽象方法
310
+
311
+ /**
312
+ * 创建数据(键值数据库的创建就是设置)
313
+ */
314
+ async create(data: T): Promise<T> {
315
+ // 键值数据库的创建需要特殊处理
316
+ throw new Error('KeyValue model does not support generic create. Use set() method instead.');
317
+ }
318
+
319
+ /**
320
+ * 查找单个数据
321
+ */
322
+ async selectOne(query: { key: string }): Promise<any> {
323
+ return this.get(query.key);
324
+ }
325
+
326
+ /**
327
+ * 统计数量
328
+ */
329
+ async count(): Promise<number> {
330
+ return this.size();
331
+ }
332
+
333
+ /**
334
+ * 检查是否存在
335
+ */
336
+ async exists(query: { key: string }): Promise<boolean> {
337
+ return this.has(query.key);
338
+ }
339
+ }