monsqlize 1.3.0 → 2.0.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.
Files changed (172) hide show
  1. package/CHANGELOG.md +56 -60
  2. package/LICENSE +201 -21
  3. package/README.md +537 -1828
  4. package/changelogs/README.md +160 -0
  5. package/changelogs/v2.0.0.md +222 -0
  6. package/dist/cjs/index.cjs +10600 -0
  7. package/dist/cjs/mongodb/common/transaction-aware.cjs +10 -0
  8. package/dist/cjs/transaction/CacheLockManager.cjs +100 -0
  9. package/dist/cjs/transaction/Transaction.cjs +158 -0
  10. package/dist/cjs/transaction/TransactionManager.cjs +298 -0
  11. package/dist/esm/index.mjs +10650 -0
  12. package/dist/types/base.d.ts +81 -0
  13. package/dist/types/collection.d.ts +1031 -0
  14. package/dist/types/expression.d.ts +115 -0
  15. package/dist/types/index.d.ts +23 -0
  16. package/dist/types/lock.d.ts +74 -0
  17. package/dist/types/model.d.ts +526 -0
  18. package/dist/types/mongodb.d.ts +49 -0
  19. package/dist/types/monsqlize.d.ts +491 -0
  20. package/dist/types/pool.d.ts +84 -0
  21. package/dist/types/runtime.d.ts +362 -0
  22. package/dist/types/saga.d.ts +143 -0
  23. package/dist/types/slow-query-log.d.ts +126 -0
  24. package/dist/types/sync.d.ts +103 -0
  25. package/dist/types/transaction.d.ts +132 -0
  26. package/package.json +67 -69
  27. package/index.d.ts +0 -206
  28. package/index.mjs +0 -52
  29. package/lib/cache-invalidation.js +0 -279
  30. package/lib/cache.js +0 -530
  31. package/lib/common/cursor.js +0 -59
  32. package/lib/common/docs-urls.js +0 -73
  33. package/lib/common/index-options.js +0 -223
  34. package/lib/common/log.js +0 -61
  35. package/lib/common/namespace.js +0 -22
  36. package/lib/common/normalize.js +0 -34
  37. package/lib/common/page-result.js +0 -43
  38. package/lib/common/runner.js +0 -57
  39. package/lib/common/server-features.js +0 -232
  40. package/lib/common/shape-builders.js +0 -27
  41. package/lib/common/validation.js +0 -113
  42. package/lib/connect.js +0 -99
  43. package/lib/constants.js +0 -55
  44. package/lib/count-queue.js +0 -188
  45. package/lib/distributed-cache-invalidator.js +0 -260
  46. package/lib/errors.js +0 -168
  47. package/lib/expression/cache/ExpressionCache.js +0 -114
  48. package/lib/expression/compiler/ExpressionCompiler.js +0 -1090
  49. package/lib/expression/compiler/ExpressionCompilerExtensions.js +0 -531
  50. package/lib/expression/detector.js +0 -84
  51. package/lib/expression/factory.js +0 -29
  52. package/lib/expression/index.js +0 -19
  53. package/lib/function-cache.js +0 -533
  54. package/lib/index.js +0 -1251
  55. package/lib/infrastructure/ConnectionPoolManager.js +0 -464
  56. package/lib/infrastructure/HealthChecker.js +0 -281
  57. package/lib/infrastructure/PoolConfig.js +0 -199
  58. package/lib/infrastructure/PoolSelector.js +0 -225
  59. package/lib/infrastructure/PoolStats.js +0 -244
  60. package/lib/infrastructure/ssh-tunnel-ssh2.js +0 -212
  61. package/lib/infrastructure/ssh-tunnel.js +0 -41
  62. package/lib/infrastructure/uri-parser.js +0 -36
  63. package/lib/lock/Lock.js +0 -67
  64. package/lib/lock/errors.js +0 -28
  65. package/lib/lock/index.js +0 -13
  66. package/lib/logger.js +0 -225
  67. package/lib/model/examples/test.js +0 -311
  68. package/lib/model/features/defaults.js +0 -161
  69. package/lib/model/features/populate.js +0 -568
  70. package/lib/model/features/relations.js +0 -120
  71. package/lib/model/features/soft-delete.js +0 -349
  72. package/lib/model/features/version.js +0 -157
  73. package/lib/model/features/virtuals.js +0 -219
  74. package/lib/model/index.js +0 -1265
  75. package/lib/mongodb/common/accessor-helpers.js +0 -59
  76. package/lib/mongodb/common/agg-pipeline.js +0 -36
  77. package/lib/mongodb/common/aggregation-validator.js +0 -127
  78. package/lib/mongodb/common/iid.js +0 -28
  79. package/lib/mongodb/common/lexicographic-expr.js +0 -53
  80. package/lib/mongodb/common/shape.js +0 -32
  81. package/lib/mongodb/common/sort.js +0 -39
  82. package/lib/mongodb/common/transaction-aware.js +0 -25
  83. package/lib/mongodb/connect.js +0 -234
  84. package/lib/mongodb/index.js +0 -639
  85. package/lib/mongodb/management/admin-ops.js +0 -200
  86. package/lib/mongodb/management/bookmark-ops.js +0 -167
  87. package/lib/mongodb/management/cache-ops.js +0 -50
  88. package/lib/mongodb/management/collection-ops.js +0 -387
  89. package/lib/mongodb/management/database-ops.js +0 -202
  90. package/lib/mongodb/management/index-ops.js +0 -475
  91. package/lib/mongodb/management/index.js +0 -17
  92. package/lib/mongodb/management/namespace.js +0 -31
  93. package/lib/mongodb/management/validation-ops.js +0 -268
  94. package/lib/mongodb/queries/aggregate.js +0 -172
  95. package/lib/mongodb/queries/chain.js +0 -631
  96. package/lib/mongodb/queries/count.js +0 -99
  97. package/lib/mongodb/queries/distinct.js +0 -78
  98. package/lib/mongodb/queries/find-and-count.js +0 -193
  99. package/lib/mongodb/queries/find-by-ids.js +0 -236
  100. package/lib/mongodb/queries/find-one-by-id.js +0 -171
  101. package/lib/mongodb/queries/find-one.js +0 -71
  102. package/lib/mongodb/queries/find-page.js +0 -618
  103. package/lib/mongodb/queries/find.js +0 -171
  104. package/lib/mongodb/queries/index.js +0 -51
  105. package/lib/mongodb/queries/watch.js +0 -538
  106. package/lib/mongodb/writes/common/batch-retry.js +0 -65
  107. package/lib/mongodb/writes/delete-batch.js +0 -323
  108. package/lib/mongodb/writes/delete-many.js +0 -181
  109. package/lib/mongodb/writes/delete-one.js +0 -173
  110. package/lib/mongodb/writes/find-one-and-delete.js +0 -203
  111. package/lib/mongodb/writes/find-one-and-replace.js +0 -239
  112. package/lib/mongodb/writes/find-one-and-update.js +0 -240
  113. package/lib/mongodb/writes/increment-one.js +0 -259
  114. package/lib/mongodb/writes/index.js +0 -46
  115. package/lib/mongodb/writes/insert-batch.js +0 -508
  116. package/lib/mongodb/writes/insert-many.js +0 -223
  117. package/lib/mongodb/writes/insert-one.js +0 -169
  118. package/lib/mongodb/writes/replace-one.js +0 -226
  119. package/lib/mongodb/writes/result-handler.js +0 -237
  120. package/lib/mongodb/writes/update-batch.js +0 -416
  121. package/lib/mongodb/writes/update-many.js +0 -275
  122. package/lib/mongodb/writes/update-one.js +0 -273
  123. package/lib/mongodb/writes/upsert-one.js +0 -203
  124. package/lib/multi-level-cache.js +0 -244
  125. package/lib/operators.js +0 -330
  126. package/lib/redis-cache-adapter.js +0 -267
  127. package/lib/saga/SagaContext.js +0 -67
  128. package/lib/saga/SagaDefinition.js +0 -32
  129. package/lib/saga/SagaExecutor.js +0 -201
  130. package/lib/saga/SagaOrchestrator.js +0 -186
  131. package/lib/saga/index.js +0 -11
  132. package/lib/slow-query-log/base-storage.js +0 -70
  133. package/lib/slow-query-log/batch-queue.js +0 -97
  134. package/lib/slow-query-log/config-manager.js +0 -196
  135. package/lib/slow-query-log/index.js +0 -238
  136. package/lib/slow-query-log/mongodb-storage.js +0 -324
  137. package/lib/slow-query-log/query-hash.js +0 -39
  138. package/lib/sync/ChangeStreamSyncManager.js +0 -405
  139. package/lib/sync/ResumeTokenStore.js +0 -192
  140. package/lib/sync/SyncConfig.js +0 -127
  141. package/lib/sync/SyncTarget.js +0 -240
  142. package/lib/sync/index.js +0 -20
  143. package/lib/transaction/CacheLockManager.js +0 -162
  144. package/lib/transaction/DistributedCacheLockManager.js +0 -475
  145. package/lib/transaction/Transaction.js +0 -315
  146. package/lib/transaction/TransactionManager.js +0 -267
  147. package/lib/transaction/index.js +0 -11
  148. package/lib/utils/objectid-converter.js +0 -632
  149. package/types/README.md +0 -122
  150. package/types/base.ts +0 -94
  151. package/types/batch.ts +0 -187
  152. package/types/cache.ts +0 -71
  153. package/types/chain.ts +0 -254
  154. package/types/collection.ts +0 -357
  155. package/types/expression.ts +0 -109
  156. package/types/function-cache.d.ts +0 -135
  157. package/types/lock.ts +0 -95
  158. package/types/model/definition.ts +0 -152
  159. package/types/model/index.ts +0 -10
  160. package/types/model/instance.ts +0 -121
  161. package/types/model/relations.ts +0 -121
  162. package/types/model/virtuals.ts +0 -32
  163. package/types/monsqlize.ts +0 -245
  164. package/types/options.ts +0 -192
  165. package/types/pagination.ts +0 -154
  166. package/types/pool.ts +0 -125
  167. package/types/query.ts +0 -71
  168. package/types/saga.ts +0 -125
  169. package/types/stream.ts +0 -64
  170. package/types/sync.ts +0 -79
  171. package/types/transaction.ts +0 -79
  172. package/types/write.ts +0 -77
