@aetherframework/database 1.1.0 → 1.1.2

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 (47) hide show
  1. package/examples/mysql-test-pressure.js +1530 -0
  2. package/examples/test-direct.js +116 -0
  3. package/examples/transaction_example.js +127 -0
  4. package/package.json +3 -1
  5. package/src/DatabaseManager.js +565 -0
  6. package/src/core/ConnectionManager.js +351 -0
  7. package/src/core/DatabaseFactory.js +188 -0
  8. package/src/core/MongoQueryBuilder.js +576 -0
  9. package/src/core/PluginManager.js +968 -0
  10. package/src/core/QueryBuilder.js +4394 -0
  11. package/src/core/TransactionManager.js +40 -0
  12. package/src/drivers/clickhouse-driver.js +272 -0
  13. package/src/drivers/index.js +273 -0
  14. package/src/drivers/mongodb-driver.js +87 -0
  15. package/src/drivers/mssql-driver.js +117 -0
  16. package/src/drivers/mysql-driver.js +169 -0
  17. package/src/drivers/oracle-driver.js +101 -0
  18. package/src/drivers/postgres-driver.js +234 -0
  19. package/src/drivers/redis-driver.js +52 -0
  20. package/src/drivers/sqlite-driver.js +67 -0
  21. package/src/middleware/connection-pool.js +455 -0
  22. package/src/middleware/performance-monitor.js +652 -0
  23. package/src/middleware/query-cache.js +500 -0
  24. package/src/middleware/query-logger.js +262 -0
  25. package/src/plugins/AuditPlugin.js +447 -0
  26. package/src/plugins/BasePlugin.js +418 -0
  27. package/src/plugins/BatchOperationPlugin.js +165 -0
  28. package/src/plugins/CachePlugin.js +407 -0
  29. package/src/plugins/CtePlugin.js +523 -0
  30. package/src/plugins/DistributedPlugin.js +543 -0
  31. package/src/plugins/EncryptionPlugin.js +211 -0
  32. package/src/plugins/FullTextSearchPlugin.js +164 -0
  33. package/src/plugins/GeospatialPlugin.js +219 -0
  34. package/src/plugins/GraphQLPlugin.js +162 -0
  35. package/src/plugins/HookPlugin.js +211 -0
  36. package/src/plugins/JsonPlugin.js +366 -0
  37. package/src/plugins/OptimisticLockPlugin.js +374 -0
  38. package/src/plugins/PerformancePlugin.js +175 -0
  39. package/src/plugins/ResiliencePlugin.js +114 -0
  40. package/src/plugins/ShardingPlugin.js +227 -0
  41. package/src/plugins/SoftDeletePlugin.js +258 -0
  42. package/src/plugins/SyncPlugin.js +373 -0
  43. package/src/plugins/VersioningPlugin.js +314 -0
  44. package/src/plugins/WindowFunctionPlugin.js +343 -0
  45. package/src/utils/config-loader.js +632 -0
  46. package/src/utils/error-handler.js +724 -0
  47. package/src/utils/migration-runner.js +1066 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @license MIT
