@zhin.js/database 1.0.45 → 1.0.48

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.
@@ -1,339 +0,0 @@
1
- import { Model, } from '../../base/index.js';
2
- import { KeyValueDatabase } from './database.js';
3
- import { KeyValueQueryResult } from '../../types.js';
4
-
5
- /**
6
- * 键值模型类
7
- * 继承自 Model,提供键值数据库特有的操作
8
- */
9
- export class KeyValueModel<D=any, S extends Record<string, object> = Record<string, object>, T extends keyof S = keyof S> extends Model<D, S, KeyValueQueryResult, T> {
10
- constructor(
11
- database: KeyValueDatabase<D, S>,
12
- name: T
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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 as string,
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
- }