@zhin.js/database 1.0.4 → 1.0.6
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/CHANGELOG.md +12 -0
- package/README.md +1360 -34
- package/lib/base/database.d.ts +71 -13
- package/lib/base/database.d.ts.map +1 -1
- package/lib/base/database.js +128 -4
- package/lib/base/database.js.map +1 -1
- package/lib/base/dialect.d.ts +27 -10
- package/lib/base/dialect.d.ts.map +1 -1
- package/lib/base/dialect.js +32 -0
- package/lib/base/dialect.js.map +1 -1
- package/lib/base/index.d.ts +1 -0
- package/lib/base/index.d.ts.map +1 -1
- package/lib/base/index.js +1 -0
- package/lib/base/index.js.map +1 -1
- package/lib/base/model.d.ts +105 -12
- package/lib/base/model.d.ts.map +1 -1
- package/lib/base/model.js +224 -3
- package/lib/base/model.js.map +1 -1
- package/lib/base/query-classes.d.ts +204 -33
- package/lib/base/query-classes.d.ts.map +1 -1
- package/lib/base/query-classes.js +276 -0
- package/lib/base/query-classes.js.map +1 -1
- package/lib/base/thenable.d.ts +7 -7
- package/lib/base/thenable.d.ts.map +1 -1
- package/lib/base/thenable.js +5 -4
- package/lib/base/thenable.js.map +1 -1
- package/lib/base/transaction.d.ts +46 -0
- package/lib/base/transaction.d.ts.map +1 -0
- package/lib/base/transaction.js +186 -0
- package/lib/base/transaction.js.map +1 -0
- package/lib/dialects/memory.d.ts +12 -7
- package/lib/dialects/memory.d.ts.map +1 -1
- package/lib/dialects/memory.js +7 -4
- package/lib/dialects/memory.js.map +1 -1
- package/lib/dialects/mongodb.d.ts +11 -7
- package/lib/dialects/mongodb.d.ts.map +1 -1
- package/lib/dialects/mongodb.js +18 -15
- package/lib/dialects/mongodb.js.map +1 -1
- package/lib/dialects/mysql.d.ts +35 -6
- package/lib/dialects/mysql.d.ts.map +1 -1
- package/lib/dialects/mysql.js +137 -18
- package/lib/dialects/mysql.js.map +1 -1
- package/lib/dialects/pg.d.ts +35 -6
- package/lib/dialects/pg.d.ts.map +1 -1
- package/lib/dialects/pg.js +137 -18
- package/lib/dialects/pg.js.map +1 -1
- package/lib/dialects/redis.d.ts +11 -6
- package/lib/dialects/redis.d.ts.map +1 -1
- package/lib/dialects/redis.js +11 -8
- package/lib/dialects/redis.js.map +1 -1
- package/lib/dialects/sqlite.d.ts +19 -6
- package/lib/dialects/sqlite.d.ts.map +1 -1
- package/lib/dialects/sqlite.js +63 -10
- package/lib/dialects/sqlite.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/migration.d.ts +132 -0
- package/lib/migration.d.ts.map +1 -0
- package/lib/migration.js +475 -0
- package/lib/migration.js.map +1 -0
- package/lib/registry.d.ts +26 -23
- package/lib/registry.d.ts.map +1 -1
- package/lib/registry.js +1 -5
- package/lib/registry.js.map +1 -1
- package/lib/type/document/database.d.ts +11 -11
- package/lib/type/document/database.d.ts.map +1 -1
- package/lib/type/document/database.js.map +1 -1
- package/lib/type/document/model.d.ts +7 -7
- package/lib/type/document/model.d.ts.map +1 -1
- package/lib/type/document/model.js.map +1 -1
- package/lib/type/keyvalue/database.d.ts +11 -11
- package/lib/type/keyvalue/database.d.ts.map +1 -1
- package/lib/type/keyvalue/database.js.map +1 -1
- package/lib/type/keyvalue/model.d.ts +2 -2
- package/lib/type/keyvalue/model.d.ts.map +1 -1
- package/lib/type/keyvalue/model.js.map +1 -1
- package/lib/type/related/database.d.ts +48 -13
- package/lib/type/related/database.d.ts.map +1 -1
- package/lib/type/related/database.js +258 -27
- package/lib/type/related/database.js.map +1 -1
- package/lib/type/related/model.d.ts +251 -15
- package/lib/type/related/model.d.ts.map +1 -1
- package/lib/type/related/model.js +647 -22
- package/lib/type/related/model.js.map +1 -1
- package/lib/types.d.ts +475 -37
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +6 -0
- package/lib/types.js.map +1 -1
- package/package.json +14 -5
- package/src/base/database.ts +168 -24
- package/src/base/dialect.ts +49 -10
- package/src/base/index.ts +2 -1
- package/src/base/model.ts +258 -18
- package/src/base/query-classes.ts +471 -63
- package/src/base/thenable.ts +12 -11
- package/src/base/transaction.ts +213 -0
- package/src/dialects/memory.ts +14 -13
- package/src/dialects/mongodb.ts +40 -38
- package/src/dialects/mysql.ts +151 -22
- package/src/dialects/pg.ts +148 -21
- package/src/dialects/redis.ts +40 -38
- package/src/dialects/sqlite.ts +73 -15
- package/src/index.ts +1 -2
- package/src/migration.ts +544 -0
- package/src/registry.ts +33 -33
- package/src/type/document/database.ts +32 -32
- package/src/type/document/model.ts +14 -14
- package/src/type/keyvalue/database.ts +32 -32
- package/src/type/keyvalue/model.ts +18 -18
- package/src/type/related/database.ts +309 -34
- package/src/type/related/model.ts +800 -33
- package/src/types.ts +559 -44
- package/tests/database.test.ts +1738 -0
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import type { Database } from './database.js';
|
|
2
2
|
import type { Dialect } from './dialect.js';
|
|
3
3
|
import { ThenableQuery } from './thenable.js';
|
|
4
|
-
import { QueryParams, AlterDefinition, Condition, Ordering, Definition } from '../types.js';
|
|
4
|
+
import { QueryParams, AlterDefinition, Condition, Ordering, Definition, AggregateField, AggregateFunction, Subquery, JoinClause, JoinType, ModelOptions } from '../types.js';
|
|
5
5
|
|
|
6
|
-
|
|
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> {
|
|
7
10
|
constructor(
|
|
8
|
-
database: Database<C,
|
|
9
|
-
private readonly tableName:
|
|
10
|
-
private readonly alterations: AlterDefinition<T>
|
|
11
|
+
database: Database<C, S, D>,
|
|
12
|
+
private readonly tableName: T,
|
|
13
|
+
private readonly alterations: AlterDefinition<S[T]>
|
|
11
14
|
) {
|
|
12
|
-
super(database, database.dialect as Dialect<C, D>);
|
|
15
|
+
super(database, database.dialect as Dialect<C,S, D>);
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
protected getQueryParams(): QueryParams<T> {
|
|
18
|
+
protected getQueryParams(): QueryParams<S,T> {
|
|
16
19
|
return {
|
|
17
20
|
type: 'alter',
|
|
18
21
|
tableName: this.tableName,
|
|
@@ -21,22 +24,22 @@ export class Alteration<T extends object, C = any, D = string> extends ThenableQ
|
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
export class DroppingTable<
|
|
25
|
-
private conditions: Condition<T> = {};
|
|
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]> = {};
|
|
26
29
|
|
|
27
30
|
constructor(
|
|
28
|
-
database: Database<C,
|
|
29
|
-
private readonly tableName:
|
|
31
|
+
database: Database<C, S, any>,
|
|
32
|
+
private readonly tableName: T
|
|
30
33
|
) {
|
|
31
|
-
super(database, database.dialect as Dialect<C, D>);
|
|
34
|
+
super(database, database.dialect as Dialect<C,S, D>);
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
where(query: Condition<T>): this {
|
|
37
|
+
where(query: Condition<S[T]>): this {
|
|
35
38
|
this.conditions = { ...this.conditions, ...query };
|
|
36
39
|
return this;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
protected getQueryParams(): QueryParams<T> {
|
|
42
|
+
protected getQueryParams(): QueryParams<S,T> {
|
|
40
43
|
return {
|
|
41
44
|
type: 'drop_table',
|
|
42
45
|
tableName: this.tableName,
|
|
@@ -45,23 +48,23 @@ export class DroppingTable<T extends object = any, C = any, D = string> extends
|
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
export class DroppingIndex<C = any, D = string> extends ThenableQuery<number, C, D> {
|
|
51
|
+
export class DroppingIndex<S extends Record<string, object>, T extends keyof S,C = any, D = string> extends ThenableQuery<number,S,T, C, D> {
|
|
49
52
|
private conditions: Condition<any> = {};
|
|
50
53
|
|
|
51
54
|
constructor(
|
|
52
|
-
database: Database<C,
|
|
55
|
+
database: Database<C, S, D>,
|
|
53
56
|
private readonly indexName: string,
|
|
54
|
-
private readonly tableName:
|
|
57
|
+
private readonly tableName: T
|
|
55
58
|
) {
|
|
56
|
-
super(database, database.dialect as Dialect<C, D>);
|
|
59
|
+
super(database, database.dialect as Dialect<C,S, D>);
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
where(query: Condition<
|
|
62
|
+
where(query: Condition<S[T]>): this {
|
|
60
63
|
this.conditions = { ...this.conditions, ...query };
|
|
61
64
|
return this;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
protected getQueryParams(): QueryParams<
|
|
67
|
+
protected getQueryParams(): QueryParams<S,T> {
|
|
65
68
|
return {
|
|
66
69
|
type: 'drop_index',
|
|
67
70
|
tableName: this.tableName,
|
|
@@ -71,16 +74,16 @@ export class DroppingIndex<C = any, D = string> extends ThenableQuery<number, C,
|
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
export class Creation<T extends
|
|
77
|
+
export class Creation<S extends Record<string, object>, T extends keyof S, C = any, D = string> extends ThenableQuery<void,S,T, C, D> {
|
|
75
78
|
constructor(
|
|
76
|
-
database: Database<C,
|
|
77
|
-
private readonly tableName:
|
|
78
|
-
private readonly definition: Definition<T>
|
|
79
|
+
database: Database<C, S, D>,
|
|
80
|
+
private readonly tableName: T,
|
|
81
|
+
private readonly definition: Definition<S[T]>
|
|
79
82
|
) {
|
|
80
|
-
super(database, database.dialect as Dialect<C, D>);
|
|
83
|
+
super(database, database.dialect as Dialect<C,S, D>);
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
protected getQueryParams(): QueryParams<T> {
|
|
86
|
+
protected getQueryParams(): QueryParams<S,T> {
|
|
84
87
|
return {
|
|
85
88
|
type: 'create',
|
|
86
89
|
tableName: this.tableName,
|
|
@@ -89,37 +92,69 @@ export class Creation<T extends object, C = any, D = string> extends ThenableQue
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
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
|
+
*/
|
|
92
124
|
export class Selection<
|
|
93
|
-
T extends
|
|
94
|
-
K extends keyof T,
|
|
125
|
+
S extends Record<string, object>, T extends keyof S,
|
|
126
|
+
K extends keyof S[T],
|
|
95
127
|
C = any,
|
|
96
128
|
D = string
|
|
97
|
-
> extends ThenableQuery<Pick<T, K>[], C, D> {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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;
|
|
103
138
|
|
|
104
139
|
constructor(
|
|
105
|
-
database: Database<C,
|
|
106
|
-
|
|
107
|
-
|
|
140
|
+
database: Database<C, S, D>,
|
|
141
|
+
protected readonly modelName: T,
|
|
142
|
+
protected readonly fields: Array<K>
|
|
108
143
|
) {
|
|
109
|
-
super(database, database.dialect as Dialect<C, D>);
|
|
144
|
+
super(database, database.dialect as Dialect<C,S, D>);
|
|
110
145
|
}
|
|
111
146
|
|
|
112
|
-
where(query: Condition<T>): this {
|
|
147
|
+
where(query: Condition<S[T]>): this {
|
|
113
148
|
this.conditions = { ...this.conditions, ...query };
|
|
114
149
|
return this;
|
|
115
150
|
}
|
|
116
151
|
|
|
117
|
-
groupBy(...fields: (keyof T)[]): this {
|
|
152
|
+
groupBy(...fields: (keyof S[T])[]): this {
|
|
118
153
|
this.groupings.push(...fields);
|
|
119
154
|
return this;
|
|
120
155
|
}
|
|
121
156
|
|
|
122
|
-
orderBy(field: keyof T, direction: "ASC" | "DESC" = "ASC"): this {
|
|
157
|
+
orderBy(field: keyof S[T], direction: "ASC" | "DESC" = "ASC"): this {
|
|
123
158
|
this.orderings.push({ field, direction });
|
|
124
159
|
return this;
|
|
125
160
|
}
|
|
@@ -134,7 +169,100 @@ export class Selection<
|
|
|
134
169
|
return this;
|
|
135
170
|
}
|
|
136
171
|
|
|
137
|
-
|
|
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> {
|
|
138
266
|
return {
|
|
139
267
|
type: 'select',
|
|
140
268
|
tableName: this.modelName,
|
|
@@ -148,16 +276,158 @@ export class Selection<
|
|
|
148
276
|
}
|
|
149
277
|
}
|
|
150
278
|
|
|
151
|
-
|
|
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> {
|
|
152
422
|
constructor(
|
|
153
|
-
database: Database<C,
|
|
154
|
-
private readonly modelName:
|
|
155
|
-
private readonly data: T
|
|
423
|
+
database: Database<C, S, D>,
|
|
424
|
+
private readonly modelName: T,
|
|
425
|
+
private readonly data: S[T]
|
|
156
426
|
) {
|
|
157
|
-
super(database, database.dialect as Dialect<C, D>);
|
|
427
|
+
super(database, database.dialect as Dialect<C,S, D>);
|
|
158
428
|
}
|
|
159
429
|
|
|
160
|
-
protected getQueryParams(): QueryParams<T> {
|
|
430
|
+
protected getQueryParams(): QueryParams<S,T> {
|
|
161
431
|
return {
|
|
162
432
|
type: 'insert',
|
|
163
433
|
tableName: this.modelName,
|
|
@@ -166,23 +436,23 @@ export class Insertion<T extends object, C = any, D = string> extends ThenableQu
|
|
|
166
436
|
}
|
|
167
437
|
}
|
|
168
438
|
|
|
169
|
-
export class Updation<T extends
|
|
170
|
-
private conditions: Condition<T> = {};
|
|
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]> = {};
|
|
171
441
|
|
|
172
442
|
constructor(
|
|
173
|
-
database: Database<C,
|
|
174
|
-
private readonly modelName:
|
|
175
|
-
private readonly update: Partial<T>
|
|
443
|
+
database: Database<C, S, D>,
|
|
444
|
+
private readonly modelName: T,
|
|
445
|
+
private readonly update: Partial<S[T]>
|
|
176
446
|
) {
|
|
177
|
-
super(database, database.dialect as Dialect<C, D>);
|
|
447
|
+
super(database, database.dialect as Dialect<C,S, D>);
|
|
178
448
|
}
|
|
179
449
|
|
|
180
|
-
where(query: Condition<T>): this {
|
|
450
|
+
where(query: Condition<S[T]>): this {
|
|
181
451
|
this.conditions = { ...this.conditions, ...query };
|
|
182
452
|
return this;
|
|
183
453
|
}
|
|
184
454
|
|
|
185
|
-
protected getQueryParams(): QueryParams<T> {
|
|
455
|
+
protected getQueryParams(): QueryParams<S,T> {
|
|
186
456
|
return {
|
|
187
457
|
type: 'update',
|
|
188
458
|
tableName: this.modelName,
|
|
@@ -192,22 +462,22 @@ export class Updation<T extends object, C = any, D = string> extends ThenableQue
|
|
|
192
462
|
}
|
|
193
463
|
}
|
|
194
464
|
|
|
195
|
-
export class Deletion<
|
|
196
|
-
private conditions: Condition<T> = {};
|
|
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]> = {};
|
|
197
467
|
|
|
198
468
|
constructor(
|
|
199
|
-
database: Database<C,
|
|
200
|
-
private readonly modelName:
|
|
469
|
+
database: Database<C, S, D>,
|
|
470
|
+
private readonly modelName: T
|
|
201
471
|
) {
|
|
202
|
-
super(database, database.dialect
|
|
472
|
+
super(database, database.dialect);
|
|
203
473
|
}
|
|
204
474
|
|
|
205
|
-
where(query: Condition<T>): this {
|
|
475
|
+
where(query: Condition<S[T]>): this {
|
|
206
476
|
this.conditions = { ...this.conditions, ...query };
|
|
207
477
|
return this;
|
|
208
478
|
}
|
|
209
479
|
|
|
210
|
-
protected getQueryParams(): QueryParams<T> {
|
|
480
|
+
protected getQueryParams(): QueryParams<S,T> {
|
|
211
481
|
return {
|
|
212
482
|
type: 'delete',
|
|
213
483
|
tableName: this.modelName,
|
|
@@ -215,3 +485,141 @@ export class Deletion<T extends object = any, C = any, D = string> extends Thena
|
|
|
215
485
|
};
|
|
216
486
|
}
|
|
217
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
|
+
}
|