fdb2 1.0.39 → 1.0.40

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 (63) hide show
  1. package/README.md +2 -1
  2. package/dist/public/explorer.css +403 -380
  3. package/dist/public/explorer.js +1294 -737
  4. package/dist/public/index.js +242 -16
  5. package/dist/public/layout.js +1 -1
  6. package/dist/scripts/verify-db-support.cjs +239 -0
  7. package/dist/server/core/dialect.d.ts +9 -0
  8. package/dist/server/core/dialect.d.ts.map +1 -1
  9. package/dist/server/core/dialect.js +24 -9
  10. package/dist/server/core/dialect.js.map +1 -1
  11. package/dist/server/core/dialect.ts +29 -9
  12. package/dist/server/core/schema/__tests__/ddl-emitter.test.ts +7 -3
  13. package/dist/server/core/schema/ddl-emitter.d.ts.map +1 -1
  14. package/dist/server/core/schema/ddl-emitter.js +32 -12
  15. package/dist/server/core/schema/ddl-emitter.js.map +1 -1
  16. package/dist/server/core/schema/ddl-emitter.ts +34 -11
  17. package/dist/server/index.d.ts.map +1 -1
  18. package/dist/server/index.js +83 -6
  19. package/dist/server/index.js.map +1 -1
  20. package/dist/server/index.ts +87 -6
  21. package/dist/server/model/ddl-spec.d.ts +9 -0
  22. package/dist/server/model/ddl-spec.d.ts.map +1 -1
  23. package/dist/server/model/ddl-spec.ts +9 -0
  24. package/dist/server/service/connection.service.d.ts +8 -0
  25. package/dist/server/service/connection.service.d.ts.map +1 -1
  26. package/dist/server/service/connection.service.js +75 -17
  27. package/dist/server/service/connection.service.js.map +1 -1
  28. package/dist/server/service/connection.service.ts +74 -17
  29. package/dist/server/service/database/base.service.d.ts +45 -0
  30. package/dist/server/service/database/base.service.d.ts.map +1 -1
  31. package/dist/server/service/database/base.service.js +120 -7
  32. package/dist/server/service/database/base.service.js.map +1 -1
  33. package/dist/server/service/database/base.service.ts +143 -8
  34. package/dist/server/service/database/cockroachdb.service.d.ts +9 -0
  35. package/dist/server/service/database/cockroachdb.service.d.ts.map +1 -1
  36. package/dist/server/service/database/cockroachdb.service.js +67 -56
  37. package/dist/server/service/database/cockroachdb.service.js.map +1 -1
  38. package/dist/server/service/database/cockroachdb.service.ts +66 -57
  39. package/dist/server/service/database/database.service.d.ts +3 -221
  40. package/dist/server/service/database/database.service.d.ts.map +1 -1
  41. package/dist/server/service/database/database.service.js +66 -24
  42. package/dist/server/service/database/database.service.js.map +1 -1
  43. package/dist/server/service/database/database.service.ts +69 -23
  44. package/dist/server/service/database/mongodb.service.d.ts +58 -3
  45. package/dist/server/service/database/mongodb.service.d.ts.map +1 -1
  46. package/dist/server/service/database/mongodb.service.js +251 -54
  47. package/dist/server/service/database/mongodb.service.js.map +1 -1
  48. package/dist/server/service/database/mongodb.service.ts +275 -60
  49. package/dist/server/service/database/mssql.service.d.ts +8 -0
  50. package/dist/server/service/database/mssql.service.d.ts.map +1 -1
  51. package/dist/server/service/database/mssql.service.js +41 -36
  52. package/dist/server/service/database/mssql.service.js.map +1 -1
  53. package/dist/server/service/database/mssql.service.ts +45 -40
  54. package/dist/server/service/database/oracle.service.d.ts +24 -0
  55. package/dist/server/service/database/oracle.service.d.ts.map +1 -1
  56. package/dist/server/service/database/oracle.service.js +146 -106
  57. package/dist/server/service/database/oracle.service.js.map +1 -1
  58. package/dist/server/service/database/oracle.service.ts +145 -105
  59. package/dist/server/service/database/sqlite.service.js +1 -1
  60. package/dist/server/service/database/sqlite.service.js.map +1 -1
  61. package/dist/server/service/database/sqlite.service.ts +1 -1
  62. package/package.json +8 -8
  63. package/scripts/verify-db-support.cjs +239 -0