@@ -1,538 +0,0 @@
1
- /**
2
- * watch 查询模块 - MongoDB Change Streams 封装
3
- * @description 提供实时数据监听功能,支持自动重连、断点续传、智能缓存失效
4
- */
5
-
6
- const { EventEmitter } = require('events');
7
-
8
- /**
9
- * ChangeStream 包装类
10
- * 提供自动重连、resumeToken 管理、智能缓存失效等功能
11
- */
12
- class ChangeStreamWrapper {
13
- /**
14
- * @param {Object} changeStream - MongoDB ChangeStream 实例
15
- * @param {Object} collection - MongoDB Collection 实例
16
- * @param {Array} pipeline - 聚合管道
17
- * @param {Object} options - 配置选项
18
- * @param {Object} context - 上下文对象
19
- */
20
- constructor(changeStream, collection, pipeline, options, context) {
21
- this._stream = changeStream;
22
- this._collection = collection;
23
- this._pipeline = pipeline;
24
- this._options = options;
25
- this._context = context;
26
-
27
- // 状态管理
28
- this._closed = false;
29
- this._reconnecting = false;
30
- this._reconnectAttempts = 0;
31
- this._lastResumeToken = null;
32
-
33
- // 统计信息
34
- this._stats = {
35
- totalChanges: 0,
36
- reconnectAttempts: 0,
37
- lastReconnectTime: null,
38
- startTime: Date.now(),
39
- cacheInvalidations: 0,
40
- errors: 0
41
- };
42
-
43
- // 事件发射器
44
- this._emitter = new EventEmitter();
45
-
46
- // 设置事件监听
47
- this._setupListeners();
48
- }
49
-
50
- /**
51
- * 设置 MongoDB ChangeStream 事件监听
52
- * @private
53
- */
54
- _setupListeners() {
55
- if (!this._stream) return;
56
-
57
- // 监听变更事件
58
- this._stream.on('change', (change) => {
59
- this._lastResumeToken = change._id;
60
- this._stats.totalChanges++;
61
- this._handleChange(change);
62
- });
63
-
64
- // 监听错误事件
65
- this._stream.on('error', (error) => {
66
- this._stats.errors++;
67
- this._handleError(error);
68
- });
69
-
70
- // 监听关闭事件
71
- this._stream.on('close', () => {
72
- if (!this._closed) {
73
- this._handleClose();
74
- }
75
- });
76
-
77
- // 监听结束事件
78
- this._stream.on('end', () => {
79
- if (!this._closed) {
80
- this._handleClose();
81
- }
82
- });
83
- }
84
-
85
- /**
86
- * 处理变更事件
87
- * @private
88
- */
89
- async _handleChange(change) {
90
- try {
91
- // 触发用户事件
92
- this._emitter.emit('change', change);
93
-
94
- // 自动缓存失效
95
- if (this._options.autoInvalidateCache !== false) {
96
- await this._invalidateCache(change);
97
- }
98
- } catch (error) {
99
- if (this._context.logger) {
100
- this._context.logger.error('[Watch] Error handling change:', error);
101
- }
102
- }
103
- }
104
-
105
- /**
106
- * 处理错误事件
107
- * @private
108
- */
109
- _handleError(error) {
110
- const errorType = this._classifyError(error);
111
-
112
- if (this._context.logger) {
113
- this._context.logger.warn(`[Watch] Error (${errorType}):`, error.message);
114
- }
115
-
116
- if (errorType === 'transient') {
117
- // 瞬态错误:自动重连,不触发用户事件
118
- this._reconnect();
119
- } else if (errorType === 'resumable') {
120
- // 持久性错误:清除 token,重新开始,触发用户事件
121
- this._lastResumeToken = null;
122
- this._reconnect();
123
- this._emitter.emit('error', error);
124
- } else {
125
- // 致命错误:触发 fatal 事件,然后停止监听
126
- this._emitter.emit('fatal', error);
127
- // 使用 setImmediate 确保 fatal 事件处理器先执行完成
128
- // 避免在事件处理器中访问已关闭的资源
129
- setImmediate(() => this.close());
130
- }
131
- }
132
-
133
- /**
134
- * 处理关闭事件
135
- * @private
136
- */
137
- _handleClose() {
138
- if (this._options.autoReconnect !== false && !this._closed) {
139
- // 自动重连
140
- this._reconnect();
141
- } else {
142
- this._emitter.emit('close');
143
- }
144
- }
145
-
146
- /**
147
- * 分类错误类型
148
- * @private
149
- * @param {Error} error - 错误对象
150
- * @returns {string} 'transient' | 'resumable' | 'fatal'
151
- */
152
- _classifyError(error) {
153
- const code = error.code;
154
- const message = error.message || '';
155
-
156
- // 瞬态错误(自动重试,不通知用户)
157
- if (code === 'ECONNRESET' ||
158
- code === 'ETIMEDOUT' ||
159
- code === 'EPIPE' ||
160
- message.includes('interrupted') ||
161
- message.includes('connection') ||
162
- message.includes('network') ||
163
- message.includes('timeout')) {
164
- return 'transient';
165
- }
166
-
167
- // 持久性错误(清除 token,通知用户)
168
- if (message.includes('resume token') ||
169
- message.includes('change stream history lost') ||
170
- message.includes('ChangeStreamHistoryLost')) {
171
- return 'resumable';
172
- }
173
-
174
- // 致命错误(停止监听)
175
- if (message.includes('collection dropped') ||
176
- message.includes('database dropped') ||
177
- message.includes('ns not found') ||
178
- message.includes('Unauthorized')) {
179
- return 'fatal';
180
- }
181
-
182
- // 默认为瞬态错误(尝试重连)
183
- return 'transient';
184
- }
185
-
186
- /**
187
- * 自动重连
188
- * @private
189
- */
190
- async _reconnect() {
191
- if (this._closed) return;
192
-
193
- // 如果已经在重连,记录并跳过
194
- if (this._reconnecting) {
195
- if (this._context.logger) {
196
- this._context.logger.debug('[Watch] Reconnect already in progress, skipping');
197
- }
198
- return;
199
- }
200
-
201
- this._reconnecting = true;
202
-
203
- try {
204
- this._reconnectAttempts++;
205
- this._stats.reconnectAttempts++;
206
-
207
- // 指数退避
208
- const baseInterval = this._options.reconnectInterval || 1000;
209
- const maxDelay = this._options.maxReconnectDelay || 60000;
210
- const delay = Math.min(
211
- baseInterval * Math.pow(2, this._reconnectAttempts - 1),
212
- maxDelay
213
- );
214
-
215
- this._stats.lastReconnectTime = new Date().toISOString();
216
-
217
- this._emitter.emit('reconnect', {
218
- attempt: this._reconnectAttempts,
219
- delay,
220
- lastError: this._stats.lastError
221
- });
222
-
223
- if (this._context.logger) {
224
- this._context.logger.info(
225
- `[Watch] Reconnecting... attempt ${this._reconnectAttempts}, delay ${delay}ms`
226
- );
227
- }
228
-
229
- await new Promise(resolve => setTimeout(resolve, delay));
230
-
231
- // 关闭旧的 stream
232
- if (this._stream) {
233
- try {
234
- await this._stream.close();
235
- } catch (_) {}
236
- }
237
-
238
- // 构建新的选项
239
- const watchOptions = { ...this._options };
240
-
241
- // 移除 monSQLize 扩展选项
242
- delete watchOptions.autoReconnect;
243
- delete watchOptions.reconnectInterval;
244
- delete watchOptions.maxReconnectDelay;
245
- delete watchOptions.autoInvalidateCache;
246
-
247
- // 添加 resumeToken
248
- if (this._lastResumeToken) {
249
- watchOptions.resumeAfter = this._lastResumeToken;
250
- }
251
-
252
- // 重建 changeStream
253
- this._stream = this._collection.watch(this._pipeline, watchOptions);
254
-
255
- // 重新设置监听
256
- this._setupListeners();
257
-
258
- // 重置状态
259
- this._reconnectAttempts = 0;
260
-
261
- this._emitter.emit('resume', this._lastResumeToken);
262
-
263
- if (this._context.logger) {
264
- this._context.logger.info('[Watch] Reconnected successfully');
265
- }
266
- } catch (error) {
267
- this._stats.lastError = error.message;
268
-
269
- if (this._context.logger) {
270
- this._context.logger.error('[Watch] Reconnect failed:', error.message);
271
- }
272
-
273
- // 使用 setTimeout 避免同步递归调用 _handleError
274
- // 确保当前调用栈完成后再触发下一次重连
275
- setTimeout(() => {
276
- if (!this._closed) {
277
- this._handleError(error);
278
- }
279
- }, 0);
280
- } finally {
281
- // 确保无论成功失败都重置标志
282
- this._reconnecting = false;
283
- }
284
- }
285
-
286
- /**
287
- * 智能缓存失效
288
- * @private
289
- * @param {Object} change - Change event
290
- */
291
- async _invalidateCache(change) {
292
- const { operationType, documentKey, ns } = change;
293
- const collectionName = ns.coll;
294
- const patterns = [];
295
-
296
- // 根据操作类型构建失效模式
297
- switch (operationType) {
298
- case 'insert':
299
- // 失效列表查询和计数
300
- patterns.push(`*:${collectionName}:find:*`);
301
- patterns.push(`*:${collectionName}:findPage:*`);
302
- patterns.push(`*:${collectionName}:count:*`);
303
- patterns.push(`*:${collectionName}:findAndCount:*`);
304
- break;
305
-
306
- case 'update':
307
- case 'replace':
308
- // 失效单个文档和列表查询
309
- if (documentKey?._id) {
310
- patterns.push(`*:${collectionName}:findOne:*${documentKey._id}*`);
311
- patterns.push(`*:${collectionName}:findOneById:${documentKey._id}*`);
312
- }
313
- patterns.push(`*:${collectionName}:find:*`);
314
- patterns.push(`*:${collectionName}:findPage:*`);
315
- patterns.push(`*:${collectionName}:findAndCount:*`);
316
- break;
317
-
318
- case 'delete':
319
- // 失效单个文档和列表查询
320
- if (documentKey?._id) {
321
- patterns.push(`*:${collectionName}:findOne:*${documentKey._id}*`);
322
- patterns.push(`*:${collectionName}:findOneById:${documentKey._id}*`);
323
- }
324
- patterns.push(`*:${collectionName}:find:*`);
325
- patterns.push(`*:${collectionName}:findPage:*`);
326
- patterns.push(`*:${collectionName}:count:*`);
327
- patterns.push(`*:${collectionName}:findAndCount:*`);
328
- break;
329
-
330
- case 'drop':
331
- case 'rename':
332
- case 'dropDatabase':
333
- // 失效整个集合
334
- patterns.push(`*:${collectionName}:*`);
335
- break;
336
-
337
- default:
338
- // 其他操作类型,不失效缓存
339
- return;
340
- }
341
-
342
- // 执行失效
343
- for (const pattern of patterns) {
344
- try {
345
- // 直接调用 cache.delPattern()
346
- // 自动触发 DistributedCacheInvalidator.invalidate()
347
- // 自动广播到其他实例
348
- const cache = this._context.cache;
349
- if (cache && typeof cache.delPattern === 'function') {
350
- await cache.delPattern(pattern);
351
- this._stats.cacheInvalidations++;
352
-
353
- if (this._context.logger) {
354
- this._context.logger.debug(
355
- `[Watch] Invalidated cache: ${pattern}`
356
- );
357
- }
358
- }
359
- } catch (error) {
360
- if (this._context.logger) {
361
- this._context.logger.error(
362
- `[Watch] Cache invalidation failed: ${error.message}`
363
- );
364
- }
365
- }
366
- }
367
- }
368
-
369
- // ============================================
370
- // 公共 API
371
- // ============================================
372
-
373
- /**
374
- * 监听事件
375
- * @param {string} event - 事件名称
376
- * @param {Function} handler - 事件处理函数
377
- * @returns {ChangeStreamWrapper} 返回自身,支持链式调用
378
- */
379
- on(event, handler) {
380
- this._emitter.on(event, handler);
381
- return this;
382
- }
383
-
384
- /**
385
- * 监听事件(一次性)
386
- * @param {string} event - 事件名称
387
- * @param {Function} handler - 事件处理函数
388
- * @returns {ChangeStreamWrapper} 返回自身,支持链式调用
389
- */
390
- once(event, handler) {
391
- this._emitter.once(event, handler);
392
- return this;
393
- }
394
-
395
- /**
396
- * 移除事件监听
397
- * @param {string} event - 事件名称
398
- * @param {Function} handler - 事件处理函数
399
- * @returns {ChangeStreamWrapper} 返回自身,支持链式调用
400
- */
401
- off(event, handler) {
402
- if (this._emitter.off) {
403
- this._emitter.off(event, handler);
404
- } else {
405
- this._emitter.removeListener(event, handler);
406
- }
407
- return this;
408
- }
409
-
410
- /**
411
- * 关闭监听
412
- */
413
- async close() {
414
- if (this._closed) return;
415
-
416
- this._closed = true;
417
-
418
- if (this._stream) {
419
- try {
420
- await this._stream.close();
421
- } catch (error) {
422
- if (this._context.logger) {
423
- this._context.logger.error('[Watch] Error closing stream:', error);
424
- }
425
- }
426
- }
427
-
428
- // 先触发 close 事件,再移除监听器
429
- this._emitter.emit('close');
430
-
431
- // 移除所有事件监听器
432
- this._emitter.removeAllListeners();
433
-
434
- if (this._context.logger) {
435
- this._context.logger.info('[Watch] Closed');
436
- }
437
- }
438
-
439
- /**
440
- * 检查是否已关闭
441
- * @returns {boolean}
442
- */
443
- isClosed() {
444
- return this._closed;
445
- }
446
-
447
- /**
448
- * 获取当前 resumeToken
449
- * @returns {Object|null}
450
- */
451
- getResumeToken() {
452
- return this._lastResumeToken;
453
- }
454
-
455
- /**
456
- * 获取统计信息
457
- * @returns {Object}
458
- */
459
- getStats() {
460
- return {
461
- totalChanges: this._stats.totalChanges,
462
- reconnectAttempts: this._stats.reconnectAttempts,
463
- lastReconnectTime: this._stats.lastReconnectTime,
464
- uptime: Date.now() - this._stats.startTime,
465
- isActive: !this._closed && !this._reconnecting,
466
- cacheInvalidations: this._stats.cacheInvalidations,
467
- errors: this._stats.errors
468
- };
469
- }
470
- }
471
-
472
- /**
473
- * 创建 watch 操作
474
- * @param {Object} context - 上下文对象
475
- * @returns {Object} 包含 watch 方法的对象
476
- */
477
- function createWatchOps(context) {
478
- return {
479
- /**
480
- * 监听集合变更
481
- * @param {Array} [pipeline=[]] - 聚合管道(过滤事件)
482
- * @param {Object} [options={}] - 配置选项
483
- * @returns {ChangeStreamWrapper}
484
- */
485
- watch: (pipeline = [], options = {}) => {
486
- // 参数验证
487
- if (!Array.isArray(pipeline)) {
488
- throw new Error('pipeline must be an array');
489
- }
490
-
491
- // ✅ v1.3.0: 自动转换 ObjectId 字符串
492
- const { convertAggregationPipeline } = require('../../utils/objectid-converter');
493
- const convertedPipeline = convertAggregationPipeline(pipeline, 0, {
494
- logger: context.logger,
495
- excludeFields: context.autoConvertConfig?.excludeFields,
496
- customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
497
- maxDepth: context.autoConvertConfig?.maxDepth || 5
498
- });
499
-
500
- // 构建 MongoDB watch 选项
501
- const watchOptions = {
502
- fullDocument: options.fullDocument || 'updateLookup',
503
- ...options
504
- };
505
-
506
- // 移除 monSQLize 扩展选项(避免传递给 MongoDB)
507
- delete watchOptions.autoReconnect;
508
- delete watchOptions.reconnectInterval;
509
- delete watchOptions.maxReconnectDelay;
510
- delete watchOptions.autoInvalidateCache;
511
-
512
- // 创建 MongoDB ChangeStream
513
- const changeStream = context.collection.watch(convertedPipeline, watchOptions);
514
-
515
- // 包装为 ChangeStreamWrapper
516
- const wrapper = new ChangeStreamWrapper(
517
- changeStream,
518
- context.collection,
519
- convertedPipeline,
520
- options,
521
- context
522
- );
523
-
524
- if (context.logger) {
525
- context.logger.info('[Watch] Started watching collection:', context.collection.collectionName);
526
- }
527
-
528
- return wrapper;
529
- }
530
- };
531
- }
532
-
533
- module.exports = {
534
- createWatchOps,
535
- ChangeStreamWrapper
536
- };
537
-
538
-
@@ -1,65 +0,0 @@
1
- /**
2
- * 批量操作重试机制
3
- * 供 insertBatch、deleteBatch、updateBatch 复用
4
- */
5
-
6
- /**
7
- * 执行批次操作(带重试)
8
- * @param {Function} fn - 要执行的批次操作函数
9
- * @param {Object} retryContext - 重试上下文
10
- * @param {string} retryContext.onError - 错误策略: 'stop'/'skip'/'collect'/'retry'
11
- * @param {number} retryContext.retryAttempts - 最大重试次数
12
- * @param {number} retryContext.retryDelay - 重试延迟(毫秒)
13
- * @param {Function} retryContext.onRetry - 重试回调
14
- * @param {number} retryContext.batchIndex - 当前批次索引
15
- * @returns {Promise<Object>} { success, result, attempts }
16
- */
17
- async function executeBatchWithRetry(fn, retryContext) {
18
- const {
19
- onError = 'stop',
20
- retryAttempts = 3,
21
- retryDelay = 1000,
22
- onRetry,
23
- batchIndex = 0
24
- } = retryContext;
25
-
26
- let lastError = null;
27
- let attempts = 0;
28
- const maxAttempts = onError === 'retry' ? retryAttempts + 1 : 1;
29
-
30
- while (attempts < maxAttempts) {
31
- try {
32
- const result = await fn();
33
- return { success: true, result, attempts };
34
- } catch (error) {
35
- lastError = error;
36
- attempts++;
37
-
38
- // 还有重试机会
39
- if (attempts < maxAttempts) {
40
- // 触发重试回调
41
- if (onRetry) {
42
- onRetry({
43
- batchIndex,
44
- attempt: attempts,
45
- maxAttempts: retryAttempts,
46
- error,
47
- nextRetryDelay: retryDelay
48
- });
49
- }
50
-
51
- // 延迟后重试
52
- if (retryDelay > 0) {
53
- await new Promise(resolve => setTimeout(resolve, retryDelay));
54
- }
55
- }
56
- }
57
- }
58
-
59
- // 所有重试都失败
60
- throw lastError;
61
- }
62
-
63
- module.exports = { executeBatchWithRetry };
64
-
65
-