monsqlize 1.0.0 → 1.0.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 (61) hide show
  1. package/CHANGELOG.md +92 -2419
  2. package/README.md +630 -1070
  3. package/index.d.ts +252 -15
  4. package/lib/cache.js +8 -8
  5. package/lib/common/validation.js +64 -1
  6. package/lib/connect.js +3 -3
  7. package/lib/errors.js +10 -0
  8. package/lib/index.js +118 -9
  9. package/lib/infrastructure/ssh-tunnel-ssh2.js +211 -0
  10. package/lib/infrastructure/ssh-tunnel.js +40 -0
  11. package/lib/infrastructure/uri-parser.js +35 -0
  12. package/lib/lock/Lock.js +66 -0
  13. package/lib/lock/errors.js +27 -0
  14. package/lib/lock/index.js +12 -0
  15. package/lib/logger.js +1 -1
  16. package/lib/model/examples/test.js +4 -4
  17. package/lib/mongodb/common/accessor-helpers.js +17 -3
  18. package/lib/mongodb/connect.js +68 -13
  19. package/lib/mongodb/index.js +140 -7
  20. package/lib/mongodb/management/collection-ops.js +4 -4
  21. package/lib/mongodb/management/index-ops.js +18 -18
  22. package/lib/mongodb/management/validation-ops.js +3 -3
  23. package/lib/mongodb/queries/aggregate.js +14 -5
  24. package/lib/mongodb/queries/chain.js +52 -45
  25. package/lib/mongodb/queries/count.js +16 -6
  26. package/lib/mongodb/queries/distinct.js +15 -6
  27. package/lib/mongodb/queries/find-and-count.js +22 -13
  28. package/lib/mongodb/queries/find-by-ids.js +5 -5
  29. package/lib/mongodb/queries/find-one-by-id.js +1 -1
  30. package/lib/mongodb/queries/find-one.js +12 -3
  31. package/lib/mongodb/queries/find-page.js +12 -0
  32. package/lib/mongodb/queries/find.js +15 -6
  33. package/lib/mongodb/queries/index.js +1 -0
  34. package/lib/mongodb/queries/watch.js +537 -0
  35. package/lib/mongodb/writes/delete-many.js +20 -11
  36. package/lib/mongodb/writes/delete-one.js +18 -9
  37. package/lib/mongodb/writes/find-one-and-delete.js +19 -10
  38. package/lib/mongodb/writes/find-one-and-replace.js +36 -20
  39. package/lib/mongodb/writes/find-one-and-update.js +36 -20
  40. package/lib/mongodb/writes/increment-one.js +16 -7
  41. package/lib/mongodb/writes/index.js +13 -13
  42. package/lib/mongodb/writes/insert-batch.js +46 -37
  43. package/lib/mongodb/writes/insert-many.js +22 -13
  44. package/lib/mongodb/writes/insert-one.js +18 -9
  45. package/lib/mongodb/writes/replace-one.js +33 -17
  46. package/lib/mongodb/writes/result-handler.js +14 -14
  47. package/lib/mongodb/writes/update-many.js +34 -18
  48. package/lib/mongodb/writes/update-one.js +33 -17
  49. package/lib/mongodb/writes/upsert-one.js +25 -9
  50. package/lib/operators.js +1 -1
  51. package/lib/redis-cache-adapter.js +3 -3
  52. package/lib/slow-query-log/base-storage.js +69 -0
  53. package/lib/slow-query-log/batch-queue.js +96 -0
  54. package/lib/slow-query-log/config-manager.js +195 -0
  55. package/lib/slow-query-log/index.js +237 -0
  56. package/lib/slow-query-log/mongodb-storage.js +323 -0
  57. package/lib/slow-query-log/query-hash.js +38 -0
  58. package/lib/transaction/DistributedCacheLockManager.js +240 -5
  59. package/lib/transaction/Transaction.js +1 -1
  60. package/lib/utils/objectid-converter.js +566 -0
  61. package/package.json +11 -5
