@aetherframework/database 1.0.9 → 1.1.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/package.json +1 -2
- package/src/DatabaseManager.js +0 -565
- package/src/core/ConnectionManager.js +0 -351
- package/src/core/DatabaseFactory.js +0 -188
- package/src/core/MongoQueryBuilder.js +0 -576
- package/src/core/PluginManager.js +0 -968
- package/src/core/QueryBuilder.js +0 -4398
- package/src/core/TransactionManager.js +0 -40
- package/src/drivers/clickhouse-driver.js +0 -272
- package/src/drivers/index.js +0 -273
- package/src/drivers/mongodb-driver.js +0 -87
- package/src/drivers/mssql-driver.js +0 -117
- package/src/drivers/mysql-driver.js +0 -169
- package/src/drivers/oracle-driver.js +0 -101
- package/src/drivers/postgres-driver.js +0 -234
- package/src/drivers/redis-driver.js +0 -52
- package/src/drivers/sqlite-driver.js +0 -67
- package/src/middleware/connection-pool.js +0 -455
- package/src/middleware/performance-monitor.js +0 -652
- package/src/middleware/query-cache.js +0 -500
- package/src/middleware/query-logger.js +0 -262
- package/src/plugins/AuditPlugin.js +0 -447
- package/src/plugins/BasePlugin.js +0 -418
- package/src/plugins/BatchOperationPlugin.js +0 -165
- package/src/plugins/CachePlugin.js +0 -407
- package/src/plugins/CtePlugin.js +0 -523
- package/src/plugins/DistributedPlugin.js +0 -543
- package/src/plugins/EncryptionPlugin.js +0 -211
- package/src/plugins/FullTextSearchPlugin.js +0 -164
- package/src/plugins/GeospatialPlugin.js +0 -219
- package/src/plugins/GraphQLPlugin.js +0 -162
- package/src/plugins/HookPlugin.js +0 -211
- package/src/plugins/JsonPlugin.js +0 -366
- package/src/plugins/OptimisticLockPlugin.js +0 -374
- package/src/plugins/PerformancePlugin.js +0 -175
- package/src/plugins/ResiliencePlugin.js +0 -114
- package/src/plugins/ShardingPlugin.js +0 -227
- package/src/plugins/SoftDeletePlugin.js +0 -258
- package/src/plugins/SyncPlugin.js +0 -373
- package/src/plugins/VersioningPlugin.js +0 -314
- package/src/plugins/WindowFunctionPlugin.js +0 -343
- package/src/utils/config-loader.js +0 -632
- package/src/utils/error-handler.js +0 -724
- package/src/utils/migration-runner.js +0 -1066
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license MIT
|
|
3
|
-
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
-
* SPDX-License-Identifier: MIT
|
|
5
|
-
* @module @aetherframework/database/plugin/JsonPlugin
|
|
6
|
-
*/
|
|
7
|
-
import { BasePlugin } from './BasePlugin.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Json Plugin - Provides database-agnostic JSON field operations.
|
|
11
|
-
* Translates JSON methods to the appropriate SQL functions for the current dialect.
|
|
12
|
-
*/
|
|
13
|
-
export class JsonPlugin extends BasePlugin {
|
|
14
|
-
constructor(queryBuilder) {
|
|
15
|
-
super(queryBuilder);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
_registerMethods() {
|
|
19
|
-
// Register JSON methods to QueryBuilder
|
|
20
|
-
this.queryBuilder.whereJsonContains = this.whereJsonContains.bind(this);
|
|
21
|
-
this.queryBuilder.whereJsonLength = this.whereJsonLength.bind(this);
|
|
22
|
-
this.queryBuilder.whereJsonHasKey = this.whereJsonHasKey.bind(this);
|
|
23
|
-
this.queryBuilder.jsonSet = this.jsonSet.bind(this);
|
|
24
|
-
this.queryBuilder.selectJson = this.selectJson.bind(this);
|
|
25
|
-
this.queryBuilder.jsonExtract = this.jsonExtract.bind(this); // 修复:添加 jsonExtract 方法
|
|
26
|
-
this.queryBuilder.jsonInsert = this.jsonInsert.bind(this);
|
|
27
|
-
this.queryBuilder.jsonReplace = this.jsonReplace.bind(this);
|
|
28
|
-
this.queryBuilder.jsonRemove = this.jsonRemove.bind(this);
|
|
29
|
-
this.queryBuilder.jsonMerge = this.jsonMerge.bind(this);
|
|
30
|
-
this.queryBuilder.whereJsonContainsPath = this.whereJsonContainsPath.bind(this);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* WHERE clause: Check if a JSON column contains a specific value.
|
|
35
|
-
* @param {string} column - The JSON column name.
|
|
36
|
-
* @param {*} value - The value to check for.
|
|
37
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
38
|
-
*/
|
|
39
|
-
whereJsonContains(column, value) {
|
|
40
|
-
const wrappedColumn = this.queryBuilder.wrapColumn(column);
|
|
41
|
-
const jsonValue = JSON.stringify(value);
|
|
42
|
-
|
|
43
|
-
switch (this.queryBuilder.dialect) {
|
|
44
|
-
case 'mysql':
|
|
45
|
-
case 'mariadb':
|
|
46
|
-
this.queryBuilder.whereRaw(`JSON_CONTAINS(${wrappedColumn}, ?)`, [jsonValue]);
|
|
47
|
-
break;
|
|
48
|
-
case 'postgresql':
|
|
49
|
-
case 'postgres':
|
|
50
|
-
case 'pg':
|
|
51
|
-
case 'cockroachdb':
|
|
52
|
-
this.queryBuilder.whereRaw(`${wrappedColumn} @> ?::jsonb`, [jsonValue]);
|
|
53
|
-
break;
|
|
54
|
-
default:
|
|
55
|
-
throw new Error(`JSON_CONTAINS operation not supported for dialect: ${this.queryBuilder.dialect}`);
|
|
56
|
-
}
|
|
57
|
-
return this.queryBuilder;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* WHERE clause: Compare the length of a JSON array.
|
|
62
|
-
* @param {string} column - The JSON column name.
|
|
63
|
-
* @param {string} operator - Comparison operator (>, <, =, >=, <=).
|
|
64
|
-
* @param {number} value - The length value to compare against.
|
|
65
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
66
|
-
*/
|
|
67
|
-
whereJsonLength(column, operator, value) {
|
|
68
|
-
const wrappedColumn = this.queryBuilder.wrapColumn(column);
|
|
69
|
-
|
|
70
|
-
switch (this.queryBuilder.dialect) {
|
|
71
|
-
case 'mysql':
|
|
72
|
-
case 'mariadb':
|
|
73
|
-
this.queryBuilder.whereRaw(`JSON_LENGTH(${wrappedColumn}) ${operator} ?`, [value]);
|
|
74
|
-
break;
|
|
75
|
-
case 'postgresql':
|
|
76
|
-
case 'postgres':
|
|
77
|
-
case 'pg':
|
|
78
|
-
case 'cockroachdb':
|
|
79
|
-
this.queryBuilder.whereRaw(`jsonb_array_length(${wrappedColumn}) ${operator} ?`, [value]);
|
|
80
|
-
break;
|
|
81
|
-
default:
|
|
82
|
-
throw new Error(`JSON_LENGTH operation not supported for dialect: ${this.queryBuilder.dialect}`);
|
|
83
|
-
}
|
|
84
|
-
return this.queryBuilder;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* WHERE clause: Check if a JSON column has a specific key.
|
|
89
|
-
* @param {string} column - The JSON column name.
|
|
90
|
-
* @param {string} key - The key to check for.
|
|
91
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
92
|
-
*/
|
|
93
|
-
whereJsonHasKey(column, key) {
|
|
94
|
-
const wrappedColumn = this.queryBuilder.wrapColumn(column);
|
|
95
|
-
|
|
96
|
-
switch (this.queryBuilder.dialect) {
|
|
97
|
-
case 'mysql':
|
|
98
|
-
case 'mariadb':
|
|
99
|
-
this.queryBuilder.whereRaw(`JSON_CONTAINS_PATH(${wrappedColumn}, 'one', ?)`, [`$.${key}`]);
|
|
100
|
-
break;
|
|
101
|
-
case 'postgresql':
|
|
102
|
-
case 'postgres':
|
|
103
|
-
case 'pg':
|
|
104
|
-
case 'cockroachdb':
|
|
105
|
-
this.queryBuilder.whereRaw(`${wrappedColumn} ?? ?`, [key]);
|
|
106
|
-
break;
|
|
107
|
-
default:
|
|
108
|
-
throw new Error(`JSON_HAS_KEY operation not supported for dialect: ${this.queryBuilder.dialect}`);
|
|
109
|
-
}
|
|
110
|
-
return this.queryBuilder;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* SET clause: Update a specific path within a JSON column.
|
|
115
|
-
* @param {string} column - The JSON column name.
|
|
116
|
-
* @param {string} path - The JSON path (e.g., 'user.name').
|
|
117
|
-
* @param {*} value - The value to set at the path.
|
|
118
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
119
|
-
*/
|
|
120
|
-
jsonSet(column, path, value) {
|
|
121
|
-
if (!this.queryBuilder.query.jsonUpdates) {
|
|
122
|
-
this.queryBuilder.query.jsonUpdates = [];
|
|
123
|
-
}
|
|
124
|
-
this.queryBuilder.query.jsonUpdates.push({ column, path, value, operation: 'set' });
|
|
125
|
-
return this.queryBuilder;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* SELECT clause: Extract a value from a JSON column.
|
|
130
|
-
* @param {string} column - The JSON column name.
|
|
131
|
-
* @param {string} path - The JSON path to extract (e.g., 'user.name').
|
|
132
|
-
* @param {string} alias - Optional alias for the selected value.
|
|
133
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
134
|
-
*/
|
|
135
|
-
selectJson(column, path, alias = null) {
|
|
136
|
-
const wrappedColumn = this.queryBuilder.wrapColumn(column);
|
|
137
|
-
const jsonPath = `$.${path}`;
|
|
138
|
-
let selectExpr;
|
|
139
|
-
|
|
140
|
-
switch (this.queryBuilder.dialect) {
|
|
141
|
-
case 'mysql':
|
|
142
|
-
case 'mariadb':
|
|
143
|
-
selectExpr = `JSON_UNQUOTE(JSON_EXTRACT(${wrappedColumn}, ?))`;
|
|
144
|
-
this.queryBuilder.bindings.push(jsonPath);
|
|
145
|
-
break;
|
|
146
|
-
case 'postgresql':
|
|
147
|
-
case 'postgres':
|
|
148
|
-
case 'pg':
|
|
149
|
-
case 'cockroachdb':
|
|
150
|
-
selectExpr = `${wrappedColumn}->>?`;
|
|
151
|
-
this.queryBuilder.bindings.push(path);
|
|
152
|
-
break;
|
|
153
|
-
case 'mssql':
|
|
154
|
-
case 'sqlserver':
|
|
155
|
-
selectExpr = `JSON_VALUE(${wrappedColumn}, ?)`;
|
|
156
|
-
this.queryBuilder.bindings.push(`$${path}`);
|
|
157
|
-
break;
|
|
158
|
-
default:
|
|
159
|
-
throw new Error(`JSON_EXTRACT operation not supported for dialect: ${this.queryBuilder.dialect}`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const finalAlias = alias || `${column}_${path.replace(/\./g, '_')}`;
|
|
163
|
-
const finalSelectExpr = `${selectExpr} AS ${finalAlias}`;
|
|
164
|
-
|
|
165
|
-
if (this.queryBuilder.query.columns.length === 0 || this.queryBuilder.query.columns === '*') {
|
|
166
|
-
this.queryBuilder.query.columns = [finalSelectExpr];
|
|
167
|
-
} else {
|
|
168
|
-
this.queryBuilder.query.columns.push(finalSelectExpr);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return this.queryBuilder;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Extract a value from a JSON column (alias for selectJson)
|
|
176
|
-
* @param {string} column - The JSON column name
|
|
177
|
-
* @param {string} path - The JSON path to extract
|
|
178
|
-
* @param {string} [alias] - Optional alias for the extracted value
|
|
179
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining
|
|
180
|
-
*/
|
|
181
|
-
jsonExtract(column, path, alias = null) {
|
|
182
|
-
// 直接调用 selectJson 方法,因为功能相同
|
|
183
|
-
return this.selectJson(column, path, alias);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* WHERE clause: Check if a JSON column contains a specific path.
|
|
188
|
-
* @param {string} column - The JSON column name.
|
|
189
|
-
* @param {string} path - The JSON path to check (e.g., 'user.address.city').
|
|
190
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
191
|
-
*/
|
|
192
|
-
whereJsonContainsPath(column, path) {
|
|
193
|
-
const wrappedColumn = this.queryBuilder.wrapColumn(column);
|
|
194
|
-
const jsonPath = `$.${path}`;
|
|
195
|
-
|
|
196
|
-
switch (this.queryBuilder.dialect) {
|
|
197
|
-
case 'mysql':
|
|
198
|
-
case 'mariadb':
|
|
199
|
-
this.queryBuilder.whereRaw(`JSON_CONTAINS_PATH(${wrappedColumn}, 'one', ?)`, [jsonPath]);
|
|
200
|
-
break;
|
|
201
|
-
case 'postgresql':
|
|
202
|
-
case 'postgres':
|
|
203
|
-
case 'pg':
|
|
204
|
-
case 'cockroachdb':
|
|
205
|
-
this.queryBuilder.whereRaw(`${wrappedColumn} ?? ?`, [path]);
|
|
206
|
-
break;
|
|
207
|
-
default:
|
|
208
|
-
throw new Error(`JSON_CONTAINS_PATH operation not supported for dialect: ${this.queryBuilder.dialect}`);
|
|
209
|
-
}
|
|
210
|
-
return this.queryBuilder;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// --- The following methods would require modifying the SQL building process ---
|
|
214
|
-
// They are registered but their implementation would need to integrate with
|
|
215
|
-
// the QueryBuilder's `toSQL()` or `buildUpdateSQL()` methods.
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* JSON_INSERT operation (insert if path does not exist).
|
|
219
|
-
* @param {string} column - The JSON column name.
|
|
220
|
-
* @param {string} path - The JSON path.
|
|
221
|
-
* @param {*} value - The value to insert.
|
|
222
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
223
|
-
*/
|
|
224
|
-
jsonInsert(column, path, value) {
|
|
225
|
-
if (!this.queryBuilder.query.jsonUpdates) {
|
|
226
|
-
this.queryBuilder.query.jsonUpdates = [];
|
|
227
|
-
}
|
|
228
|
-
this.queryBuilder.query.jsonUpdates.push({ column, path, value, operation: 'json_insert' });
|
|
229
|
-
return this.queryBuilder;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* JSON_REPLACE operation (replace if path exists).
|
|
234
|
-
* @param {string} column - The JSON column name.
|
|
235
|
-
* @param {string} path - The JSON path.
|
|
236
|
-
* @param {*} value - The value to replace with.
|
|
237
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
238
|
-
*/
|
|
239
|
-
jsonReplace(column, path, value) {
|
|
240
|
-
if (!this.queryBuilder.query.jsonUpdates) {
|
|
241
|
-
this.queryBuilder.query.jsonUpdates = [];
|
|
242
|
-
}
|
|
243
|
-
this.queryBuilder.query.jsonUpdates.push({ column, path, value, operation: 'json_replace' });
|
|
244
|
-
return this.queryBuilder;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* JSON_REMOVE operation.
|
|
249
|
-
* @param {string} column - The JSON column name.
|
|
250
|
-
* @param {string} path - The JSON path to remove.
|
|
251
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
252
|
-
*/
|
|
253
|
-
jsonRemove(column, path) {
|
|
254
|
-
if (!this.queryBuilder.query.jsonUpdates) {
|
|
255
|
-
this.queryBuilder.query.jsonUpdates = [];
|
|
256
|
-
}
|
|
257
|
-
this.queryBuilder.query.jsonUpdates.push({ column, path, operation: 'json_remove' });
|
|
258
|
-
return this.queryBuilder;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* JSON_MERGE operation (merge two JSON documents).
|
|
263
|
-
* @param {string} column - The JSON column name.
|
|
264
|
-
* @param {Object|Array} value - The JSON value to merge.
|
|
265
|
-
* @returns {QueryBuilder} The QueryBuilder instance for chaining.
|
|
266
|
-
*/
|
|
267
|
-
jsonMerge(column, value) {
|
|
268
|
-
if (!this.queryBuilder.query.jsonUpdates) {
|
|
269
|
-
this.queryBuilder.query.jsonUpdates = [];
|
|
270
|
-
}
|
|
271
|
-
this.queryBuilder.query.jsonUpdates.push({ column, value, operation: 'json_merge' });
|
|
272
|
-
return this.queryBuilder;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Helper method to be called by QueryBuilder when building UPDATE SQL for JSON operations.
|
|
277
|
-
* This should be integrated into the main QueryBuilder's `buildUpdateSQL` method.
|
|
278
|
-
* @private
|
|
279
|
-
*/
|
|
280
|
-
_buildJsonUpdateClause() {
|
|
281
|
-
if (!this.queryBuilder.query.jsonUpdates || this.queryBuilder.query.jsonUpdates.length === 0) {
|
|
282
|
-
return '';
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const updates = [];
|
|
286
|
-
this.queryBuilder.query.jsonUpdates.forEach(update => {
|
|
287
|
-
const wrappedColumn = this.queryBuilder.wrapColumn(update.column);
|
|
288
|
-
const jsonPath = `$.${update.path}`;
|
|
289
|
-
const placeholder = '?';
|
|
290
|
-
|
|
291
|
-
switch (update.operation) {
|
|
292
|
-
case 'set':
|
|
293
|
-
// This is the most common operation, syntax varies by DB
|
|
294
|
-
switch (this.queryBuilder.dialect) {
|
|
295
|
-
case 'mysql':
|
|
296
|
-
case 'mariadb':
|
|
297
|
-
updates.push(`${wrappedColumn} = JSON_SET(${wrappedColumn}, ?, ?)`);
|
|
298
|
-
this.queryBuilder.bindings.push(jsonPath, JSON.stringify(update.value));
|
|
299
|
-
break;
|
|
300
|
-
case 'postgresql':
|
|
301
|
-
case 'postgres':
|
|
302
|
-
case 'pg':
|
|
303
|
-
case 'cockroachdb':
|
|
304
|
-
updates.push(`${wrappedColumn} = jsonb_set(${wrappedColumn}, ?, ?)`);
|
|
305
|
-
this.queryBuilder.bindings.push(`{${update.path.split('.').join(',')}}`, JSON.stringify(update.value));
|
|
306
|
-
break;
|
|
307
|
-
}
|
|
308
|
-
break;
|
|
309
|
-
case 'json_insert':
|
|
310
|
-
case 'json_replace':
|
|
311
|
-
case 'json_remove':
|
|
312
|
-
case 'json_merge':
|
|
313
|
-
// Implementations for these would follow similar patterns
|
|
314
|
-
// For brevity, they are omitted here but would be added in a full implementation.
|
|
315
|
-
console.warn(`JSON operation '${update.operation}' not fully implemented in plugin example.`);
|
|
316
|
-
break;
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
return updates.join(', ');
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Returns the plugin's capabilities for the current dialect.
|
|
325
|
-
* @returns {Object} An object describing supported JSON features.
|
|
326
|
-
*/
|
|
327
|
-
getCapabilities() {
|
|
328
|
-
const capabilities = {
|
|
329
|
-
jsonContains: false,
|
|
330
|
-
jsonLength: false,
|
|
331
|
-
jsonHasKey: false,
|
|
332
|
-
jsonSet: false,
|
|
333
|
-
jsonExtract: false
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
switch (this.queryBuilder.dialect) {
|
|
337
|
-
case 'mysql':
|
|
338
|
-
case 'mariadb':
|
|
339
|
-
capabilities.jsonContains = true;
|
|
340
|
-
capabilities.jsonLength = true;
|
|
341
|
-
capabilities.jsonHasKey = true;
|
|
342
|
-
capabilities.jsonSet = true;
|
|
343
|
-
capabilities.jsonExtract = true;
|
|
344
|
-
break;
|
|
345
|
-
case 'postgresql':
|
|
346
|
-
case 'postgres':
|
|
347
|
-
case 'pg':
|
|
348
|
-
case 'cockroachdb':
|
|
349
|
-
capabilities.jsonContains = true;
|
|
350
|
-
capabilities.jsonLength = true;
|
|
351
|
-
capabilities.jsonHasKey = true;
|
|
352
|
-
capabilities.jsonSet = true;
|
|
353
|
-
capabilities.jsonExtract = true;
|
|
354
|
-
break;
|
|
355
|
-
case 'mssql':
|
|
356
|
-
case 'sqlserver':
|
|
357
|
-
capabilities.jsonContains = false; // Limited support
|
|
358
|
-
capabilities.jsonLength = false;
|
|
359
|
-
capabilities.jsonHasKey = false;
|
|
360
|
-
capabilities.jsonSet = true; // Via JSON_MODIFY
|
|
361
|
-
capabilities.jsonExtract = true; // Via JSON_VALUE
|
|
362
|
-
break;
|
|
363
|
-
}
|
|
364
|
-
return capabilities;
|
|
365
|
-
}
|
|
366
|
-
}
|