@@ -1,18 +1,30 @@
1
1
  import { DataSource } from 'typeorm';
2
+ import { ObjectId } from 'mongodb';
2
3
  import { BaseDatabaseService } from './base.service';
3
- import {
4
- DatabaseEntity,
5
- TableEntity,
6
- ColumnEntity,
7
- IndexEntity,
8
- ForeignKeyEntity
4
+ import {
5
+ DatabaseEntity,
6
+ TableEntity,
7
+ ColumnEntity,
8
+ IndexEntity,
9
+ ForeignKeyEntity
9
10
  } from '../../model/database.entity';
10
11
  import * as fs from 'fs';
11
12
  import * as path from 'path';
12
13
 
14
+ /**
15
+ * MongoDB 数据库只有在拥有至少一个集合后才会被持久化并出现在 listDatabases 中。
16
+ * 创建空库时保留一个占位集合作为"存在证明",并在 getTables 中过滤掉它,避免暴露垃圾集合。
17
+ */
18
+ const DB_INIT_MARKER = '__db_tool_init';
19
+
13
20
  /**
14
21
  * MongoDB数据库服务实现
15
- * MongoDB 是一个 NoSQL 文档数据库,与关系型数据库有很大不同
22
+ * MongoDB 是一个 NoSQL 文档数据库,与关系型数据库有很大不同。
23
+ *
24
+ * 重要:TypeORM 的 MongoDB 驱动不支持通过 dataSource.query() 执行 shell 命令字符串
25
+ * (如 `db.collection.find()`),那种写法永远无法工作。这里统一通过驱动持有的
26
+ * 原生 MongoClient(dataSource.driver.queryRunner.databaseConnection)直接操作,
27
+ * 这是唯一可靠的访问方式。
16
28
  */
17
29
  export class MongoDBService extends BaseDatabaseService {
18
30
 
@@ -20,13 +32,28 @@ export class MongoDBService extends BaseDatabaseService {
20
32
  return 'mongodb';
21
33
  }
22
34
 
35
+ /**
36
+ * 获取驱动持有的原生 MongoClient
37
+ */
38
+ private nativeClient(dataSource: DataSource): any {
39
+ return (dataSource.driver as any).queryRunner.databaseConnection;
40
+ }
41
+
42
+ /**
43
+ * 获取指定数据库的原生 Db 句柄
44
+ */
45
+ private nativeDb(dataSource: DataSource, database: string): any {
46
+ return this.nativeClient(dataSource).db(database);
47
+ }
48
+
23
49
  /**
24
50
  * 获取MongoDB数据库列表
25
51
  */
26
52
  async getDatabases(dataSource: DataSource): Promise<string[]> {
27
53
  try {
28
- const result = await dataSource.query(`db.adminCommand({ listDatabases: 1 })`);
29
- return result.databases.map((db: any) => db.name).filter((name: string) => name !== 'admin' && name !== 'local' && name !== 'config');
54
+ const res = await this.nativeDb(dataSource, 'admin').admin().listDatabases();
55
+ return (res.databases || []).map((db: any) => db.name)
56
+ .filter((name: string) => name !== 'admin' && name !== 'local' && name !== 'config');
30
57
  } catch (error) {
31
58
  console.error('获取MongoDB数据库列表失败:', error);
32
59
  return [];
@@ -38,10 +65,13 @@ export class MongoDBService extends BaseDatabaseService {
38
65
  */
39
66
  async getTables(dataSource: DataSource, database: string): Promise<TableEntity[]> {
40
67
  try {
41
- const result = await dataSource.query(`db.getCollectionNames()`);
42
- return result.map((collectionName: string) => ({
43
- name: collectionName,
44
- type: 'collection',
68
+ const db = this.nativeDb(dataSource, database);
69
+ const collections = await db.listCollections().toArray();
70
+ return collections
71
+ .filter((c: any) => c.name !== DB_INIT_MARKER)
72
+ .map((c: any) => ({
73
+ name: c.name,
74
+ type: c.type === 'view' ? 'view' : 'collection',
45
75
  rowCount: undefined,
46
76
  dataSize: undefined,
47
77
  indexSize: undefined
@@ -53,19 +83,20 @@ export class MongoDBService extends BaseDatabaseService {
53
83
  }
54
84
 
55
85
  /**
56
- * 获取MongoDB集合的字段信息(文档结构)
86
+ * 获取MongoDB集合的字段信息(文档结构,取一条样本推断)
57
87
  */
58
88
  async getColumns(dataSource: DataSource, database: string, table: string): Promise<ColumnEntity[]> {
59
89
  try {
60
- const result = await dataSource.query(`db.${table}.findOne()`);
61
- if (!result) {
90
+ const db = this.nativeDb(dataSource, database);
91
+ const sample = await db.collection(table).findOne();
92
+ if (!sample) {
62
93
  return [];
63
94
  }
64
95
 
65
- const fields = Object.keys(result);
96
+ const fields = Object.keys(sample);
66
97
  return fields.map(field => ({
67
98
  name: field,
68
- type: this.inferType(result[field]),
99
+ type: this.inferType(sample[field]),
69
100
  nullable: true,
70
101
  defaultValue: undefined,
71
102
  isPrimary: field === '_id',
@@ -107,13 +138,143 @@ export class MongoDBService extends BaseDatabaseService {
107
138
  return 'unknown';
108
139
  }
109
140
 
141
+ // ---------------------------------------------------------------- 文档查询辅助
142
+
143
+ /**
144
+ * 过滤条件:接受 JSON 对象或 JSON 字符串(如 {"status":"active"})。
145
+ * SQL WHERE 一律明确拒绝——静默忽略会变成"查全表",比报错更危险。
146
+ */
147
+ private parseFilter(where?: string | Record<string, any>): Record<string, any> {
148
+ if (!where) return {};
149
+ if (typeof where === 'object') return where;
150
+ if (typeof where === 'string') {
151
+ const t = where.trim();
152
+ if (!t) return {};
153
+ try {
154
+ const parsed = JSON.parse(t);
155
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed;
156
+ } catch {
157
+ throw new Error('MongoDB 过滤条件必须是 JSON(如 {"status":"active"}),不支持 SQL WHERE');
158
+ }
159
+ throw new Error('MongoDB 过滤条件必须是 JSON 对象(如 {"status":"active"})');
160
+ }
161
+ return {};
162
+ }
163
+
164
+ /** 排序:支持 {"name":1} / {"name":-1} / "name ASC, age DESC" */
165
+ private parseSort(orderBy?: string | Record<string, any>): Record<string, 1 | -1> | undefined {
166
+ if (!orderBy) return undefined;
167
+ if (typeof orderBy === 'object') {
168
+ const out: Record<string, 1 | -1> = {};
169
+ for (const k of Object.keys(orderBy)) out[k] = Number((orderBy as any)[k]) < 0 ? -1 : 1;
170
+ return Object.keys(out).length ? out : undefined;
171
+ }
172
+ const t = orderBy.trim();
173
+ if (!t) return undefined;
174
+ if (t.startsWith('{')) {
175
+ try {
176
+ return this.parseSort(JSON.parse(t));
177
+ } catch {
178
+ throw new Error('MongoDB 排序条件必须是 JSON(如 {"name":1})');
179
+ }
180
+ }
181
+ const out: Record<string, 1 | -1> = {};
182
+ for (const part of t.split(',')) {
183
+ const seg = part.trim().split(/\s+/).filter(Boolean);
184
+ if (!seg.length) continue;
185
+ out[seg[0]] = /desc/i.test(seg[1] || '') ? -1 : 1;
186
+ }
187
+ return Object.keys(out).length ? out : undefined;
188
+ }
189
+
190
+ /** 24 位十六进制字符串转 ObjectId(否则按原值匹配,兼容非 ObjectId 主键) */
191
+ private toObjectId(id: any): any {
192
+ if (id instanceof ObjectId) return id;
193
+ if (typeof id === 'string' && /^[0-9a-fA-F]{24}$/.test(id)) {
194
+ try { return new ObjectId(id); } catch { return id; }
195
+ }
196
+ return id;
197
+ }
198
+
199
+ /** 过滤条件中的 _id 做 ObjectId 归一化(含 $in/$nin 数组与操作符对象) */
200
+ private normalizeId(filter: Record<string, any>): Record<string, any> {
201
+ if (!filter || typeof filter !== 'object') return filter;
202
+ const out: Record<string, any> = { ...filter };
203
+ if (out._id === undefined) return out;
204
+ const conv = (v: any): any => {
205
+ if (Array.isArray(v)) return v.map(conv);
206
+ if (v && typeof v === 'object') {
207
+ const r: Record<string, any> = {};
208
+ for (const k of Object.keys(v)) r[k] = conv(v[k]);
209
+ return r;
210
+ }
211
+ return this.toObjectId(v);
212
+ };
213
+ out._id = conv(out._id);
214
+ return out;
215
+ }
216
+
217
+ /**
218
+ * 获取集合数据(文档模式):原生 find + sort + skip + limit,不走 SQL。
219
+ */
220
+ async getTableData(
221
+ dataSource: DataSource,
222
+ databaseName: string,
223
+ tableName: string,
224
+ page: number = 1,
225
+ pageSize: number = 100,
226
+ where?: string | Record<string, any>,
227
+ orderBy?: string | Record<string, any>
228
+ ): Promise<{ data: any[], total: number }> {
229
+ try {
230
+ const col = this.nativeDb(dataSource, databaseName).collection(tableName);
231
+ const filter = this.normalizeId(this.parseFilter(where));
232
+ const sort = this.parseSort(orderBy);
233
+ const total = await col.countDocuments(filter);
234
+ const cursor = col.find(filter);
235
+ if (sort) cursor.sort(sort);
236
+ const data = await cursor
237
+ .skip(Math.max(0, (page - 1) * pageSize))
238
+ .limit(pageSize)
239
+ .toArray();
240
+ return { data, total };
241
+ } catch (error) {
242
+ console.error('获取MongoDB集合数据失败:', error);
243
+ throw new Error(`获取集合数据失败: ${error instanceof Error ? error.message : String(error)}`);
244
+ }
245
+ }
246
+
247
+ /**
248
+ * 集合详情:在原生元数据(字段/索引)之上补 collStats(文档数/数据大小/索引大小)
249
+ */
250
+ async getTableInfo(dataSource: DataSource, databaseName: string, tableName: string): Promise<TableEntity> {
251
+ const table = await super.getTableInfo(dataSource, databaseName, tableName);
252
+ try {
253
+ const stats = await this.nativeDb(dataSource, databaseName).command({ collStats: tableName });
254
+ table.rowCount = stats?.count;
255
+ table.dataSize = stats?.size;
256
+ table.indexSize = stats?.totalIndexSize;
257
+ } catch {
258
+ // collStats 可能因权限不足失败:保持 undefined,不影响主流程
259
+ }
260
+ return table;
261
+ }
262
+
263
+ /**
264
+ * MongoDB 不支持 SQL:给出明确错误,而不是让驱动抛 "Queries aren't supported by MongoDB"
265
+ */
266
+ async executeQuery(_dataSource: DataSource, _sql: string): Promise<any> {
267
+ throw new Error('MongoDB 不支持 SQL 查询,请使用文档查询(JSON 过滤条件)');
268
+ }
269
+
110
270
  /**
111
271
  * 获取MongoDB索引信息
112
272
  */
113
273
  async getIndexes(dataSource: DataSource, database: string, table: string): Promise<IndexEntity[]> {
114
274
  try {
115
- const result = await dataSource.query(`db.${table}.getIndexes()`);
116
- return result.map((index: any) => ({
275
+ const db = this.nativeDb(dataSource, database);
276
+ const indexes = await db.collection(table).indexes();
277
+ return indexes.map((index: any) => ({
117
278
  name: index.name,
118
279
  type: index.unique ? 'UNIQUE' : 'INDEX',
119
280
  columns: Object.keys(index.key),
@@ -137,8 +298,9 @@ export class MongoDBService extends BaseDatabaseService {
137
298
  */
138
299
  async getDatabaseSize(dataSource: DataSource, database: string): Promise<number> {
139
300
  try {
140
- const result = await dataSource.query(`db.stats({ scale: 1 })`);
141
- return result.dataSize || 0;
301
+ const db = this.nativeDb(dataSource, database);
302
+ const stats = await db.stats();
303
+ return stats.dataSize || 0;
142
304
  } catch (error) {
143
305
  console.error('获取MongoDB数据库大小失败:', error);
144
306
  return 0;
@@ -174,12 +336,17 @@ export class MongoDBService extends BaseDatabaseService {
174
336
  }
175
337
 
176
338
  /**
177
- * 创建MongoDB数据库
339
+ * 创建MongoDB数据库(通过创建再删除一个临时集合来物化数据库)
178
340
  */
179
341
  async createDatabase(dataSource: DataSource, databaseName: string, options?: any): Promise<void> {
180
342
  try {
181
- await dataSource.query(`db.getSiblingDB('${databaseName}').createCollection('temp')`);
182
- await dataSource.query(`db.getSiblingDB('${databaseName}').temp.drop()`);
343
+ const db = this.nativeDb(dataSource, databaseName);
344
+ // MongoDB 没有"空数据库"的概念:数据库只有在拥有至少一个集合后才会被持久化,
345
+ // 并出现在 listDatabases 中。因此保留一个占位集合,否则刚创建的库在 getDatabases 中不可见。
346
+ const existing = await db.listCollections({ name: DB_INIT_MARKER }).toArray();
347
+ if (existing.length === 0) {
348
+ await db.createCollection(DB_INIT_MARKER);
349
+ }
183
350
  } catch (error) {
184
351
  console.error('创建MongoDB数据库失败:', error);
185
352
  throw new Error(`创建数据库失败: ${error.message}`);
@@ -191,7 +358,7 @@ export class MongoDBService extends BaseDatabaseService {
191
358
  */
192
359
  async dropDatabase(dataSource: DataSource, databaseName: string): Promise<void> {
193
360
  try {
194
- await dataSource.query(`db.getSiblingDB('${databaseName}').dropDatabase()`);
361
+ await this.nativeDb(dataSource, databaseName).dropDatabase();
195
362
  } catch (error) {
196
363
  console.error('删除MongoDB数据库失败:', error);
197
364
  throw new Error(`删除数据库失败: ${error.message}`);
@@ -234,8 +401,8 @@ export class MongoDBService extends BaseDatabaseService {
234
401
  */
235
402
  async viewLogs(dataSource: DataSource, database?: string, limit: number = 100): Promise<any[]> {
236
403
  try {
237
- const result = await dataSource.query(`db.getSiblingDB('admin').adminCommand({ getLog: 'global' })`);
238
- return result.log || [];
404
+ const res = await this.nativeDb(dataSource, 'admin').admin().command({ getLog: 'global' });
405
+ return (res.log || []).slice(0, limit);
239
406
  } catch (error) {
240
407
  console.error('获取MongoDB日志失败:', error);
241
408
  return [{ message: 'MongoDB日志功能需要管理员权限,请检查数据库配置' }];
@@ -255,6 +422,7 @@ export class MongoDBService extends BaseDatabaseService {
255
422
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
256
423
  const backupFile = path.join(backupPath, `${databaseName}_${timestamp}.json`);
257
424
 
425
+ const db = this.nativeDb(dataSource, databaseName);
258
426
  const collections = await this.getTables(dataSource, databaseName);
259
427
  const backupData: any = {
260
428
  database: databaseName,
@@ -263,8 +431,8 @@ export class MongoDBService extends BaseDatabaseService {
263
431
  };
264
432
 
265
433
  for (const collection of collections) {
266
- const result = await dataSource.query(`db.${collection.name}.find().toArray()`);
267
- backupData.collections[collection.name] = result;
434
+ const docs = await db.collection(collection.name).find().toArray();
435
+ backupData.collections[collection.name] = docs;
268
436
  }
269
437
 
270
438
  fs.writeFileSync(backupFile, JSON.stringify(backupData, null, 2), 'utf8');
@@ -281,11 +449,12 @@ export class MongoDBService extends BaseDatabaseService {
281
449
  async restoreDatabase(dataSource: DataSource, databaseName: string, filePath: string, options?: any): Promise<void> {
282
450
  try {
283
451
  const backupData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
452
+ const db = this.nativeDb(dataSource, databaseName);
284
453
 
285
454
  for (const collectionName in backupData.collections) {
286
455
  const documents = backupData.collections[collectionName];
287
456
  if (documents.length > 0) {
288
- await dataSource.query(`db.${collectionName}.insertMany(${JSON.stringify(documents)})`);
457
+ await db.collection(collectionName).insertMany(documents);
289
458
  }
290
459
  }
291
460
  } catch (error) {
@@ -314,8 +483,9 @@ export class MongoDBService extends BaseDatabaseService {
314
483
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
315
484
  const exportFile = path.join(exportPath, `${tableName}_data_${timestamp}.csv`);
316
485
 
317
- const result = await dataSource.query(`db.${tableName}.find().toArray()`);
318
-
486
+ const db = this.nativeDb(dataSource, databaseName);
487
+ const result = await db.collection(tableName).find().toArray();
488
+
319
489
  if (result.length === 0) {
320
490
  fs.writeFileSync(exportFile, '', 'utf8');
321
491
  return exportFile;
@@ -372,7 +542,8 @@ export class MongoDBService extends BaseDatabaseService {
372
542
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
373
543
  const exportFile = path.join(exportPath, `${tableName}_data_${timestamp}.json`);
374
544
 
375
- const result = await dataSource.query(`db.${tableName}.find().toArray()`);
545
+ const db = this.nativeDb(dataSource, databaseName);
546
+ const result = await db.collection(tableName).find().toArray();
376
547
  fs.writeFileSync(exportFile, JSON.stringify(result, null, 2), 'utf8');
377
548
  return exportFile;
378
549
  } catch (error) {
@@ -394,7 +565,8 @@ export class MongoDBService extends BaseDatabaseService {
394
565
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
395
566
  const exportFile = path.join(exportPath, `${tableName}_data_${timestamp}.xlsx`);
396
567
 
397
- const result = await dataSource.query(`db.${tableName}.find().toArray()`);
568
+ const db = this.nativeDb(dataSource, databaseName);
569
+ const result = await db.collection(tableName).find().toArray();
398
570
  const columns = await this.getColumns(dataSource, databaseName, tableName);
399
571
  const columnNames = columns.map(column => column.name);
400
572
 
@@ -443,27 +615,24 @@ export class MongoDBService extends BaseDatabaseService {
443
615
  */
444
616
  async bulkInsert(dataSource: DataSource, databaseName: string, tableName: string, data: any[]): Promise<void> {
445
617
  if (data.length === 0) return;
446
- // MongoDB使用Repository模式
447
- const repository = dataSource.getMongoRepository(tableName);
448
- await repository.insertMany(data);
618
+ const db = this.nativeDb(dataSource, databaseName);
619
+ await db.collection(tableName).insertMany(data);
449
620
  }
450
621
 
451
622
  /**
452
623
  * 插入单条数据
453
624
  */
454
625
  async insertData(dataSource: DataSource, databaseName: string, tableName: string, data: any): Promise<void> {
455
- // MongoDB使用Repository模式
456
- const repository = dataSource.getMongoRepository(tableName);
457
- await repository.insertOne(data);
626
+ const db = this.nativeDb(dataSource, databaseName);
627
+ await db.collection(tableName).insertOne(data);
458
628
  }
459
629
 
460
630
  /**
461
631
  * 删除表(集合)
462
632
  */
463
633
  async dropTable(dataSource: DataSource, databaseName: string, tableName: string): Promise<void> {
464
- // MongoDB使用Repository模式
465
- const repository = dataSource.getMongoRepository(tableName);
466
- await repository.deleteMany({});
634
+ const db = this.nativeDb(dataSource, databaseName);
635
+ await db.collection(tableName).drop();
467
636
  }
468
637
 
469
638
  /**
@@ -471,23 +640,69 @@ export class MongoDBService extends BaseDatabaseService {
471
640
  */
472
641
  async createTable(dataSource: DataSource, databaseName: string, table: any): Promise<void> {
473
642
  const { name } = table;
474
- // MongoDB会自动创建集合,无需显式创建
475
- await this.insertData(dataSource, databaseName, name, {});
476
- await this.dropTable(dataSource, databaseName, name);
643
+ const db = this.nativeDb(dataSource, databaseName);
644
+ await db.createCollection(name);
477
645
  }
478
- }
479
646
 
480
- /**
481
- * ObjectId 类型定义
482
- */
483
- class ObjectId {
484
- constructor(id?: string) {
485
- if (id) {
486
- this.id = id;
487
- }
647
+ // ---------------------------------------------------------------- 文档级操作(document 模式)
648
+
649
+ /** 按 _id 更新文档:$set 语义,_id 本身不可修改 */
650
+ async updateDocument(
651
+ dataSource: DataSource, databaseName: string, tableName: string, docId: any, document: any
652
+ ): Promise<void> {
653
+ const col = this.nativeDb(dataSource, databaseName).collection(tableName);
654
+ const { _id, ...rest } = document || {};
655
+ await col.updateOne({ _id: this.toObjectId(docId) }, { $set: rest });
656
+ }
657
+
658
+ /** 按 _id 删除文档 */
659
+ async deleteDocument(
660
+ dataSource: DataSource, databaseName: string, tableName: string, docId: any
661
+ ): Promise<void> {
662
+ const col = this.nativeDb(dataSource, databaseName).collection(tableName);
663
+ await col.deleteOne({ _id: this.toObjectId(docId) });
664
+ }
665
+
666
+ /** 插入单个文档,返回 insertedId */
667
+ async insertDocument(
668
+ dataSource: DataSource, databaseName: string, tableName: string, document: any
669
+ ): Promise<{ insertedId: any }> {
670
+ const col = this.nativeDb(dataSource, databaseName).collection(tableName);
671
+ const res = await col.insertOne(document);
672
+ return { insertedId: res.insertedId };
488
673
  }
489
- id?: string;
490
- toString() {
491
- return this.id || '';
674
+
675
+ /** 清空集合(保留集合与索引),返回删除条数 */
676
+ async truncateCollection(
677
+ dataSource: DataSource, databaseName: string, tableName: string
678
+ ): Promise<number> {
679
+ const col = this.nativeDb(dataSource, databaseName).collection(tableName);
680
+ const res = await col.deleteMany({});
681
+ return res.deletedCount ?? 0;
682
+ }
683
+
684
+ /** 创建索引:keys 形如 { name: 1 } 或 { "a.b": -1 } */
685
+ async createIndex(
686
+ dataSource: DataSource, databaseName: string, tableName: string,
687
+ keys: Record<string, any>, options?: any
688
+ ): Promise<string> {
689
+ const col = this.nativeDb(dataSource, databaseName).collection(tableName);
690
+ return col.createIndex(keys, options || {});
691
+ }
692
+
693
+ /** 删除索引 */
694
+ async dropIndex(
695
+ dataSource: DataSource, databaseName: string, tableName: string, indexName: string
696
+ ): Promise<void> {
697
+ const col = this.nativeDb(dataSource, databaseName).collection(tableName);
698
+ await col.dropIndex(indexName);
699
+ }
700
+
701
+ /** 聚合管道(聚合构建器 / 高级查询) */
702
+ async aggregate(
703
+ dataSource: DataSource, databaseName: string, tableName: string, pipeline: any[]
704
+ ): Promise<any[]> {
705
+ const col = this.nativeDb(dataSource, databaseName).collection(tableName);
706
+ return col.aggregate(Array.isArray(pipeline) ? pipeline : []).toArray();
492
707
  }
493
- }
708
+ }
@@ -46,6 +46,14 @@ export declare class SQLServerService extends BaseDatabaseService {
46
46
  * SQL Server的标识符引用方式
47
47
  */
48
48
  quoteIdentifier(identifier: string): string;
49
+ /**
50
+ * SQL Server 不支持 LIMIT/OFFSET,使用 OFFSET ... ROWS FETCH NEXT ... ROWS ONLY
51
+ */
52
+ protected buildPaginationClause(pageSize: number, offset: number): string;
53
+ /**
54
+ * SQL Server 的 OFFSET/FETCH 语法强制要求 ORDER BY,无排序时补一个空排序
55
+ */
56
+ protected buildOrderByClause(orderBy?: string): string;
49
57
  /**
50
58
  * 获取SQL Server视图列表
51
59
  */
@@ -1 +1 @@
1
- {"version":3,"file":"mssql.service.d.ts","sourceRoot":"","sources":["../../../../server/service/database/mssql.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,OAAO,EACL,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EACjB,MAAM,6BAA6B,CAAC;AAIrC;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,mBAAmB;IAEvD,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAW7D;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAyBjF;;OAEG;IACG,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAkClG;;OAEG;IACG,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAgCjG;;OAEG;IACG,cAAc,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IA2B1G;;OAEG;IACG,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAShF;;OAEG;YACW,cAAc;IAa5B;;;;;;OAMG;IACG,2BAA2B,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUlH;;OAEG;IACI,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAIlD;;OAEG;IACG,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAiBxE;;OAEG;IACG,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUpG;;OAEG;IACG,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAsB7E;;OAEG;IACG,sBAAsB,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAU9G;;OAEG;IACG,cAAc,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAoDhG;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/E;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiEjF;;OAEG;IACG,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAY,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAiB9F;;OAEG;IACG,cAAc,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IA0BlG;;OAEG;IACG,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBnH;;OAEG;IACG,oBAAoB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAwF3H;;OAEG;IACG,oBAAoB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAkF3H;;OAEG;IACG,qBAAqB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAyD5H;;OAEG;IACG,sBAAsB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAa7H;;OAEG;IACG,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB7G;;OAEG;IACG,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAc3G;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/F;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CAc3F"}
1
+ {"version":3,"file":"mssql.service.d.ts","sourceRoot":"","sources":["../../../../server/service/database/mssql.service.ts"],"names":[],"mappings":"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"}