@zhin.js/database 1.0.45 → 1.0.47

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,625 +0,0 @@
1
- import type { Database } from './database.js';
2
- import type { Dialect } from './dialect.js';
3
- import { ThenableQuery } from './thenable.js';
4
- import { QueryParams, AlterDefinition, Condition, Ordering, Definition, AggregateField, AggregateFunction, Subquery, JoinClause, JoinType, ModelOptions } from '../types.js';
5
-
6
- /** 软删除查询模式 */
7
- export type SoftDeleteMode = 'default' | 'withTrashed' | 'onlyTrashed';
8
-
9
- export class Alteration<S extends Record<string, object>, T extends keyof S, C = any, D = string> extends ThenableQuery<void,S,T, C, D> {
10
- constructor(
11
- database: Database<C, S, D>,
12
- private readonly tableName: T,
13
- private readonly alterations: AlterDefinition<S[T]>
14
- ) {
15
- super(database, database.dialect as Dialect<C,S, D>);
16
- }
17
-
18
- protected getQueryParams(): QueryParams<S,T> {
19
- return {
20
- type: 'alter',
21
- tableName: this.tableName,
22
- alterations: this.alterations
23
- };
24
- }
25
- }
26
-
27
- export class DroppingTable<S extends Record<string, object>, T extends keyof S, C = any, D = string> extends ThenableQuery<number,S,T, C, D> {
28
- private conditions: Condition<S[T]> = {};
29
-
30
- constructor(
31
- database: Database<C, S, any>,
32
- private readonly tableName: T
33
- ) {
34
- super(database, database.dialect as Dialect<C,S, D>);
35
- }
36
-
37
- where(query: Condition<S[T]>): this {
38
- this.conditions = { ...this.conditions, ...query };
39
- return this;
40
- }
41
-
42
- protected getQueryParams(): QueryParams<S,T> {
43
- return {
44
- type: 'drop_table',
45
- tableName: this.tableName,
46
- conditions: this.conditions
47
- };
48
- }
49
- }
50
-
51
- export class DroppingIndex<S extends Record<string, object>, T extends keyof S,C = any, D = string> extends ThenableQuery<number,S,T, C, D> {
52
- private conditions: Condition<any> = {};
53
-
54
- constructor(
55
- database: Database<C, S, D>,
56
- private readonly indexName: string,
57
- private readonly tableName: T
58
- ) {
59
- super(database, database.dialect as Dialect<C,S, D>);
60
- }
61
-
62
- where(query: Condition<S[T]>): this {
63
- this.conditions = { ...this.conditions, ...query };
64
- return this;
65
- }
66
-
67
- protected getQueryParams(): QueryParams<S,T> {
68
- return {
69
- type: 'drop_index',
70
- tableName: this.tableName,
71
- indexName: this.indexName,
72
- conditions: this.conditions
73
- };
74
- }
75
- }
76
-
77
- export class Creation<S extends Record<string, object>, T extends keyof S, C = any, D = string> extends ThenableQuery<void,S,T, C, D> {
78
- constructor(
79
- database: Database<C, S, D>,
80
- private readonly tableName: T,
81
- private readonly definition: Definition<S[T]>
82
- ) {
83
- super(database, database.dialect as Dialect<C,S, D>);
84
- }
85
-
86
- protected getQueryParams(): QueryParams<S,T> {
87
- return {
88
- type: 'create',
89
- tableName: this.tableName,
90
- definition: this.definition
91
- };
92
- }
93
- }
94
-
95
- /**
96
- * SELECT 查询类
97
- * 实现 Subquery<S[T][K]> 以支持类型安全的子查询
98
- *
99
- * @template S Schema 类型
100
- * @template T 表名
101
- * @template K 选择的字段
102
- * @template C 连接类型
103
- * @template D 方言类型
104
- *
105
- * @example
106
- * ```ts
107
- * // 子查询类型推断示例
108
- * interface Schema {
109
- * users: { id: number; name: string };
110
- * orders: { id: number; userId: number; amount: number };
111
- * }
112
- *
113
- * // ✅ 正确:userId 是 number,匹配 users.id
114
- * db.select('users', ['id']).where({
115
- * id: { $in: db.select('orders', ['userId']) }
116
- * });
117
- *
118
- * // ❌ 类型错误:name 是 string,不能与 number 的 id 匹配
119
- * db.select('users', ['id']).where({
120
- * id: { $in: db.select('users', ['name']) } // Type error!
121
- * });
122
- * ```
123
- */
124
- export class Selection<
125
- S extends Record<string, object>, T extends keyof S,
126
- K extends keyof S[T],
127
- C = any,
128
- D = string
129
- > extends ThenableQuery<Pick<S[T], K>[],S,T, C, D> implements Subquery<S[T][K]> {
130
- readonly __isSubquery = true as const;
131
- readonly __returnType?: S[T][K];
132
-
133
- protected conditions: Condition<S[T]> = {};
134
- protected groupings: (keyof S[T])[] = [];
135
- protected orderings: Ordering<S[T]>[] = [];
136
- protected limitCount?: number;
137
- protected offsetCount?: number;
138
-
139
- constructor(
140
- database: Database<C, S, D>,
141
- protected readonly modelName: T,
142
- protected readonly fields: Array<K>
143
- ) {
144
- super(database, database.dialect as Dialect<C,S, D>);
145
- }
146
-
147
- where(query: Condition<S[T]>): this {
148
- this.conditions = { ...this.conditions, ...query };
149
- return this;
150
- }
151
-
152
- groupBy(...fields: (keyof S[T])[]): this {
153
- this.groupings.push(...fields);
154
- return this;
155
- }
156
-
157
- orderBy(field: keyof S[T], direction: "ASC" | "DESC" = "ASC"): this {
158
- this.orderings.push({ field, direction });
159
- return this;
160
- }
161
-
162
- limit(count: number): this {
163
- this.limitCount = count;
164
- return this;
165
- }
166
-
167
- offset(count: number): this {
168
- this.offsetCount = count;
169
- return this;
170
- }
171
-
172
- // ========================================================================
173
- // JOIN Methods - 返回带扩展类型的 JoinedSelection
174
- // ========================================================================
175
-
176
- /**
177
- * INNER JOIN - 只返回两表都有匹配的行
178
- * 返回类型扩展为包含关联表字段
179
- *
180
- * @example
181
- * ```ts
182
- * const result = await db.select('users', ['id', 'name'])
183
- * .join('orders', 'id', 'userId');
184
- * // result 类型: { users: { id, name }, orders: { id, userId, amount } }[]
185
- * ```
186
- */
187
- join<J extends keyof S>(
188
- table: J,
189
- leftField: keyof S[T],
190
- rightField: keyof S[J]
191
- ): JoinedSelection<S, T, K, J, C, D> {
192
- return new JoinedSelection<S, T, K, J, C, D>(
193
- this.database,
194
- this.modelName,
195
- this.fields,
196
- table,
197
- 'INNER',
198
- leftField,
199
- rightField,
200
- this.conditions,
201
- this.orderings,
202
- this.groupings,
203
- this.limitCount,
204
- this.offsetCount
205
- );
206
- }
207
-
208
- /**
209
- * LEFT JOIN - 返回左表所有行,右表无匹配则为 NULL
210
- */
211
- leftJoin<J extends keyof S>(
212
- table: J,
213
- leftField: keyof S[T],
214
- rightField: keyof S[J]
215
- ): JoinedSelection<S, T, K, J, C, D, true> {
216
- return new JoinedSelection<S, T, K, J, C, D, true>(
217
- this.database,
218
- this.modelName,
219
- this.fields,
220
- table,
221
- 'LEFT',
222
- leftField,
223
- rightField,
224
- this.conditions,
225
- this.orderings,
226
- this.groupings,
227
- this.limitCount,
228
- this.offsetCount
229
- );
230
- }
231
-
232
- /**
233
- * RIGHT JOIN - 返回右表所有行,左表无匹配则为 NULL
234
- */
235
- rightJoin<J extends keyof S>(
236
- table: J,
237
- leftField: keyof S[T],
238
- rightField: keyof S[J]
239
- ): JoinedSelection<S, T, K, J, C, D, false, true> {
240
- return new JoinedSelection<S, T, K, J, C, D, false, true>(
241
- this.database,
242
- this.modelName,
243
- this.fields,
244
- table,
245
- 'RIGHT',
246
- leftField,
247
- rightField,
248
- this.conditions,
249
- this.orderings,
250
- this.groupings,
251
- this.limitCount,
252
- this.offsetCount
253
- );
254
- }
255
-
256
- /**
257
- * 转换为子查询 SQL
258
- * 用于 $in / $nin 等操作符中嵌套查询
259
- */
260
- toSQL(): { sql: string; params: any[] } {
261
- const { query, params } = this.database.buildQuery(this.getQueryParams());
262
- return { sql: String(query), params };
263
- }
264
-
265
- protected getQueryParams(): QueryParams<S,T> {
266
- return {
267
- type: 'select',
268
- tableName: this.modelName,
269
- fields: this.fields,
270
- conditions: this.conditions,
271
- groupings: this.groupings,
272
- orderings: this.orderings,
273
- limitCount: this.limitCount,
274
- offsetCount: this.offsetCount
275
- };
276
- }
277
- }
278
-
279
- /**
280
- * JOIN 结果类型 - 根据表名命名空间化
281
- */
282
- type JoinResult<
283
- S extends Record<string, object>,
284
- T extends keyof S,
285
- K extends keyof S[T],
286
- J extends keyof S,
287
- LeftNullable extends boolean = false,
288
- RightNullable extends boolean = false
289
- > = {
290
- [P in T]: LeftNullable extends true ? Partial<Pick<S[T], K>> : Pick<S[T], K>;
291
- } & {
292
- [P in J]: RightNullable extends true ? Partial<S[J]> | null : S[J];
293
- };
294
-
295
- /**
296
- * JOIN 查询类 - 支持类型安全的关联查询
297
- *
298
- * @template S Schema 类型
299
- * @template T 主表名
300
- * @template K 主表选择的字段
301
- * @template J 关联表名
302
- * @template LeftNullable LEFT JOIN 时主表可能为 null
303
- * @template RightNullable RIGHT JOIN 时关联表可能为 null
304
- */
305
- export class JoinedSelection<
306
- S extends Record<string, object>,
307
- T extends keyof S,
308
- K extends keyof S[T],
309
- J extends keyof S,
310
- C = any,
311
- D = string,
312
- LeftNullable extends boolean = false,
313
- RightNullable extends boolean = false
314
- > extends ThenableQuery<JoinResult<S, T, K, J, LeftNullable, RightNullable>[], S, T, C, D> {
315
-
316
- private joinClauses: JoinClause<S, T, keyof S>[] = [];
317
-
318
- constructor(
319
- database: Database<C, S, D>,
320
- private readonly modelName: T,
321
- private readonly fields: Array<K>,
322
- joinTable: J,
323
- joinType: JoinType,
324
- leftField: keyof S[T],
325
- rightField: keyof S[J],
326
- private conditions: Condition<S[T]> = {},
327
- private orderings: Ordering<S[T]>[] = [],
328
- private groupings: (keyof S[T])[] = [],
329
- private limitCount?: number,
330
- private offsetCount?: number
331
- ) {
332
- super(database, database.dialect as Dialect<C, S, D>);
333
- this.joinClauses.push({
334
- type: joinType,
335
- table: joinTable,
336
- leftField,
337
- rightField: rightField as keyof S[keyof S]
338
- });
339
- }
340
-
341
- where(query: Condition<S[T]>): this {
342
- this.conditions = { ...this.conditions, ...query };
343
- return this;
344
- }
345
-
346
- orderBy(field: keyof S[T], direction: "ASC" | "DESC" = "ASC"): this {
347
- this.orderings.push({ field, direction });
348
- return this;
349
- }
350
-
351
- limit(count: number): this {
352
- this.limitCount = count;
353
- return this;
354
- }
355
-
356
- offset(count: number): this {
357
- this.offsetCount = count;
358
- return this;
359
- }
360
-
361
- /**
362
- * 继续 JOIN 更多表
363
- */
364
- join<J2 extends keyof S>(
365
- table: J2,
366
- leftField: keyof S[T],
367
- rightField: keyof S[J2]
368
- ): this {
369
- this.joinClauses.push({
370
- type: 'INNER',
371
- table,
372
- leftField,
373
- rightField: rightField as keyof S[keyof S]
374
- });
375
- return this;
376
- }
377
-
378
- leftJoin<J2 extends keyof S>(
379
- table: J2,
380
- leftField: keyof S[T],
381
- rightField: keyof S[J2]
382
- ): this {
383
- this.joinClauses.push({
384
- type: 'LEFT',
385
- table,
386
- leftField,
387
- rightField: rightField as keyof S[keyof S]
388
- });
389
- return this;
390
- }
391
-
392
- rightJoin<J2 extends keyof S>(
393
- table: J2,
394
- leftField: keyof S[T],
395
- rightField: keyof S[J2]
396
- ): this {
397
- this.joinClauses.push({
398
- type: 'RIGHT',
399
- table,
400
- leftField,
401
- rightField: rightField as keyof S[keyof S]
402
- });
403
- return this;
404
- }
405
-
406
- protected getQueryParams(): QueryParams<S, T> {
407
- return {
408
- type: 'select',
409
- tableName: this.modelName,
410
- fields: this.fields,
411
- conditions: this.conditions,
412
- groupings: this.groupings,
413
- orderings: this.orderings,
414
- limitCount: this.limitCount,
415
- offsetCount: this.offsetCount,
416
- joins: this.joinClauses
417
- };
418
- }
419
- }
420
-
421
- export class Insertion<S extends Record<string, object>, T extends keyof S, C = any, D = string> extends ThenableQuery<S[T], S, T, C, D> {
422
- constructor(
423
- database: Database<C, S, D>,
424
- private readonly modelName: T,
425
- private readonly data: S[T]
426
- ) {
427
- super(database, database.dialect as Dialect<C,S, D>);
428
- }
429
-
430
- protected getQueryParams(): QueryParams<S,T> {
431
- return {
432
- type: 'insert',
433
- tableName: this.modelName,
434
- data: this.data
435
- };
436
- }
437
- }
438
-
439
- export class Updation<S extends Record<string, object>, T extends keyof S, C = any, D = string> extends ThenableQuery<number,S,T, C, D> {
440
- private conditions: Condition<S[T]> = {};
441
-
442
- constructor(
443
- database: Database<C, S, D>,
444
- private readonly modelName: T,
445
- private readonly update: Partial<S[T]>
446
- ) {
447
- super(database, database.dialect as Dialect<C,S, D>);
448
- }
449
-
450
- where(query: Condition<S[T]>): this {
451
- this.conditions = { ...this.conditions, ...query };
452
- return this;
453
- }
454
-
455
- protected getQueryParams(): QueryParams<S,T> {
456
- return {
457
- type: 'update',
458
- tableName: this.modelName,
459
- update: this.update,
460
- conditions: this.conditions
461
- };
462
- }
463
- }
464
-
465
- export class Deletion<S extends Record<string, object>, T extends keyof S, C = any, D = string> extends ThenableQuery<S[T][],S,T, C, D> {
466
- private conditions: Condition<S[T]> = {};
467
-
468
- constructor(
469
- database: Database<C, S, D>,
470
- private readonly modelName: T
471
- ) {
472
- super(database, database.dialect);
473
- }
474
-
475
- where(query: Condition<S[T]>): this {
476
- this.conditions = { ...this.conditions, ...query };
477
- return this;
478
- }
479
-
480
- protected getQueryParams(): QueryParams<S,T> {
481
- return {
482
- type: 'delete',
483
- tableName: this.modelName,
484
- conditions: this.conditions
485
- };
486
- }
487
- }
488
-
489
- /**
490
- * 批量插入查询类
491
- */
492
- export class BatchInsertion<S extends Record<string, object>, T extends keyof S, C = any, D = string> extends ThenableQuery<{ affectedRows: number; insertIds?: (number | string)[] }, S, T, C, D> {
493
- constructor(
494
- database: Database<C, S, D>,
495
- private readonly modelName: T,
496
- private readonly data: S[T][]
497
- ) {
498
- super(database, database.dialect as Dialect<C,S, D>);
499
- }
500
-
501
- // 重写 then 方法来转换返回值
502
- then<TResult1 = { affectedRows: number; insertIds?: (number | string)[] }, TResult2 = never>(
503
- onfulfilled?: ((value: { affectedRows: number; insertIds?: (number | string)[] }) => TResult1 | PromiseLike<TResult1>) | undefined | null,
504
- onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
505
- ): Promise<TResult1 | TResult2> {
506
- const params = this.getQueryParams();
507
- const { query, params: queryParams } = this.database.buildQuery(params);
508
- // 使用 database.query 以支持日志记录
509
- return this.database.query<any>(query, queryParams).then((result) => {
510
- // 转换不同数据库的返回格式
511
- const normalized = {
512
- affectedRows: result?.changes ?? result?.affectedRows ?? this.data.length,
513
- insertIds: result?.lastID ? [result.lastID] : result?.insertIds
514
- };
515
- return onfulfilled ? onfulfilled(normalized) : normalized as any;
516
- }, onrejected);
517
- }
518
-
519
- protected getQueryParams(): QueryParams<S,T> {
520
- return {
521
- type: 'insert_many',
522
- tableName: this.modelName,
523
- data: this.data
524
- };
525
- }
526
- }
527
-
528
- /**
529
- * 聚合查询结果类型
530
- */
531
- export interface AggregateResult {
532
- [key: string]: number | string | null;
533
- }
534
-
535
- /**
536
- * 聚合查询类
537
- */
538
- export class Aggregation<S extends Record<string, object>, T extends keyof S, C = any, D = string> extends ThenableQuery<AggregateResult[], S, T, C, D> {
539
- private conditions: Condition<S[T]> = {};
540
- private groupings: (keyof S[T])[] = [];
541
- private havingConditions: Condition<S[T]> = {};
542
- private aggregates: AggregateField<S[T]>[] = [];
543
-
544
- constructor(
545
- database: Database<C, S, D>,
546
- private readonly modelName: T
547
- ) {
548
- super(database, database.dialect as Dialect<C,S, D>);
549
- }
550
-
551
- /**
552
- * COUNT 聚合
553
- */
554
- count(field: keyof S[T] | '*' = '*', alias?: string): this {
555
- this.aggregates.push({ fn: 'count', field, alias: alias || `count_${String(field)}` });
556
- return this;
557
- }
558
-
559
- /**
560
- * SUM 聚合
561
- */
562
- sum(field: keyof S[T], alias?: string): this {
563
- this.aggregates.push({ fn: 'sum', field, alias: alias || `sum_${String(field)}` });
564
- return this;
565
- }
566
-
567
- /**
568
- * AVG 聚合
569
- */
570
- avg(field: keyof S[T], alias?: string): this {
571
- this.aggregates.push({ fn: 'avg', field, alias: alias || `avg_${String(field)}` });
572
- return this;
573
- }
574
-
575
- /**
576
- * MIN 聚合
577
- */
578
- min(field: keyof S[T], alias?: string): this {
579
- this.aggregates.push({ fn: 'min', field, alias: alias || `min_${String(field)}` });
580
- return this;
581
- }
582
-
583
- /**
584
- * MAX 聚合
585
- */
586
- max(field: keyof S[T], alias?: string): this {
587
- this.aggregates.push({ fn: 'max', field, alias: alias || `max_${String(field)}` });
588
- return this;
589
- }
590
-
591
- /**
592
- * WHERE 条件
593
- */
594
- where(query: Condition<S[T]>): this {
595
- this.conditions = { ...this.conditions, ...query };
596
- return this;
597
- }
598
-
599
- /**
600
- * GROUP BY
601
- */
602
- groupBy(...fields: (keyof S[T])[]): this {
603
- this.groupings.push(...fields);
604
- return this;
605
- }
606
-
607
- /**
608
- * HAVING 条件(用于聚合后的过滤)
609
- */
610
- having(query: Condition<S[T]>): this {
611
- this.havingConditions = { ...this.havingConditions, ...query };
612
- return this;
613
- }
614
-
615
- protected getQueryParams(): QueryParams<S,T> {
616
- return {
617
- type: 'aggregate',
618
- tableName: this.modelName,
619
- aggregates: this.aggregates,
620
- conditions: this.conditions,
621
- groupings: this.groupings,
622
- havingConditions: this.havingConditions
623
- };
624
- }
625
- }
@@ -1,55 +0,0 @@
1
- import type { Database } from "./database.js";
2
- import type { Dialect } from "./dialect.js";
3
- import { QueryParams } from "../types.js";
4
-
5
-
6
- export abstract class ThenableQuery<R,S extends Record<string, object>, T extends keyof S,C=any,D=string>
7
- implements PromiseLike<R>, AsyncIterable<R>
8
- {
9
- protected constructor(protected readonly database: Database<C,S,D>,protected readonly dialect: Dialect<C,S,D>) {}
10
-
11
- // Abstract method to get query parameters
12
- protected abstract getQueryParams(): QueryParams<S,T>;
13
- [Symbol.toStringTag] = 'ThenableQuery';
14
- then<TResult1 = R, TResult2 = never>(
15
- onfulfilled?:
16
- | ((value: R) => TResult1 | PromiseLike<TResult1>)
17
- | undefined
18
- | null,
19
- onrejected?:
20
- | ((reason: any) => TResult2 | PromiseLike<TResult2>)
21
- | undefined
22
- | null
23
- ): Promise<TResult1 | TResult2> {
24
- const params = this.getQueryParams();
25
- const { query, params: queryParams } = this.database.buildQuery(params);
26
- // 使用 database.query 以支持日志记录
27
- return this.database.query<R>(query, queryParams).then(onfulfilled, onrejected);
28
- }
29
-
30
- catch<TResult = never>(
31
- onrejected?:
32
- | ((reason: any) => TResult | PromiseLike<TResult>)
33
- | undefined
34
- | null
35
- ): Promise<any | TResult> {
36
- const params = this.getQueryParams();
37
- const { query, params: queryParams } = this.database.buildQuery(params);
38
- return this.database.query(query, queryParams).catch(onrejected);
39
- }
40
-
41
- finally(onfinally?: (() => void) | undefined | null): Promise<any> {
42
- const params = this.getQueryParams();
43
- const { query, params: queryParams } = this.database.buildQuery(params);
44
- return this.database.query(query, queryParams).finally(onfinally);
45
- }
46
-
47
- async *[Symbol.asyncIterator](): AsyncIterator<R, void, unknown> {
48
- const params = this.getQueryParams();
49
- const { query, params: queryParams } = this.database.buildQuery(params);
50
- const rows = await this.database.query(query, queryParams);
51
- for (const row of Array.isArray(rows) ? rows : [rows]) {
52
- yield row;
53
- }
54
- }
55
- }