@@ -3,9 +3,10 @@
3
3
  * 更新单个匹配的文档
4
4
  */
5
5
 
6
- const { createError, ErrorCodes } = require("../../errors");
7
- const CacheFactory = require("../../cache");
8
- const { isInTransaction, getTransactionFromSession } = require("../common/transaction-aware");
6
+ const { createError, ErrorCodes } = require('../../errors');
7
+ const CacheFactory = require('../../cache');
8
+ const { isInTransaction, getTransactionFromSession } = require('../common/transaction-aware');
9
+ const { convertObjectIdStrings, convertUpdateDocument } = require('../../utils/objectid-converter');
9
10
 
10
11
  /**
11
12
  * 创建 updateOne 操作
@@ -60,39 +61,54 @@ function createUpdateOneOps(context) {
60
61
  const startTime = Date.now();
61
62
 
62
63
  // 1. 参数验证
63
- if (!filter || typeof filter !== "object" || Array.isArray(filter)) {
64
+ if (!filter || typeof filter !== 'object' || Array.isArray(filter)) {
64
65
  throw createError(
65
66
  ErrorCodes.INVALID_ARGUMENT,
66
- "filter 必须是对象类型",
67
- [{ field: "filter", type: "object.required", message: "filter 是必需参数且必须是对象" }]
67
+ 'filter 必须是对象类型',
68
+ [{ field: 'filter', type: 'object.required', message: 'filter 是必需参数且必须是对象' }]
68
69
  );
69
70
  }
70
71
 
71
- if (!update || typeof update !== "object" || Array.isArray(update)) {
72
+ if (!update || typeof update !== 'object' || Array.isArray(update)) {
72
73
  throw createError(
73
74
  ErrorCodes.INVALID_ARGUMENT,
74
- "update 必须是对象类型",
75
- [{ field: "update", type: "object.required", message: "update 是必需参数且必须是对象" }]
75
+ 'update 必须是对象类型',
76
+ [{ field: 'update', type: 'object.required', message: 'update 是必需参数且必须是对象' }]
76
77
  );
77
78
  }
78
79
 
79
80
  // 验证 update 包含更新操作符(防止整体替换)
80
81
  const updateKeys = Object.keys(update);
81
- if (updateKeys.length > 0 && !updateKeys.some(key => key.startsWith("$"))) {
82
+ if (updateKeys.length > 0 && !updateKeys.some(key => key.startsWith('$'))) {
82
83
  throw createError(
83
84
  ErrorCodes.INVALID_ARGUMENT,
84
- "update 必须使用更新操作符(如 $set, $inc 等)",
85
- [{ field: "update", type: "object.invalid", message: "请使用 $set, $inc, $push 等更新操作符,或使用 replaceOne 进行整体替换" }]
85
+ 'update 必须使用更新操作符(如 $set, $inc 等)',
86
+ [{ field: 'update', type: 'object.invalid', message: '请使用 $set, $inc, $push 等更新操作符,或使用 replaceOne 进行整体替换' }]
86
87
  );
87
88
  }
88
89
 
90
+ // ✅ v1.3.0: 自动转换 ObjectId 字符串
91
+ const convertedFilter = convertObjectIdStrings(filter, 'filter', 0, new WeakSet(), {
92
+ logger: context.logger,
93
+ excludeFields: context.autoConvertConfig?.excludeFields,
94
+ customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
95
+ maxDepth: context.autoConvertConfig?.maxDepth
96
+ });
97
+
98
+ const convertedUpdate = convertUpdateDocument(update, {
99
+ logger: context.logger,
100
+ excludeFields: context.autoConvertConfig?.excludeFields,
101
+ customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
102
+ maxDepth: context.autoConvertConfig?.maxDepth
103
+ });
104
+
89
105
  // 2. 构建操作上下文
90
- const operation = "updateOne";
106
+ const operation = 'updateOne';
91
107
  const ns = `${databaseName}.${collectionName}`;
92
108
 
93
109
  try {
94
110
  // 3. 执行更新操作
95
- const result = await nativeCollection.updateOne(filter, update, options);
111
+ const result = await nativeCollection.updateOne(convertedFilter, convertedUpdate, options);
96
112
 
97
113
  // 4. 自动失效缓存
98
114
  if (cache && result.modifiedCount > 0) {
@@ -100,7 +116,7 @@ function createUpdateOneOps(context) {
100
116
  // 使用标准命名空间模式删除该集合的所有缓存
101
117
  const ns = {
102
118
  iid: instanceId,
103
- type: "mongodb",
119
+ type: 'mongodb',
104
120
  db: databaseName,
105
121
  collection: collectionName
106
122
  };
@@ -184,8 +200,8 @@ function createUpdateOneOps(context) {
184
200
  // MongoDB 重复键错误(可能在 upsert 时发生)
185
201
  throw createError(
186
202
  ErrorCodes.DUPLICATE_KEY,
187
- "更新失败:违反唯一性约束",
188
- [{ field: "_id", message: error.message }],
203
+ '更新失败:违反唯一性约束',
204
+ [{ field: '_id', message: error.message }],
189
205
  error
190
206
  );
191
207
  }
@@ -4,7 +4,8 @@
4
4
  */
