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.
- package/README.md +2 -1
- package/dist/public/explorer.css +403 -380
- package/dist/public/explorer.js +1294 -737
- package/dist/public/index.js +242 -16
- package/dist/public/layout.js +1 -1
- package/dist/scripts/verify-db-support.cjs +239 -0
- package/dist/server/core/dialect.d.ts +9 -0
- package/dist/server/core/dialect.d.ts.map +1 -1
- package/dist/server/core/dialect.js +24 -9
- package/dist/server/core/dialect.js.map +1 -1
- package/dist/server/core/dialect.ts +29 -9
- package/dist/server/core/schema/__tests__/ddl-emitter.test.ts +7 -3
- package/dist/server/core/schema/ddl-emitter.d.ts.map +1 -1
- package/dist/server/core/schema/ddl-emitter.js +32 -12
- package/dist/server/core/schema/ddl-emitter.js.map +1 -1
- package/dist/server/core/schema/ddl-emitter.ts +34 -11
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +83 -6
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.ts +87 -6
- package/dist/server/model/ddl-spec.d.ts +9 -0
- package/dist/server/model/ddl-spec.d.ts.map +1 -1
- package/dist/server/model/ddl-spec.ts +9 -0
- package/dist/server/service/connection.service.d.ts +8 -0
- package/dist/server/service/connection.service.d.ts.map +1 -1
- package/dist/server/service/connection.service.js +75 -17
- package/dist/server/service/connection.service.js.map +1 -1
- package/dist/server/service/connection.service.ts +74 -17
- package/dist/server/service/database/base.service.d.ts +45 -0
- package/dist/server/service/database/base.service.d.ts.map +1 -1
- package/dist/server/service/database/base.service.js +120 -7
- package/dist/server/service/database/base.service.js.map +1 -1
- package/dist/server/service/database/base.service.ts +143 -8
- package/dist/server/service/database/cockroachdb.service.d.ts +9 -0
- package/dist/server/service/database/cockroachdb.service.d.ts.map +1 -1
- package/dist/server/service/database/cockroachdb.service.js +67 -56
- package/dist/server/service/database/cockroachdb.service.js.map +1 -1
- package/dist/server/service/database/cockroachdb.service.ts +66 -57
- package/dist/server/service/database/database.service.d.ts +3 -221
- package/dist/server/service/database/database.service.d.ts.map +1 -1
- package/dist/server/service/database/database.service.js +66 -24
- package/dist/server/service/database/database.service.js.map +1 -1
- package/dist/server/service/database/database.service.ts +69 -23
- package/dist/server/service/database/mongodb.service.d.ts +58 -3
- package/dist/server/service/database/mongodb.service.d.ts.map +1 -1
- package/dist/server/service/database/mongodb.service.js +251 -54
- package/dist/server/service/database/mongodb.service.js.map +1 -1
- package/dist/server/service/database/mongodb.service.ts +275 -60
- package/dist/server/service/database/mssql.service.d.ts +8 -0
- package/dist/server/service/database/mssql.service.d.ts.map +1 -1
- package/dist/server/service/database/mssql.service.js +41 -36
- package/dist/server/service/database/mssql.service.js.map +1 -1
- package/dist/server/service/database/mssql.service.ts +45 -40
- package/dist/server/service/database/oracle.service.d.ts +24 -0
- package/dist/server/service/database/oracle.service.d.ts.map +1 -1
- package/dist/server/service/database/oracle.service.js +146 -106
- package/dist/server/service/database/oracle.service.js.map +1 -1
- package/dist/server/service/database/oracle.service.ts +145 -105
- package/dist/server/service/database/sqlite.service.js +1 -1
- package/dist/server/service/database/sqlite.service.js.map +1 -1
- package/dist/server/service/database/sqlite.service.ts +1 -1
- package/package.json +8 -8
- 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
|
|
29
|
-
return
|
|
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
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
61
|
-
|
|
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(
|
|
96
|
+
const fields = Object.keys(sample);
|
|
66
97
|
return fields.map(field => ({
|
|
67
98
|
name: field,
|
|
68
|
-
type: this.inferType(
|
|
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
|
|
116
|
-
|
|
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
|
|
141
|
-
|
|
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
|
-
|
|
182
|
-
|
|
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
|
|
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
|
|
238
|
-
return
|
|
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
|
|
267
|
-
backupData.collections[collection.name] =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
447
|
-
|
|
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
|
-
|
|
456
|
-
|
|
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
|
-
|
|
465
|
-
|
|
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
|
-
|
|
475
|
-
await
|
|
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
|
-
|
|
482
|
-
*/
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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":"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
|
|
1
|
+
{"version":3,"file":"mssql.service.d.ts","sourceRoot":"","sources":["../../../../server/service/database/mssql.service.ts"],"names":[],"mappings":"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"}
|