@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,392 @@
|
|
|
1
|
+
import { Database,Dialect } from '../../base';
|
|
2
|
+
import { RelatedModel } from './model.js';
|
|
3
|
+
import {
|
|
4
|
+
QueryParams,
|
|
5
|
+
BuildQueryResult,
|
|
6
|
+
CreateQueryParams,
|
|
7
|
+
SelectQueryParams,
|
|
8
|
+
InsertQueryParams,
|
|
9
|
+
UpdateQueryParams,
|
|
10
|
+
DeleteQueryParams,
|
|
11
|
+
AlterQueryParams,
|
|
12
|
+
DropTableQueryParams,
|
|
13
|
+
DropIndexQueryParams,
|
|
14
|
+
Condition,
|
|
15
|
+
Column,
|
|
16
|
+
AddSchema,
|
|
17
|
+
ModifySchema,
|
|
18
|
+
DropSchema,
|
|
19
|
+
isCreateQuery,
|
|
20
|
+
isSelectQuery,
|
|
21
|
+
isInsertQuery,
|
|
22
|
+
isUpdateQuery,
|
|
23
|
+
isDeleteQuery,
|
|
24
|
+
isAlterQuery,
|
|
25
|
+
isDropTableQuery,
|
|
26
|
+
isDropIndexQuery,
|
|
27
|
+
} from '../../types.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 关系型数据库类
|
|
31
|
+
* 支持表、行、列的关系型数据模型
|
|
32
|
+
*/
|
|
33
|
+
export class RelatedDatabase<
|
|
34
|
+
D=any,
|
|
35
|
+
S extends Record<string, object> = Record<string, object>
|
|
36
|
+
> extends Database<D,S,string> {
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
dialect: Dialect<D,string>,
|
|
40
|
+
schemas?: Database.Schemas<S>,
|
|
41
|
+
) {
|
|
42
|
+
super(dialect,schemas);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected async initialize(): Promise<void> {
|
|
46
|
+
// 自动创建表
|
|
47
|
+
for (const [tableName, schema] of Object.entries(this.schemas || {})) {
|
|
48
|
+
await this.create(tableName, schema);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// SQL generation method
|
|
53
|
+
buildQuery<U extends object = any>(params: QueryParams<U>): BuildQueryResult<string> {
|
|
54
|
+
if (isCreateQuery(params)) {
|
|
55
|
+
return this.buildCreateQuery(params);
|
|
56
|
+
} else if (isSelectQuery(params)) {
|
|
57
|
+
return this.buildSelectQuery(params);
|
|
58
|
+
} else if (isInsertQuery(params)) {
|
|
59
|
+
return this.buildInsertQuery(params);
|
|
60
|
+
} else if (isUpdateQuery(params)) {
|
|
61
|
+
return this.buildUpdateQuery(params);
|
|
62
|
+
} else if (isDeleteQuery(params)) {
|
|
63
|
+
return this.buildDeleteQuery(params);
|
|
64
|
+
} else if (isAlterQuery(params)) {
|
|
65
|
+
return this.buildAlterQuery(params);
|
|
66
|
+
} else if (isDropTableQuery(params)) {
|
|
67
|
+
return this.buildDropTableQuery(params);
|
|
68
|
+
} else if (isDropIndexQuery(params)) {
|
|
69
|
+
return this.buildDropIndexQuery(params);
|
|
70
|
+
} else {
|
|
71
|
+
throw new Error(`Unsupported query type: ${(params as any).type}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ========================================================================
|
|
76
|
+
// CREATE TABLE Query
|
|
77
|
+
// ========================================================================
|
|
78
|
+
|
|
79
|
+
protected buildCreateQuery<T extends object>(params: CreateQueryParams<T>): BuildQueryResult<string> {
|
|
80
|
+
const columnDefs = Object.entries(params.schema).map(([field, column]) => this.formatColumnDefinition(field,column as Column));
|
|
81
|
+
const query = this.dialect.formatCreateTable(params.tableName, columnDefs);
|
|
82
|
+
return { query, params: [] };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ========================================================================
|
|
86
|
+
// SELECT Query
|
|
87
|
+
// ========================================================================
|
|
88
|
+
|
|
89
|
+
protected buildSelectQuery<T extends object>(params: SelectQueryParams<T>): BuildQueryResult<string> {
|
|
90
|
+
const fields = params.fields && params.fields.length
|
|
91
|
+
? params.fields.map(f => this.dialect.quoteIdentifier(String(f))).join(', ')
|
|
92
|
+
: '*';
|
|
93
|
+
|
|
94
|
+
let query = `SELECT ${fields} FROM ${this.dialect.quoteIdentifier(params.tableName)}`;
|
|
95
|
+
const queryParams: any[] = [];
|
|
96
|
+
|
|
97
|
+
// WHERE clause
|
|
98
|
+
if (params.conditions) {
|
|
99
|
+
const [condition, conditionParams] = this.parseCondition(params.conditions);
|
|
100
|
+
if (condition) {
|
|
101
|
+
query += ` WHERE ${condition}`;
|
|
102
|
+
queryParams.push(...conditionParams);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// GROUP BY clause
|
|
107
|
+
if (params.groupings && params.groupings.length) {
|
|
108
|
+
const groupings = params.groupings.map(f => this.dialect.quoteIdentifier(String(f))).join(', ');
|
|
109
|
+
query += ` GROUP BY ${groupings}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ORDER BY clause
|
|
113
|
+
if (params.orderings && params.orderings.length) {
|
|
114
|
+
const orderings = params.orderings
|
|
115
|
+
.map(o => `${this.dialect.quoteIdentifier(String(o.field))} ${o.direction}`)
|
|
116
|
+
.join(', ');
|
|
117
|
+
query += ` ORDER BY ${orderings}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// LIMIT and OFFSET
|
|
121
|
+
if (params.limitCount !== undefined && params.offsetCount !== undefined) {
|
|
122
|
+
query += ` ${this.dialect.formatLimitOffset(params.limitCount, params.offsetCount)}`;
|
|
123
|
+
} else if (params.limitCount !== undefined) {
|
|
124
|
+
query += ` ${this.dialect.formatLimit(params.limitCount)}`;
|
|
125
|
+
} else if (params.offsetCount !== undefined) {
|
|
126
|
+
query += ` ${this.dialect.formatOffset(params.offsetCount)}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { query, params: queryParams };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ========================================================================
|
|
133
|
+
// INSERT Query
|
|
134
|
+
// ========================================================================
|
|
135
|
+
|
|
136
|
+
protected buildInsertQuery<T extends object>(params: InsertQueryParams<T>): BuildQueryResult<string> {
|
|
137
|
+
const keys = Object.keys(params.data);
|
|
138
|
+
const columns = keys.map(k => this.dialect.quoteIdentifier(k)).join(', ');
|
|
139
|
+
const placeholders = keys.map((_, index) => this.dialect.getParameterPlaceholder(index)).join(', ');
|
|
140
|
+
|
|
141
|
+
const query = `INSERT INTO ${this.dialect.quoteIdentifier(params.tableName)} (${columns}) VALUES (${placeholders})`;
|
|
142
|
+
const values = Object.values(params.data).map(v => this.dialect.formatDefaultValue(v));
|
|
143
|
+
|
|
144
|
+
return { query, params: values };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ========================================================================
|
|
148
|
+
// UPDATE Query
|
|
149
|
+
// ========================================================================
|
|
150
|
+
|
|
151
|
+
protected buildUpdateQuery<T extends object>(params: UpdateQueryParams<T>): BuildQueryResult<string> {
|
|
152
|
+
const updateKeys = Object.keys(params.update);
|
|
153
|
+
const setClause = updateKeys
|
|
154
|
+
.map((k, index) => `${this.dialect.quoteIdentifier(k)} = ${this.dialect.getParameterPlaceholder(index)}`)
|
|
155
|
+
.join(', ');
|
|
156
|
+
|
|
157
|
+
let query = `UPDATE ${this.dialect.quoteIdentifier(params.tableName)} SET ${setClause}`;
|
|
158
|
+
const queryParams: any[] = [...Object.values(params.update)];
|
|
159
|
+
|
|
160
|
+
// WHERE clause
|
|
161
|
+
if (params.conditions) {
|
|
162
|
+
const [condition, conditionParams] = this.parseCondition(params.conditions);
|
|
163
|
+
if (condition) {
|
|
164
|
+
query += ` WHERE ${condition}`;
|
|
165
|
+
queryParams.push(...conditionParams);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return { query, params: queryParams };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ========================================================================
|
|
173
|
+
// DELETE Query
|
|
174
|
+
// ========================================================================
|
|
175
|
+
|
|
176
|
+
protected buildDeleteQuery<T extends object>(params: DeleteQueryParams<T>): BuildQueryResult<string> {
|
|
177
|
+
let query = `DELETE FROM ${this.dialect.quoteIdentifier(params.tableName)}`;
|
|
178
|
+
const queryParams: any[] = [];
|
|
179
|
+
|
|
180
|
+
// WHERE clause
|
|
181
|
+
if (params.conditions) {
|
|
182
|
+
const [condition, conditionParams] = this.parseCondition(params.conditions);
|
|
183
|
+
if (condition) {
|
|
184
|
+
query += ` WHERE ${condition}`;
|
|
185
|
+
queryParams.push(...conditionParams);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return { query, params: queryParams };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ========================================================================
|
|
193
|
+
// ALTER TABLE Query
|
|
194
|
+
// ========================================================================
|
|
195
|
+
|
|
196
|
+
protected buildAlterQuery<T extends object>(params: AlterQueryParams<T>): BuildQueryResult<string> {
|
|
197
|
+
const alterations = Object.entries(params.alterations).map(([field,alteration]) => this.formatAlteration(field, alteration as AddSchema<T> | ModifySchema<T> | DropSchema));
|
|
198
|
+
const query = this.dialect.formatAlterTable(params.tableName, alterations);
|
|
199
|
+
return { query, params: [] };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ========================================================================
|
|
203
|
+
// DROP TABLE Query
|
|
204
|
+
// ========================================================================
|
|
205
|
+
|
|
206
|
+
protected buildDropTableQuery<T extends object>(params: DropTableQueryParams<T>): BuildQueryResult<string> {
|
|
207
|
+
const query = this.dialect.formatDropTable(params.tableName, true);
|
|
208
|
+
return { query, params: [] };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ========================================================================
|
|
212
|
+
// DROP INDEX Query
|
|
213
|
+
// ========================================================================
|
|
214
|
+
|
|
215
|
+
protected buildDropIndexQuery(params: DropIndexQueryParams): BuildQueryResult<string> {
|
|
216
|
+
const query = this.dialect.formatDropIndex(params.indexName, params.tableName, true);
|
|
217
|
+
return { query, params: [] };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ========================================================================
|
|
221
|
+
// Helper Methods
|
|
222
|
+
// ========================================================================
|
|
223
|
+
|
|
224
|
+
protected formatColumnDefinition<T =any>(field: string, column: Column<T>): string {
|
|
225
|
+
const name = this.dialect.quoteIdentifier(String(field));
|
|
226
|
+
const type = this.dialect.mapColumnType(column.type);
|
|
227
|
+
const length = column.length ? `(${column.length})` : '';
|
|
228
|
+
const nullable = column.nullable === false ? ' NOT NULL' : '';
|
|
229
|
+
const primary = column.primary ? ' PRIMARY KEY' : '';
|
|
230
|
+
const unique = column.unique ? ' UNIQUE' : '';
|
|
231
|
+
const defaultVal = column.default !== undefined
|
|
232
|
+
? ` DEFAULT ${this.dialect.formatDefaultValue(column.default)}`
|
|
233
|
+
: '';
|
|
234
|
+
|
|
235
|
+
return `${name} ${type}${length}${primary}${unique}${nullable}${defaultVal}`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
protected formatAlteration<T=any>(field:string,alteration: AddSchema<T> | ModifySchema<T> | DropSchema): string {
|
|
239
|
+
const name = this.dialect.quoteIdentifier(field);
|
|
240
|
+
|
|
241
|
+
switch (alteration.action) {
|
|
242
|
+
case 'add':
|
|
243
|
+
// 将 alteration 转换为 Column 格式
|
|
244
|
+
const addColumn: Column<T> = {
|
|
245
|
+
type: alteration.type,
|
|
246
|
+
nullable: alteration.nullable,
|
|
247
|
+
default: alteration.default,
|
|
248
|
+
primary: alteration.primary,
|
|
249
|
+
length: alteration.length
|
|
250
|
+
};
|
|
251
|
+
return `ADD COLUMN ${this.formatColumnDefinition(field, addColumn)}`;
|
|
252
|
+
case 'modify':
|
|
253
|
+
const type = alteration.type ? this.dialect.mapColumnType(alteration.type) : '';
|
|
254
|
+
const length = alteration.length ? `(${alteration.length})` : '';
|
|
255
|
+
const nullable = alteration.nullable !== undefined
|
|
256
|
+
? (alteration.nullable ? ' NULL' : ' NOT NULL')
|
|
257
|
+
: '';
|
|
258
|
+
const defaultVal = alteration.default !== undefined
|
|
259
|
+
? ` DEFAULT ${this.dialect.formatDefaultValue(alteration.default)}`
|
|
260
|
+
: '';
|
|
261
|
+
return `MODIFY COLUMN ${name} ${type}${length}${nullable}${defaultVal}`;
|
|
262
|
+
case 'drop':
|
|
263
|
+
return `DROP COLUMN ${name}`;
|
|
264
|
+
default:
|
|
265
|
+
throw new Error(`Unsupported alteration action`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
protected parseCondition<T extends object>(condition: Condition<T>): [string, any[]] {
|
|
270
|
+
const clauses: string[] = [];
|
|
271
|
+
const params: any[] = [];
|
|
272
|
+
|
|
273
|
+
for (const key in condition) {
|
|
274
|
+
if (key === '$and' && Array.isArray((condition as any).$and)) {
|
|
275
|
+
const subClauses: string[] = [];
|
|
276
|
+
for (const subCondition of (condition as any).$and) {
|
|
277
|
+
const [subClause, subParams] = this.parseCondition(subCondition);
|
|
278
|
+
if (subClause) {
|
|
279
|
+
subClauses.push(`(${subClause})`);
|
|
280
|
+
params.push(...subParams);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (subClauses.length) {
|
|
284
|
+
clauses.push(subClauses.join(' AND '));
|
|
285
|
+
}
|
|
286
|
+
} else if (key === '$or' && Array.isArray((condition as any).$or)) {
|
|
287
|
+
const subClauses: string[] = [];
|
|
288
|
+
for (const subCondition of (condition as any).$or) {
|
|
289
|
+
const [subClause, subParams] = this.parseCondition(subCondition);
|
|
290
|
+
if (subClause) {
|
|
291
|
+
subClauses.push(`(${subClause})`);
|
|
292
|
+
params.push(...subParams);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (subClauses.length) {
|
|
296
|
+
clauses.push(subClauses.join(' OR '));
|
|
297
|
+
}
|
|
298
|
+
} else if (key === '$not' && (condition as any).$not) {
|
|
299
|
+
const [subClause, subParams] = this.parseCondition((condition as any).$not);
|
|
300
|
+
if (subClause) {
|
|
301
|
+
clauses.push(`NOT (${subClause})`);
|
|
302
|
+
params.push(...subParams);
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
const value = (condition as any)[key];
|
|
306
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
307
|
+
for (const op in value) {
|
|
308
|
+
const quotedKey = this.dialect.quoteIdentifier(key);
|
|
309
|
+
const placeholder = this.dialect.getParameterPlaceholder(params.length);
|
|
310
|
+
|
|
311
|
+
switch (op) {
|
|
312
|
+
case '$eq':
|
|
313
|
+
clauses.push(`${quotedKey} = ${placeholder}`);
|
|
314
|
+
params.push(value[op]);
|
|
315
|
+
break;
|
|
316
|
+
case '$ne':
|
|
317
|
+
clauses.push(`${quotedKey} <> ${placeholder}`);
|
|
318
|
+
params.push(value[op]);
|
|
319
|
+
break;
|
|
320
|
+
case '$gt':
|
|
321
|
+
clauses.push(`${quotedKey} > ${placeholder}`);
|
|
322
|
+
params.push(value[op]);
|
|
323
|
+
break;
|
|
324
|
+
case '$gte':
|
|
325
|
+
clauses.push(`${quotedKey} >= ${placeholder}`);
|
|
326
|
+
params.push(value[op]);
|
|
327
|
+
break;
|
|
328
|
+
case '$lt':
|
|
329
|
+
clauses.push(`${quotedKey} < ${placeholder}`);
|
|
330
|
+
params.push(value[op]);
|
|
331
|
+
break;
|
|
332
|
+
case '$lte':
|
|
333
|
+
clauses.push(`${quotedKey} <= ${placeholder}`);
|
|
334
|
+
params.push(value[op]);
|
|
335
|
+
break;
|
|
336
|
+
case '$in':
|
|
337
|
+
if (Array.isArray(value[op]) && value[op].length) {
|
|
338
|
+
const placeholders = value[op].map(() => this.dialect.getParameterPlaceholder(params.length + value[op].indexOf(value[op])));
|
|
339
|
+
clauses.push(`${quotedKey} IN (${placeholders.join(', ')})`);
|
|
340
|
+
params.push(...value[op]);
|
|
341
|
+
} else {
|
|
342
|
+
clauses.push('1=0'); // Empty IN clause should yield no results
|
|
343
|
+
}
|
|
344
|
+
break;
|
|
345
|
+
case '$nin':
|
|
346
|
+
if (Array.isArray(value[op]) && value[op].length) {
|
|
347
|
+
const placeholders = value[op].map(() => this.dialect.getParameterPlaceholder(params.length + value[op].indexOf(value[op])));
|
|
348
|
+
clauses.push(`${quotedKey} NOT IN (${placeholders.join(', ')})`);
|
|
349
|
+
params.push(...value[op]);
|
|
350
|
+
}
|
|
351
|
+
break;
|
|
352
|
+
case '$like':
|
|
353
|
+
clauses.push(`${quotedKey} LIKE ${placeholder}`);
|
|
354
|
+
params.push(value[op]);
|
|
355
|
+
break;
|
|
356
|
+
case '$nlike':
|
|
357
|
+
clauses.push(`${quotedKey} NOT LIKE ${placeholder}`);
|
|
358
|
+
params.push(value[op]);
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
const quotedKey = this.dialect.quoteIdentifier(key);
|
|
364
|
+
const placeholder = this.dialect.getParameterPlaceholder(params.length);
|
|
365
|
+
clauses.push(`${quotedKey} = ${placeholder}`);
|
|
366
|
+
params.push(value);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return [clauses.join(' AND '), params];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* 获取模型
|
|
376
|
+
*/
|
|
377
|
+
model<T extends keyof S>(name: T): RelatedModel<S[T], Dialect<D,string>> {
|
|
378
|
+
let model = this.models.get(name as string);
|
|
379
|
+
if (!model) {
|
|
380
|
+
model = new RelatedModel(this as unknown as RelatedDatabase<D>, name as string);
|
|
381
|
+
this.models.set(name as string, model);
|
|
382
|
+
}
|
|
383
|
+
return model as unknown as RelatedModel<S[T], Dialect<D,string>>;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* 获取所有模型名称
|
|
388
|
+
*/
|
|
389
|
+
getModelNames(): string[] {
|
|
390
|
+
return Object.keys(this.schemas || {});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Model} from '../../base';
|
|
2
|
+
import { RelatedDatabase } from './database.js';
|
|
3
|
+
import { Condition } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 关系型模型类
|
|
7
|
+
* 继承自 BaseModel,提供关系型数据库特有的操作
|
|
8
|
+
*/
|
|
9
|
+
export class RelatedModel<T extends object = object,D=any> extends Model<D,T,string> {
|
|
10
|
+
constructor(
|
|
11
|
+
database: RelatedDatabase<D>,
|
|
12
|
+
name: string
|
|
13
|
+
) {
|
|
14
|
+
super(database, name);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 创建数据
|
|
19
|
+
*/
|
|
20
|
+
async create(data: Partial<T>): Promise<T> {
|
|
21
|
+
if (!this.validateData(data)) {
|
|
22
|
+
throw new Error('Invalid data provided');
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const result = await (this.database as RelatedDatabase<D>).insert<T>(this.name, data as T);
|
|
26
|
+
return result as T;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
this.handleError(error as Error, 'create');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 批量创建数据
|
|
34
|
+
*/
|
|
35
|
+
async createMany(data: Partial<T>[]): Promise<T[]> {
|
|
36
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
37
|
+
throw new Error('Invalid data array provided');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const results = [];
|
|
42
|
+
for (const item of data) {
|
|
43
|
+
const result = await this.create(item);
|
|
44
|
+
results.push(result);
|
|
45
|
+
}
|
|
46
|
+
return results;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
this.handleError(error as Error, 'createMany');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 查找单个数据
|
|
53
|
+
*/
|
|
54
|
+
async selectOne(query?: Condition<T>): Promise<T | null> {
|
|
55
|
+
try {
|
|
56
|
+
const selection = this.select();
|
|
57
|
+
if (query) {
|
|
58
|
+
selection.where(query);
|
|
59
|
+
}
|
|
60
|
+
const results = await selection.limit(1);
|
|
61
|
+
return results.length > 0 ? results[0] : null;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
this.handleError(error as Error, 'selectOne');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 根据ID查找
|
|
69
|
+
*/
|
|
70
|
+
async selectById(id: any): Promise<T | null> {
|
|
71
|
+
return this.selectOne({ id } as Condition<T>);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 更新单个数据
|
|
76
|
+
*/
|
|
77
|
+
async updateOne(query: Condition<T>, data: Partial<T>): Promise<boolean> {
|
|
78
|
+
try {
|
|
79
|
+
const result = await this.update(data).where(query);
|
|
80
|
+
return result > 0;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
this.handleError(error as Error, 'updateOne');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 根据ID更新
|
|
88
|
+
*/
|
|
89
|
+
async updateById(id: any, data: Partial<T>): Promise<boolean> {
|
|
90
|
+
return this.updateOne({ id } as Condition<T>, data);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 根据ID删除
|
|
96
|
+
*/
|
|
97
|
+
async deleteById(id: any): Promise<boolean> {
|
|
98
|
+
const result=await this.delete({ id } as Condition<T>);
|
|
99
|
+
return result>0
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 统计数量
|
|
104
|
+
*/
|
|
105
|
+
async count(query?: Condition<T>): Promise<number> {
|
|
106
|
+
try {
|
|
107
|
+
const selection = this.select('*' as keyof T);
|
|
108
|
+
if (query) {
|
|
109
|
+
selection.where(query);
|
|
110
|
+
}
|
|
111
|
+
const results = await selection;
|
|
112
|
+
return results.length;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
this.handleError(error as Error, 'count');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|