5
5
 
6
6
  const { createError, ErrorCodes } = require('../../errors');
7
- const { isInTransaction, getTransactionFromSession } = require("../common/transaction-aware");
7
+ const { isInTransaction, getTransactionFromSession } = require('../common/transaction-aware');
8
+ const { convertObjectIdStrings } = require('../../utils/objectid-converter');
8
9
 
9
10
  /**
10
11
  * 创建 upsertOne 操作
@@ -68,11 +69,26 @@ function createUpsertOneOps(context) {
68
69
  );
69
70
  }
70
71
 
72
+ // ✅ v1.3.0: 自动转换 ObjectId 字符串
73
+ const convertedFilter = convertObjectIdStrings(filter, 'filter', 0, new WeakSet(), {
74
+ logger: context.logger,
75
+ excludeFields: context.autoConvertConfig?.excludeFields,
76
+ customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
77
+ maxDepth: context.autoConvertConfig?.maxDepth
78
+ });
79
+
80
+ const convertedUpdate = convertObjectIdStrings(update, 'document', 0, new WeakSet(), {
81
+ logger: context.logger,
82
+ excludeFields: context.autoConvertConfig?.excludeFields,
83
+ customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
84
+ maxDepth: context.autoConvertConfig?.maxDepth
85
+ });
86
+
71
87
  // 2. 检查 update 是否包含更新操作符
72
- const hasOperator = Object.keys(update).some(key => key.startsWith('$'));
88
+ const hasOperator = Object.keys(convertedUpdate).some(key => key.startsWith('$'));
73
89
 
74
90
  // 如果没有操作符,自动包装为 $set
75
- const updateDoc = hasOperator ? update : { $set: update };
91
+ const updateDoc = hasOperator ? convertedUpdate : { $set: convertedUpdate };
76
92
 
77
93
  // 3. 构建选项
78
94
  const maxTimeMS = options.maxTimeMS !== undefined ? options.maxTimeMS : defaults.maxTimeMS;
@@ -84,7 +100,7 @@ function createUpsertOneOps(context) {
84
100
  // 4. 执行 updateOne 操作
85
101
  let result;
86
102
  try {
87
- result = await collection.updateOne(filter, updateDoc, driverOpts);
103
+ result = await collection.updateOne(convertedFilter, updateDoc, driverOpts);
88
104
  } catch (error) {
89
105
  // 统一错误处理
90
106
  if (error.code === 11000) {
@@ -146,7 +162,7 @@ function createUpsertOneOps(context) {
146
162
  operation: 'upsertOne',
147
163
  durationMs: duration,
148
164
  iid: instanceId,
149
- type: type,
165
+ type,
150
166
  db: effectiveDbName,
151
167
  collection: collection.collectionName,
152
168
  matchedCount: result.matchedCount,
@@ -154,7 +170,7 @@ function createUpsertOneOps(context) {
154
170
  upsertedId: result.upsertedId,
155
171
  filter: mongoSlowLogShaper?.sanitize ? mongoSlowLogShaper.sanitize(filter) : filter,
156
172
  update: mongoSlowLogShaper?.sanitize ? mongoSlowLogShaper.sanitize(updateDoc) : updateDoc,
157
- comment: comment
173
+ comment
158
174
  };
159
175
  logger?.warn?.('🐌 Slow query: upsertOne', meta);
160
176
  emit?.('slow-query', meta);
@@ -166,11 +182,11 @@ function createUpsertOneOps(context) {
166
182
  // 7. 日志记录
167
183
  logger?.debug?.('[upsertOne] 操作完成', {
168
184
  ns: `${effectiveDbName}.${collection.collectionName}`,
169
- duration: duration,
185
+ duration,
170
186
  matchedCount: result.matchedCount,
171
187
  modifiedCount: result.modifiedCount,
172
188
  upsertedId: result.upsertedId === null ? undefined : result.upsertedId,
173
- upsertedCount: upsertedCount
189
+ upsertedCount
174
190
  });
175
191
 
176
192
  // 8. 返回结果
@@ -179,7 +195,7 @@ function createUpsertOneOps(context) {
179
195
  matchedCount: result.matchedCount,
180
196
  modifiedCount: result.modifiedCount,
181
197
  upsertedId: result.upsertedId === null ? undefined : result.upsertedId,
182
- upsertedCount: upsertedCount
198
+ upsertedCount
183
199
  };
184
200
  };
185
201
 
package/lib/operators.js CHANGED
@@ -327,4 +327,4 @@ module.exports = {
327
327
  '$setReadConcern': { mysql: '' },
328
328
  '$setReadPref': { mysql: '' }
329
329
  }
330
- }
330
+ };
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Redis 缓存适配器:将 Redis 封装为 CacheLike 接口
3
3
  * 支持直接传入 Redis URL 字符串或 ioredis 实例
4
- *
4
+ *
5
5
  * @example
6
6
  * const { createRedisCacheAdapter } = require('monsqlize/lib/redis-cache-adapter');
7
- *
7
+ *
8
8
  * // 方式 1:传入 URL 字符串(推荐)
9
9
  * const cache = createRedisCacheAdapter('redis://localhost:6379/0');
10
- *
10
+ *
11
11
  * // 方式 2:传入 ioredis 实例
12
12
  * const Redis = require('ioredis');
13
13
  * const redis = new Redis('redis://localhost:6379/0');
@@ -0,0 +1,69 @@
1
+ /**
2
+ * 慢查询日志存储接口
3
+ * 所有存储适配器必须实现此接口
4
+ *
5
+ * @version 1.3.0
6
+ * @since 2025-12-22
7
+ */
8
+
9
+ class ISlowQueryLogStorage {
10
+ /**
11
+ * 初始化存储(创建集合/表、索引等)
12
+ * @returns {Promise<void>}
13
+ */
14
+ async initialize() {
15
+ throw new Error('ISlowQueryLogStorage.initialize() must be implemented');
16
+ }
17
+
18
+ /**
19
+ * 保存单条慢查询日志
20
+ * @param {Object} log - 慢查询日志对象
21
+ * @param {string} log.db - 数据库名
22
+ * @param {string} log.collection - 集合名
23
+ * @param {string} log.operation - 操作类型
24
+ * @param {Object} log.queryShape - 查询模式(已脱敏)
25
+ * @param {number} log.executionTimeMs - 执行时间(毫秒)
26
+ * @param {Date} log.timestamp - 时间戳
27
+ * @param {Object} [log.metadata] - 扩展元数据
28
+ * @returns {Promise<void>}
29
+ */
30
+ async save(log) {
31
+ throw new Error('ISlowQueryLogStorage.save() must be implemented');
32
+ }
33
+
34
+ /**
35
+ * 批量保存慢查询日志
36
+ * @param {Object[]} logs - 慢查询日志数组
37
+ * @returns {Promise<void>}
38
+ */
39
+ async saveBatch(logs) {
40
+ throw new Error('ISlowQueryLogStorage.saveBatch() must be implemented');
41
+ }
42
+
43
+ /**
44
+ * 查询慢查询日志
45
+ * @param {Object} filter - 查询条件
46
+ * @param {string} [filter.db] - 数据库名
47
+ * @param {string} [filter.collection] - 集合名
48
+ * @param {string} [filter.operation] - 操作类型
49
+ * @param {Object} options - 查询选项
50
+ * @param {Object} [options.sort] - 排序规则
51
+ * @param {number} [options.limit] - 限制数量
52
+ * @param {number} [options.skip] - 跳过数量
53
+ * @returns {Promise<Object[]>}
54
+ */
55
+ async query(filter, options) {
56
+ throw new Error('ISlowQueryLogStorage.query() must be implemented');
57
+ }
58
+
59
+ /**
60
+ * 关闭连接
61
+ * @returns {Promise<void>}
62
+ */
63
+ async close() {
64
+ throw new Error('ISlowQueryLogStorage.close() must be implemented');
65
+ }
66
+ }
67
+
68
+ module.exports = { ISlowQueryLogStorage };
69
+
@@ -0,0 +1,96 @@
1
+ /**
2
+ * 批量队列管理器
3
+ * 用于批量写入慢查询日志,优化性能
4
+ *
5
+ * @version 1.3.0
6
+ * @since 2025-12-22
7
+ */
8
+
9
+ class BatchQueue {
10
+ /**
11
+ * 创建批量队列
12
+ * @param {Object} storage - 存储适配器实例
13
+ * @param {Object} options - 队列配置
14
+ * @param {number} [options.batchSize=10] - 批量大小
15
+ * @param {number} [options.flushInterval=5000] - 刷新间隔(毫秒)
16
+ * @param {number} [options.maxBufferSize=100] - 最大缓冲区大小
17
+ * @param {Object} [logger] - 日志记录器
18
+ */
19
+ constructor(storage, options = {}, logger) {
20
+ this.storage = storage;
21
+ this.buffer = [];
22
+ this.batchSize = options.batchSize || 10;
23
+ this.flushInterval = options.flushInterval || 5000;
24
+ this.maxBufferSize = options.maxBufferSize || 100;
25
+ this.timer = null;
26
+ this.flushing = false;
27
+ this.logger = logger || console;
28
+ }
29
+
30
+ /**
31
+ * 添加日志到队列
32
+ * @param {Object} log - 慢查询日志对象
33
+ * @returns {Promise<void>}
34
+ */
35
+ async add(log) {
36
+ this.buffer.push(log);
37
+
38
+ // 防止内存溢出:达到最大缓冲区,立即刷新
39
+ if (this.buffer.length >= this.maxBufferSize) {
40
+ await this.flush();
41
+ return;
42
+ }
43
+
44
+ // 达到批量大小,立即刷新
45
+ if (this.buffer.length >= this.batchSize) {
46
+ await this.flush();
47
+ } else if (!this.timer) {
48
+ // 启动定时器(防止数据积压)
49
+ this.timer = setTimeout(() => this.flush(), this.flushInterval);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * 刷新队列(批量写入)
55
+ * @returns {Promise<void>}
56
+ */
57
+ async flush() {
58
+ // 防止并发刷新
59
+ if (this.flushing || this.buffer.length === 0) {
60
+ return;
61
+ }
62
+
63
+ this.flushing = true;
64
+ const logs = this.buffer.splice(0); // 清空缓冲区
65
+ clearTimeout(this.timer);
66
+ this.timer = null;
67
+
68
+ try {
69
+ // 批量写入
70
+ await this.storage.saveBatch(logs);
71
+ if (this.logger.debug) {
72
+ this.logger.debug(`[SlowQueryLog] Batch flushed: ${logs.length} logs`);
73
+ }
74
+ } catch (err) {
75
+ // ⚠️ 失败不阻塞主流程,仅记录错误
76
+ if (this.logger.error) {
77
+ this.logger.error('[SlowQueryLog] Failed to save slow query logs batch:', err);
78
+ }
79
+ } finally {
80
+ this.flushing = false;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 关闭队列(确保数据不丢失)
86
+ * @returns {Promise<void>}
87
+ */
88
+ async close() {
89
+ clearTimeout(this.timer);
90
+ this.timer = null;
91
+ await this.flush(); // 最后刷新一次
92
+ }
93
+ }
94
+
95
+ module.exports = { BatchQueue };
96
+
@@ -0,0 +1,195 @@
1
+ /**
2
+ * 慢查询日志配置管理器
3
+ * 负责配置合并、验证和默认值处理
4
+ *
5
+ * @version 1.3.0
6
+ * @since 2025-12-22
7
+ */
8
+
9
+ // 默认配置
10
+ const DEFAULT_CONFIG = {
11
+ enabled: false,
12
+
13
+ storage: {
14
+ type: null, // null = 自动推断
15
+ useBusinessConnection: true,
16
+ uri: null,
17
+
18
+ mongodb: {
19
+ database: 'admin',
20
+ collection: 'slow_query_logs',
21
+ ttl: 7 * 24 * 3600, // 7天
22
+ ttlField: 'lastSeen'
23
+ }
24
+ },
25
+
26
+ deduplication: {
27
+ enabled: true,
28
+ strategy: 'aggregate',
29
+ keepRecentExecutions: 0
30
+ },
31
+
32
+ batch: {
33
+ enabled: true,
34
+ size: 10,
35
+ interval: 5000,
36
+ maxBufferSize: 100
37
+ },
38
+
39
+ filter: {
40
+ excludeDatabases: [],
41
+ excludeCollections: [],
42
+ excludeOperations: [],
43
+ minExecutionTimeMs: 0
44
+ },
45
+
46
+ advanced: {
47
+ autoCreateIndexes: true,
48
+ validateConnection: true,
49
+ errorHandling: 'log', // log | throw | silent
50
+ debug: false
51
+ }
52
+ };
53
+
54
+ class SlowQueryLogConfigManager {
55
+ /**
56
+ * 合并用户配置与默认配置
57
+ * @param {*} userConfig - 用户配置(可以是boolean或object)
58
+ * @param {string} businessType - 业务库类型
59
+ * @returns {Object} 合并后的完整配置
60
+ */
61
+ static mergeConfig(userConfig, businessType) {
62
+ // 场景1:未配置(默认禁用)
63
+ if (userConfig === undefined || userConfig === null) {
64
+ return { ...deepClone(DEFAULT_CONFIG), enabled: false };
65
+ }
66
+
67
+ // 场景2:boolean快捷配置
68
+ if (typeof userConfig === 'boolean') {
69
+ const config = deepClone(DEFAULT_CONFIG);
70
+ config.enabled = userConfig;
71
+
72
+ // 自动推断storage.type
73
+ if (userConfig && businessType) {
74
+ config.storage.type = businessType;
75
+ }
76
+
77
+ return config;
78
+ }
79
+
80
+ // 场景3:对象配置(深度合并)
81
+ if (typeof userConfig === 'object') {
82
+ const merged = deepMerge(deepClone(DEFAULT_CONFIG), userConfig);
83
+
84
+ // 智能推断:如果提供了storage配置,自动启用
85
+ if (userConfig.storage && merged.enabled === false) {
86
+ merged.enabled = true;
87
+ }
88
+
89
+ // 自动推断storage.type
90
+ if (merged.storage.type === null && businessType) {
91
+ if (merged.storage.useBusinessConnection) {
92
+ merged.storage.type = businessType;
93
+ } else {
94
+ merged.storage.type = 'mongodb'; // 独立连接默认MongoDB
95
+ }
96
+ }
97
+
98
+ return merged;
99
+ }
100
+
101
+ throw new Error('Invalid slowQueryLog config type. Expected boolean or object');
102
+ }
103
+
104
+ /**
105
+ * 验证配置合法性
106
+ * @param {Object} config - 配置对象
107
+ * @param {string} businessType - 业务库类型
108
+ * @throws {Error} 配置错误时抛出异常
109
+ */
110
+ static validate(config, businessType) {
111
+ const { storage } = config;
112
+
113
+ // 验证storage.type
114
+ const validTypes = ['mongodb', 'postgresql', 'mysql', 'file', 'custom'];
115
+ if (storage.type && !validTypes.includes(storage.type)) {
116
+ throw new Error(
117
+ `Invalid storage.type: ${storage.type}. ` +
118
+ `Valid types are: ${validTypes.join(', ')}`
119
+ );
120
+ }
121
+
122
+ // 验证复用连接的类型一致性
123
+ if (storage.useBusinessConnection === true) {
124
+ if (storage.type && storage.type !== businessType) {
125
+ throw new Error(
126
+ `Cannot use business connection when storage type (${storage.type}) ` +
127
+ `differs from business type (${businessType}). ` +
128
+ `Set useBusinessConnection=false and provide storage.uri`
129
+ );
130
+ }
131
+ } else {
132
+ // 验证独立连接的uri
133
+ if (!storage.uri) {
134
+ throw new Error(
135
+ 'storage.uri is required when useBusinessConnection=false'
136
+ );
137
+ }
138
+ }
139
+
140
+ // 验证deduplication.strategy
141
+ const validStrategies = ['aggregate', 'none'];
142
+ if (!validStrategies.includes(config.deduplication.strategy)) {
143
+ throw new Error(
144
+ `Invalid deduplication.strategy: ${config.deduplication.strategy}. ` +
145
+ `Valid strategies are: ${validStrategies.join(', ')}`
146
+ );
147
+ }
148
+
149
+ // 验证TTL
150
+ if (storage.mongodb.ttl < 0) {
151
+ throw new Error('storage.mongodb.ttl must be positive');
152
+ }
153
+
154
+ // 验证batch配置
155
+ if (config.batch.size < 1 || config.batch.size > 1000) {
156
+ throw new Error('batch.size must be between 1 and 1000');
157
+ }
158
+
159
+ if (config.batch.interval < 100) {
160
+ throw new Error('batch.interval must be >= 100ms');
161
+ }
162
+
163
+ return true;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * 深度克隆对象
169
+ * @param {Object} obj - 源对象
170
+ * @returns {Object} 克隆后的对象
171
+ */
172
+ function deepClone(obj) {
173
+ return JSON.parse(JSON.stringify(obj));
174
+ }
175
+
176
+ /**
177
+ * 深度合并对象
178
+ * @param {Object} target - 目标对象
179
+ * @param {Object} source - 源对象
180
+ * @returns {Object} 合并后的对象
181
+ */
182
+ function deepMerge(target, source) {
183
+ for (const key in source) {
184
+ if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
185
+ target[key] = target[key] || {};
186
+ deepMerge(target[key], source[key]);
187
+ } else {
188
+ target[key] = source[key];
189
+ }
190
+ }
191
+ return target;
192
+ }
193
+
194
+ module.exports = { SlowQueryLogConfigManager, DEFAULT_CONFIG };
195
+