3
+ * Copyright (c) 2026-present AetherFramework Contributors.
4
+ * SPDX-License-Identifier: MIT
5
+ * @module @aetherframework/database/core/TransactionManager
6
+ */
7
+
8
+ class TransactionManager {
9
+ constructor(driver) {
10
+ this.driver = driver;
11
+ }
12
+
13
+ async transaction(callback) {
14
+ const connection = await this.driver.pool.getConnection();
15
+ try {
16
+ await this.driver.beginTransaction(connection);
17
+
18
+ const trx = {
19
+ query: (sql, params) => this.driver.query(connection, sql, params),
20
+ rawQuery: (sql) => this.driver.rawQuery(connection, sql),
21
+ execute: (sql, params) => this.driver.execute(connection, sql, params),
22
+ };
23
+
24
+ const result = await callback(trx);
25
+
26
+ await this.driver.commitTransaction(connection);
27
+
28
+ return result;
29
+
30
+ } catch (error) {
31
+
32
+ await this.driver.rollbackTransaction(connection).catch(() => {});
33
+ throw error;
34
+ } finally {
35
+ connection.release();
36
+ }
37
+ }
38
+ }
39
+
40
+ export default TransactionManager;
@@ -0,0 +1,272 @@
1
+ /**
2
+ * @license MIT
3
+ * Copyright (c) 2026-present AetherFramework Contributors.
4
+ * SPDX-License-Identifier: MIT
5
+ * @module @aetherframework/database/drivers/clickhouse-driver
6
+ */
7
+ import { createClient } from '@clickhouse/client';
8
+
9
+ /**
10
+ * ClickHouse Driver
11
+ * Implements ClickHouse database driver with environment variable control
12
+ */
13
+ class ClickHouseDriver {
14
+ constructor(config) {
15
+ this.config = config;
16
+ this.client = null;
17
+ this.isConnected = false;
18
+ }
19
+
20
+ /**
21
+ * Initialize the ClickHouse client
22
+ * @returns {Promise<Object>} ClickHouse connection
23
+ */
24
+ async connect() {
25
+ // Check if ClickHouse is enabled via environment variable
26
+ if (process.env.ENABLE_CLICKHOUSE !== 'true') {
27
+ throw new Error('ClickHouse driver is disabled. Set ENABLE_CLICKHOUSE=true to enable.');
28
+ }
29
+
30
+ if (this.isConnected) return this.client;
31
+
32
+ try {
33
+ this.client = createClient({
34
+ host: this.config.host || 'http://localhost:8123',
35
+ username: this.config.user || 'default',
36
+ password: this.config.password || '',
37
+ database: this.config.database || 'default',
38
+ // ClickHouse specific options
39
+ clickhouse_settings: {
40
+ async_insert: 1, // Enable async insert for better performance
41
+ wait_for_async_insert: 0,
42
+ },
43
+ // Connection pool settings
44
+ max_open_connections: this.config.pool?.max || 10,
45
+ request_timeout: this.config.pool?.acquireTimeout || 10000,
46
+ compression: {
47
+ response: true,
48
+ request: true
49
+ }
50
+ });
51
+
52
+ // Test connection
53
+ await this.client.ping();
54
+ this.isConnected = true;
55
+ return this.client;
56
+ } catch (error) {
57
+ console.error('❌ ClickHouse connection failed:', error.message);
58
+ throw error;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Create connection pool (ClickHouse doesn't have traditional connection pool)
64
+ * @returns {Promise<Object>} ClickHouse client
65
+ */
66
+ async createPool() {
67
+ // ClickHouse client handles connection pooling internally
68
+ return await this.connect();
69
+ }
70
+
71
+ /**
72
+ * Execute SELECT query
73
+ * @param {Object} connection - ClickHouse client
74
+ * @param {string} sql - SQL query
75
+ * @param {Array} params - Query parameters
76
+ * @returns {Promise<Object>} Query result
77
+ */
78
+ async query(connection, sql, params = []) {
79
+ await this.ensureConnected();
80
+
81
+ try {
82
+ // ClickHouse uses parameterized queries differently
83
+ // We need to handle parameters manually
84
+ let finalSql = sql;
85
+ if (params && params.length > 0) {
86
+ // Replace ? placeholders with ClickHouse format
87
+ params.forEach((param, index) => {
88
+ const placeholder = `{param${index}:${this.getClickHouseType(param)}}`;
89
+ finalSql = finalSql.replace('?', placeholder);
90
+ });
91
+ }
92
+
93
+ const resultSet = await connection.query({
94
+ query: finalSql,
95
+ format: 'JSONEachRow', // Return data as JSON array
96
+ clickhouse_settings: {
97
+ allow_experimental_object_type: 1
98
+ }
99
+ });
100
+
101
+ const rows = await resultSet.json();
102
+ return {
103
+ rows,
104
+ rowCount: rows.length,
105
+ metaData: resultSet.meta || []
106
+ };
107
+ } catch (error) {
108
+ console.error('❌ ClickHouse query error:', error.message);
109
+ throw error;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Execute INSERT/UPDATE/DELETE operation
115
+ * Note: ClickHouse has limited UPDATE/DELETE support
116
+ * @param {Object} connection - ClickHouse client
117
+ * @param {string} sql - SQL statement
118
+ * @param {Array} params - Query parameters
119
+ * @returns {Promise<Object>} Execution result
120
+ */
121
+ async execute(connection, sql, params = []) {
122
+ await this.ensureConnected();
123
+
124
+ try {
125
+ let finalSql = sql;
126
+ if (params && params.length > 0) {
127
+ // Replace ? placeholders with ClickHouse format
128
+ params.forEach((param, index) => {
129
+ const placeholder = `{param${index}:${this.getClickHouseType(param)}}`;
130
+ finalSql = finalSql.replace('?', placeholder);
131
+ });
132
+ }
133
+
134
+ // For INSERT operations, ClickHouse has optimized methods
135
+ if (sql.toUpperCase().startsWith('INSERT')) {
136
+ // Extract table name from INSERT statement
137
+ const tableMatch = sql.match(/INSERT\s+INTO\s+(\w+)/i);
138
+ if (tableMatch && tableMatch) {
139
+ const tableName = tableMatch;
140
+ // If params is an array of objects, use insert method
141
+ if (params.length > 0 && typeof params === 'object') {
142
+ await connection.insert({
143
+ table: tableName,
144
+ values: params,
145
+ format: 'JSONEachRow'
146
+ });
147
+ return { affectedRows: params.length };
148
+ }
149
+ }
150
+ }
151
+
152
+ // For other operations, use command
153
+ await connection.command({ query: finalSql });
154
+ return { affectedRows: 1 };
155
+ } catch (error) {
156
+ console.error('❌ ClickHouse execute error:', error.message);
157
+ throw error;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Get ClickHouse type for parameter
163
+ * @param {*} value - Parameter value
164
+ * @returns {string} ClickHouse type string
165
+ */
166
+ getClickHouseType(value) {
167
+ if (value === null || value === undefined) return 'Nullable(Nothing)';
168
+ if (typeof value === 'number') {
169
+ return Number.isInteger(value) ? 'Int64' : 'Float64';
170
+ }
171
+ if (typeof value === 'boolean') return 'UInt8';
172
+ if (typeof value === 'string') return 'String';
173
+ if (value instanceof Date) return 'DateTime';
174
+ if (Array.isArray(value)) return 'Array(String)';
175
+ if (typeof value === 'object') return 'JSON';
176
+ return 'String';
177
+ }
178
+
179
+ /**
180
+ * Begin transaction (ClickHouse has limited transaction support)
181
+ * @param {Object} connection - ClickHouse client
182
+ * @returns {Promise<Object>} Transaction object
183
+ */
184
+ async beginTransaction(connection) {
185
+ console.warn('⚠️ ClickHouse has limited transaction support. Transactions may not work as expected.');
186
+ // ClickHouse doesn't support traditional transactions for all operations
187
+ return { sessionId: Date.now() };
188
+ }
189
+
190
+ /**
191
+ * Commit transaction
192
+ * @param {Object} transaction - Transaction object
193
+ */
194
+ async commitTransaction(transaction) {
195
+ // ClickHouse doesn't support traditional commit
196
+ console.warn('⚠️ ClickHouse transaction committed (no-op)');
197
+ }
198
+
199
+ /**
200
+ * Rollback transaction
201
+ * @param {Object} transaction - Transaction object
202
+ */
203
+ async rollbackTransaction(transaction) {
204
+ // ClickHouse doesn't support traditional rollback
205
+ console.warn('⚠️ ClickHouse transaction rolled back (no-op)');
206
+ }
207
+
208
+ /**
209
+ * Close connection
210
+ * @param {Object} connection - ClickHouse client
211
+ */
212
+ async close(connection) {
213
+ if (this.client) {
214
+ await this.client.close();
215
+ this.isConnected = false;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Close pool (ClickHouse doesn't have traditional connection pool)
221
+ * @param {Object} pool - Not used for ClickHouse
222
+ */
223
+ async closePool(pool) {
224
+ await this.close();
225
+ }
226
+
227
+ /**
228
+ * Get pool statistics
229
+ * @returns {Object} Pool statistics
230
+ */
231
+ getPoolStats() {
232
+ return {
233
+ isConnected: this.isConnected,
234
+ // ClickHouse doesn't expose connection pool stats
235
+ note: 'ClickHouse uses HTTP connection pooling internally'
236
+ };
237
+ }
238
+
239
+ /**
240
+ * Health check
241
+ * @param {Object} connection - ClickHouse client
242
+ * @returns {Promise<boolean>} Health status
243
+ */
244
+ async healthCheck(connection) {
245
+ try {
246
+ await connection.ping();
247
+ return true;
248
+ } catch (error) {
249
+ return false;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Ensure connection is established
255
+ * @private
256
+ */
257
+ async ensureConnected() {
258
+ if (!this.isConnected) {
259
+ await this.connect();
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Get dialect name
265
+ * @returns {string} Dialect name
266
+ */
267
+ getDialect() {
268
+ return 'clickhouse';
269
+ }
270
+ }
271
+
272
+ export default ClickHouseDriver;
@@ -0,0 +1,273 @@
1
+ /**
2
+ * @license MIT
3
+ * Copyright (c) 2026-present AetherFramework Contributors.
4
+ * SPDX-License-Identifier: MIT
5
+ * @module @aetherframework/database/drivers/index
6
+ */
7
+ // Driver registry mapping database types to driver classes
8
+ const drivers = {};
9
+
10
+ // Driver file path mapping
11
+ const driverPaths = {
12
+ mysql: './mysql-driver.js',
13
+ mariadb: './mysql-driver.js',
14
+ postgresql: './postgres-driver.js',
15
+ postgres: './postgres-driver.js',
16
+ pg: './postgres-driver.js',
17
+ sqlite: './sqlite-driver.js',
18
+ sqlite3: './sqlite-driver.js',
19
+ mongodb: './mongodb-driver.js',
20
+ mongo: './mongodb-driver.js',
21
+ mssql: './mssql-driver.js',
22
+ sqlserver: './mssql-driver.js',
23
+ oracle: './oracle-driver.js',
24
+ // Add CockroachDB support (reuse PostgreSQL driver)
25
+ cockroachdb: './postgres-driver.js',
26
+ cockroach: './postgres-driver.js',
27
+ // Add ClickHouse support (requires separate driver)
28
+ clickhouse: './clickhouse-driver.js'
29
+ };
30
+
31
+ /**
32
+ * Get driver class by database type name using dynamic imports
33
+ * @param {string} name - Database type name (mysql, postgresql, sqlite, etc.)
34
+ * @returns {Promise<Class|null>} Driver class or null if not found
35
+ */
36
+ export async function getDriver(name) {
37
+ const normalizedName = name.toLowerCase();
38
+
39
+ // Return cached driver if already loaded
40
+ if (drivers[normalizedName]) {
41
+ return drivers[normalizedName];
42
+ }
43
+
44
+ // Check if it's MySQL (your current database)
45
+ if (normalizedName === 'mysql' || normalizedName === 'mariadb') {
46
+ try {
47
+ // Only dynamically import MySQL driver
48
+ const { default: MySQLDriver } = await import('./mysql-driver.js');
49
+ drivers[normalizedName] = MySQLDriver;
50
+ return MySQLDriver;
51
+ } catch (error) {
52
+ console.error(`❌ Failed to load MySQL driver:`, error.message);
53
+ return null;
54
+ }
55
+ }
56
+
57
+ // Check if it's MongoDB
58
+ if (normalizedName === 'mongodb' || normalizedName === 'mongo') {
59
+ try {
60
+ // Dynamically import MongoDB driver
61
+ const { default: MongoDBDriver } = await import('./mongodb-driver.js');
62
+ drivers[normalizedName] = MongoDBDriver;
63
+ return MongoDBDriver;
64
+ } catch (error) {
65
+ console.error(`❌ Failed to load MongoDB driver:`, error.message);
66
+ return null;
67
+ }
68
+ }
69
+
70
+ // Check if it's CockroachDB (reuse PostgreSQL driver)
71
+ if (normalizedName === 'cockroachdb' || normalizedName === 'cockroach') {
72
+ // Check if CockroachDB support is enabled via environment variable
73
+ if (process.env.ENABLE_COCKROACHDB !== 'true') {
74
+ console.warn(`⚠️ CockroachDB driver is disabled. Set ENABLE_COCKROACHDB=true to enable.`);
75
+ return null;
76
+ }
77
+
78
+ try {
79
+ // CockroachDB is PostgreSQL compatible, reuse PostgreSQL driver
80
+ const { default: PostgreSQLDriver } = await import('./postgres-driver.js');
81
+ drivers[normalizedName] = PostgreSQLDriver;
82
+
83
+ return PostgreSQLDriver;
84
+ } catch (error) {
85
+ console.error(`❌ Failed to load CockroachDB driver:`, error.message);
86
+ return null;
87
+ }
88
+ }
89
+
90
+ // Check if it's ClickHouse
91
+ if (normalizedName === 'clickhouse') {
92
+ // Check if ClickHouse support is enabled via environment variable
93
+ if (process.env.ENABLE_CLICKHOUSE !== 'true') {
94
+ console.warn(`⚠️ ClickHouse driver is disabled. Set ENABLE_CLICKHOUSE=true to enable.`);
95
+ return null;
96
+ }
97
+
98
+ try {
99
+ // Dynamically import ClickHouse driver
100
+ const { default: ClickHouseDriver } = await import('./clickhouse-driver.js');
101
+ drivers[normalizedName] = ClickHouseDriver;
102
+
103
+ return ClickHouseDriver;
104
+ } catch (error) {
105
+ console.error(`❌ Failed to load ClickHouse driver:`, error.message);
106
+ return null;
107
+ }
108
+ }
109
+
110
+ // For other drivers, return null and prompt for installation
111
+ console.warn(`⚠️ Driver "${name}" not loaded. To use this driver, install required package:`);
112
+
113
+ const packageMap = {
114
+ postgresql: 'pg',
115
+ postgres: 'pg',
116
+ pg: 'pg',
117
+ sqlite: 'sqlite3',
118
+ sqlite3: 'sqlite3',
119
+ mongodb: 'mongodb',
120
+ mongo: 'mongodb',
121
+ redis: 'redis',
122
+ mssql: 'mssql',
123
+ sqlserver: 'mssql',
124
+ oracle: 'oracledb',
125
+ cockroachdb: 'pg', // CockroachDB uses PostgreSQL driver
126
+ cockroach: 'pg', // CockroachDB uses PostgreSQL driver
127
+ clickhouse: '@clickhouse/client' // ClickHouse client package
128
+ };
129
+
130
+ if (packageMap[normalizedName]) {
131
+ console.warn(` npm install ${packageMap[normalizedName]}`);
132
+ }
133
+
134
+ return null;
135
+ }
136
+
137
+ // Check if driver exists
138
+ export function hasDriver(name) {
139
+ const normalizedName = name.toLowerCase();
140
+ return !!drivers[normalizedName];
141
+ }
142
+
143
+ // Get all available driver names
144
+ export function getAvailableDrivers() {
145
+ return Object.keys(driverPaths);
146
+ }
147
+
148
+ // Register custom driver
149
+ export function registerDriver(name, driverClass) {
150
+ const normalizedName = name.toLowerCase();
151
+ if (drivers[normalizedName]) {
152
+ console.warn(`⚠️ Driver "${name}" is already registered, overriding existing driver`);
153
+ }
154
+ drivers[normalizedName] = driverClass;
155
+ }
156
+
157
+ // Remove registered driver
158
+ export function unregisterDriver(name) {
159
+ const normalizedName = name.toLowerCase();
160
+ if (drivers[normalizedName]) {
161
+ delete drivers[normalizedName];
162
+ } else {
163
+ console.warn(`⚠️ Driver "${name}" not found, nothing to unregister`);
164
+ }
165
+ }
166
+
167
+ // Preload all drivers
168
+ export async function preloadDrivers() {
169
+ const driverNames = getAvailableDrivers();
170
+ const promises = driverNames.map(async (name) => {
171
+ try {
172
+ await getDriver(name);
173
+ } catch (error) {
174
+ console.error(`❌ Failed to preload driver "${name}":`, error.message);
175
+ }
176
+ });
177
+ await Promise.all(promises);
178
+ }
179
+
180
+ // Clear driver cache
181
+ export function clearDriverCache() {
182
+ drivers.clear();
183
+ }
184
+
185
+ // On-demand export of individual drivers
186
+ export async function getMySQLDriver() {
187
+ return await getDriver('mysql');
188
+ }
189
+
190
+ export async function getPostgreSQLDriver() {
191
+ return await getDriver('postgresql');
192
+ }
193
+
194
+ export async function getSQLiteDriver() {
195
+ return await getDriver('sqlite');
196
+ }
197
+
198
+ export async function getMongoDBDriver() {
199
+ return await getDriver('mongodb');
200
+ }
201
+
202
+ export async function getMSSQLDriver() {
203
+ return await getDriver('mssql');
204
+ }
205
+
206
+ export async function getOracleDriver() {
207
+ return await getDriver('oracle');
208
+ }
209
+
210
+ // New: Export CockroachDB driver (environment variable controlled)
211
+ export async function getCockroachDBDriver() {
212
+ // Check if CockroachDB support is enabled
213
+ if (process.env.ENABLE_COCKROACHDB !== 'true') {
214
+ console.warn(`⚠️ CockroachDB driver is disabled. Set ENABLE_COCKROACHDB=true to enable.`);
215
+ return null;
216
+ }
217
+ return await getDriver('cockroachdb');
218
+ }
219
+
220
+ // New: Export ClickHouse driver (environment variable controlled)
221
+ export async function getClickHouseDriver() {
222
+ // Check if ClickHouse support is enabled
223
+ if (process.env.ENABLE_CLICKHOUSE !== 'true') {
224
+ console.warn(`⚠️ ClickHouse driver is disabled. Set ENABLE_CLICKHOUSE=true to enable.`);
225
+ return null;
226
+ }
227
+ return await getDriver('clickhouse');
228
+ }
229
+
230
+ // New: Export Redis driver
231
+ export async function getRedisDriver() {
232
+ return await getDriver('redis');
233
+ }
234
+
235
+ // New: Export all drivers as an object
236
+ export async function getAllDrivers() {
237
+ const driverNames = getAvailableDrivers();
238
+ const driversObj = {};
239
+
240
+ for (const name of driverNames) {
241
+ try {
242
+ const driver = await getDriver(name);
243
+ if (driver) {
244
+ driversObj[name] = driver;
245
+ }
246
+ } catch (error) {
247
+ console.warn(`⚠️ Failed to load driver "${name}":`, error.message);
248
+ }
249
+ }
250
+
251
+ return driversObj;
252
+ }
253
+
254
+ // New: Check if specific driver is available
255
+ export async function isDriverAvailable(name) {
256
+ try {
257
+ const driver = await getDriver(name);
258
+ return driver !== null;
259
+ } catch (error) {
260
+ return false;
261
+ }
262
+ }
263
+
264
+ // New: Get driver with fallback
265
+ export async function getDriverWithFallback(name, fallbackName = 'mysql') {
266
+ const driver = await getDriver(name);
267
+ if (driver) {
268
+ return driver;
269
+ }
270
+
271
+ console.warn(`⚠️ Driver "${name}" not available, falling back to "${fallbackName}"`);
272
+ return await getDriver(fallbackName);
273
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @license MIT
3
+ * Copyright (c) 2026-present AetherFramework Contributors.
4
+ * SPDX-License-Identifier: MIT
5
+ * @module @aetherframework/database/drivers/mongodb-driver
6
+ */
7
+ import { MongoClient } from 'mongodb';
8
+
9
+ class MongoDBDriver {
10
+ constructor(config) {
11
+ this.config = config;
12
+ this.client = null;
13
+ }
14
+
15
+ async connect(config) {
16
+ const url = `mongodb://${config.user}:${config.password}@${config.host}:${config.port}/${config.database}?authSource=${config.authSource || 'admin'}`;
17
+ this.client = new MongoClient(url, {
18
+ useNewUrlParser: true,
19
+ useUnifiedTopology: true,
20
+ maxPoolSize: config.pool?.max || 10,
21
+ minPoolSize: config.pool?.min || 2,
22
+ maxIdleTimeMS: config.pool?.idleTimeout || 30000,
23
+ serverSelectionTimeoutMS: config.pool?.acquireTimeout || 10000
24
+ });
25
+ await this.client.connect();
26
+ return this.client.db(config.database);
27
+ }
28
+
29
+ async query(connection, collectionName, query = {}, options = {}) {
30
+ const collection = connection.collection(collectionName);
31
+ const cursor = collection.find(query, options);
32
+ const rows = await cursor.toArray();
33
+ return { rows, rowCount: rows.length };
34
+ }
35
+
36
+ async execute(connection, collectionName, operation, data, options = {}) {
37
+ const collection = connection.collection(collectionName);
38
+ let result;
39
+
40
+ switch (operation) {
41
+ case 'insert':
42
+ result = await collection.insertOne(data, options);
43
+ return { insertedId: result.insertedId, insertedCount: result.insertedCount };
44
+ case 'update':
45
+ result = await collection.updateOne(data.filter, data.update, options);
46
+ return { matchedCount: result.matchedCount, modifiedCount: result.modifiedCount };
47
+ case 'delete':
48
+ result = await collection.deleteOne(data.filter, options);
49
+ return { deletedCount: result.deletedCount };
50
+ default:
51
+ throw new Error(`Unsupported MongoDB operation: ${operation}`);
52
+ }
53
+ }
54
+
55
+ async beginTransaction(connection) {
56
+ const session = this.client.startSession();
57
+ session.startTransaction();
58
+ return session;
59
+ }
60
+
61
+ async commitTransaction(session) {
62
+ await session.commitTransaction();
63
+ session.endSession();
64
+ }
65
+
66
+ async rollbackTransaction(session) {
67
+ await session.abortTransaction();
68
+ session.endSession();
69
+ }
70
+
71
+ async close() {
72
+ if (this.client) {
73
+ await this.client.close();
74
+ }
75
+ }
76
+
77
+ async healthCheck(connection) {
78
+ try {
79
+ await connection.command({ ping: 1 });
80
+ return true;
81
+ } catch (error) {
82
+ return false;
83
+ }
84
+ }
85
+ }
86
+
87
+ export default MongoDBDriver;