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 } = require('../../utils/objectid-converter');
9
10
 
10
11
  /**
11
12
  * 创建 insertBatch 操作
@@ -46,26 +47,34 @@ function createInsertBatchOps(context) {
46
47
  if (!Array.isArray(documents)) {
47
48
  throw createError(
48
49
  ErrorCodes.DOCUMENTS_REQUIRED,
49
- "documents 必须是数组类型",
50
- [{ field: "documents", type: "array.required", message: "documents 是必需参数且必须是数组" }]
50
+ 'documents 必须是数组类型',
51
+ [{ field: 'documents', type: 'array.required', message: 'documents 是必需参数且必须是数组' }]
51
52
  );
52
53
  }
53
54
 
54
55
  if (documents.length === 0) {
55
56
  throw createError(
56
57
  ErrorCodes.DOCUMENTS_REQUIRED,
57
- "documents 数组不能为空",
58
- [{ field: "documents", type: "array.min", message: "documents 至少需要包含一个文档" }]
58
+ 'documents 数组不能为空',
59
+ [{ field: 'documents', type: 'array.min', message: 'documents 至少需要包含一个文档' }]
59
60
  );
60
61
  }
61
62
 
62
- // 2. 提取并验证选项
63
+ // ✅ v1.3.0: 自动转换 ObjectId 字符串
64
+ const convertedDocuments = documents.map(doc => convertObjectIdStrings(doc, 'document', 0, new WeakSet(), {
65
+ logger: context.logger,
66
+ excludeFields: context.autoConvertConfig?.excludeFields,
67
+ customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
68
+ maxDepth: context.autoConvertConfig?.maxDepth
69
+ }));
70
+
71
+ // 2. 提取选项
63
72
  const {
64
73
  batchSize = 1000,
65
74
  concurrency = 1,
66
75
  ordered = false,
67
76
  onProgress,
68
- onError = "stop",
77
+ onError = 'stop',
69
78
  retryAttempts = 3,
70
79
  retryDelay = 1000,
71
80
  onRetry,
@@ -73,51 +82,51 @@ function createInsertBatchOps(context) {
73
82
  } = options;
74
83
 
75
84
  // 验证参数
76
- if (typeof batchSize !== "number" || batchSize < 1) {
85
+ if (typeof batchSize !== 'number' || batchSize < 1) {
77
86
  throw createError(
78
87
  ErrorCodes.INVALID_PARAMETER,
79
- "batchSize 必须是大于 0 的数字",
80
- [{ field: "batchSize", type: "number.min", message: "batchSize 必须大于 0" }]
88
+ 'batchSize 必须是大于 0 的数字',
89
+ [{ field: 'batchSize', type: 'number.min', message: 'batchSize 必须大于 0' }]
81
90
  );
82
91
  }
83
92
 
84
- if (typeof concurrency !== "number" || concurrency < 1) {
93
+ if (typeof concurrency !== 'number' || concurrency < 1) {
85
94
  throw createError(
86
95
  ErrorCodes.INVALID_PARAMETER,
87
- "concurrency 必须是大于 0 的数字",
88
- [{ field: "concurrency", type: "number.min", message: "concurrency 必须大于 0" }]
96
+ 'concurrency 必须是大于 0 的数字',
97
+ [{ field: 'concurrency', type: 'number.min', message: 'concurrency 必须大于 0' }]
89
98
  );
90
99
  }
91
100
 
92
- const validErrorStrategies = ["stop", "skip", "collect", "retry"];
101
+ const validErrorStrategies = ['stop', 'skip', 'collect', 'retry'];
93
102
  if (!validErrorStrategies.includes(onError)) {
94
103
  throw createError(
95
104
  ErrorCodes.INVALID_PARAMETER,
96
- `onError 必须是以下值之一: ${validErrorStrategies.join(", ")}`,
97
- [{ field: "onError", type: "enum", message: `有效值: ${validErrorStrategies.join(", ")}` }]
105
+ `onError 必须是以下值之一: ${validErrorStrategies.join(', ')}`,
106
+ [{ field: 'onError', type: 'enum', message: `有效值: ${validErrorStrategies.join(', ')}` }]
98
107
  );
99
108
  }
100
109
 
101
- if (typeof retryAttempts !== "number" || retryAttempts < 0) {
110
+ if (typeof retryAttempts !== 'number' || retryAttempts < 0) {
102
111
  throw createError(
103
112
  ErrorCodes.INVALID_PARAMETER,
104
- "retryAttempts 必须是非负数",
105
- [{ field: "retryAttempts", type: "number.min", message: "retryAttempts 必须 >= 0" }]
113
+ 'retryAttempts 必须是非负数',
114
+ [{ field: 'retryAttempts', type: 'number.min', message: 'retryAttempts 必须 >= 0' }]
106
115
  );
107
116
  }
108
117
 
109
- if (typeof retryDelay !== "number" || retryDelay < 0) {
118
+ if (typeof retryDelay !== 'number' || retryDelay < 0) {
110
119
  throw createError(
111
120
  ErrorCodes.INVALID_PARAMETER,
112
- "retryDelay 必须是非负数",
113
- [{ field: "retryDelay", type: "number.min", message: "retryDelay 必须 >= 0" }]
121
+ 'retryDelay 必须是非负数',
122
+ [{ field: 'retryDelay', type: 'number.min', message: 'retryDelay 必须 >= 0' }]
114
123
  );
115
124
  }
116
125
 
117
126
  // 3. 构建操作上下文
118
- const operation = "insertBatch";
127
+ const operation = 'insertBatch';
119
128
  const ns = `${databaseName}.${collectionName}`;
120
- const totalCount = documents.length;
129
+ const totalCount = convertedDocuments.length;
121
130
  const totalBatches = Math.ceil(totalCount / batchSize);
122
131
 
123
132
  // 结果统计
@@ -138,16 +147,16 @@ function createInsertBatchOps(context) {
138
147
  totalBatches,
139
148
  concurrency,
140
149
  onError,
141
- retryAttempts: onError === "retry" ? retryAttempts : 0
150
+ retryAttempts: onError === 'retry' ? retryAttempts : 0
142
151
  });
143
152
 
144
153
  try {
145
154
  // 4. 分割文档为批次
146
155
  const batches = [];
147
- for (let i = 0; i < documents.length; i += batchSize) {
156
+ for (let i = 0; i < convertedDocuments.length; i += batchSize) {
148
157
  batches.push({
149
158
  index: batches.length,
150
- documents: documents.slice(i, Math.min(i + batchSize, documents.length)),
159
+ documents: convertedDocuments.slice(i, Math.min(i + batchSize, convertedDocuments.length)),
151
160
  startIndex: i
152
161
  });
153
162
  }
@@ -178,7 +187,7 @@ function createInsertBatchOps(context) {
178
187
  try {
179
188
  const nsObj = {
180
189
  iid: instanceId,
181
- type: "mongodb",
190
+ type: 'mongodb',
182
191
  db: databaseName,
183
192
  collection: collectionName
184
193
  };
@@ -222,7 +231,7 @@ function createInsertBatchOps(context) {
222
231
  }
223
232
 
224
233
  // 8. 如果有错误且策略是 stop,抛出错误
225
- if (result.errors.length > 0 && onError === "stop") {
234
+ if (result.errors.length > 0 && onError === 'stop') {
226
235
  throw createError(
227
236
  ErrorCodes.WRITE_ERROR,
228
237
  `insertBatch 操作失败: 在批次 ${result.errors[0].batchIndex + 1}/${totalBatches} 遇到错误`,
@@ -268,7 +277,7 @@ function createInsertBatchOps(context) {
268
277
 
269
278
  let lastError = null;
270
279
  let attempts = 0;
271
- const maxAttempts = onError === "retry" ? retryAttempts + 1 : 1;
280
+ const maxAttempts = onError === 'retry' ? retryAttempts + 1 : 1;
272
281
 
273
282
  while (attempts < maxAttempts) {
274
283
  try {
@@ -302,7 +311,7 @@ function createInsertBatchOps(context) {
302
311
  batchIndex: batch.index,
303
312
  attempt: attempts,
304
313
  maxAttempts: retryAttempts,
305
- error: error,
314
+ error,
306
315
  nextRetryDelay: retryDelay
307
316
  });
308
317
  }
@@ -382,11 +391,11 @@ function createInsertBatchOps(context) {
382
391
  }
383
392
 
384
393
  // 根据错误策略处理
385
- if (retryContext.onError === "stop") {
394
+ if (retryContext.onError === 'stop') {
386
395
  throw batchResult.error;
387
- } else if (retryContext.onError === "skip" || retryContext.onError === "retry") {
396
+ } else if (retryContext.onError === 'skip' || retryContext.onError === 'retry') {
388
397
  retryContext.logger.warn(`[insertBatch] 跳过失败批次 ${batch.index + 1}/${batches.length}`, errorInfo);
389
- } else if (retryContext.onError === "collect") {
398
+ } else if (retryContext.onError === 'collect') {
390
399
  retryContext.logger.warn(`[insertBatch] 收集错误,继续执行 ${batch.index + 1}/${batches.length}`, errorInfo);
391
400
  }
392
401
  }
@@ -469,7 +478,7 @@ function createInsertBatchOps(context) {
469
478
  });
470
479
  }
471
480
 
472
- if (retryContext.onError === "stop") {
481
+ if (retryContext.onError === 'stop') {
473
482
  throw batchResult.error;
474
483
  }
475
484
  }
@@ -5,7 +5,8 @@
5
5
 
6
6
  const { createError, ErrorCodes } = require('../../errors');
7
7
  const CacheFactory = require('../../cache');
8
- const { isInTransaction, getTransactionFromSession } = require("../common/transaction-aware");
8
+ const { isInTransaction, getTransactionFromSession } = require('../common/transaction-aware');
9
+ const { convertObjectIdStrings } = require('../../utils/objectid-converter');
9
10
 
10
11
  /**
11
12
  * 创建 insertMany 操作
@@ -36,7 +37,7 @@ function createInsertManyOps(context) {
36
37
  * @param {string} [options.comment] - 操作注释(用于日志追踪)
37
38
  * @returns {Promise<Object>} 插入结果 { acknowledged, insertedCount, insertedIds }
38
39
  * @throws {Error} 当 documents 参数无效时
39
- *
40
+ *
40
41
  * @example
41
42
  * const result = await collection('users').insertMany([
42
43
  * { name: 'Alice', age: 25 },
@@ -62,36 +63,44 @@ function createInsertManyOps(context) {
62
63
  if (!Array.isArray(documents)) {
63
64
  throw createError(
64
65
  ErrorCodes.DOCUMENTS_REQUIRED,
65
- "documents 必须是数组类型",
66
- [{ field: "documents", type: "array.required", message: "documents 是必需参数且必须是数组" }]
66
+ 'documents 必须是数组类型',
67
+ [{ field: 'documents', type: 'array.required', message: 'documents 是必需参数且必须是数组' }]
67
68
  );
68
69
  }
69
70
 
70
71
  if (documents.length === 0) {
71
72
  throw createError(
72
73
  ErrorCodes.DOCUMENTS_REQUIRED,
73
- "documents 数组不能为空",
74
- [{ field: "documents", type: "array.min", message: "documents 至少需要包含一个文档" }]
74
+ 'documents 数组不能为空',
75
+ [{ field: 'documents', type: 'array.min', message: 'documents 至少需要包含一个文档' }]
75
76
  );
76
77
  }
77
78
 
78
79
  // 验证每个文档都是对象
79
80
  const invalidDocs = documents.filter((doc, index) => {
80
- return !doc || typeof doc !== "object" || Array.isArray(doc);
81
+ return !doc || typeof doc !== 'object' || Array.isArray(doc);
81
82
  });
82
83
 
83
84
  if (invalidDocs.length > 0) {
84
85
  throw createError(
85
86
  ErrorCodes.DOCUMENTS_REQUIRED,
86
- "documents 中的所有元素必须是对象类型",
87
+ 'documents 中的所有元素必须是对象类型',
87
88
  invalidDocs.map((doc, idx) => ({
88
89
  field: `documents[${idx}]`,
89
- type: "object.required",
90
- message: "必须是对象类型"
90
+ type: 'object.required',
91
+ message: '必须是对象类型'
91
92
  }))
92
93
  );
93
94
  }
94
95
 
96
+ // ✅ v1.3.0: 自动转换 ObjectId 字符串
97
+ const convertedDocuments = documents.map(doc => convertObjectIdStrings(doc, 'document', 0, new WeakSet(), {
98
+ logger: context.logger,
99
+ excludeFields: context.autoConvertConfig?.excludeFields,
100
+ customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
101
+ maxDepth: context.autoConvertConfig?.maxDepth
102
+ }));
103
+
95
104
  // 2. 构建操作上下文
96
105
  const operation = 'insertMany';
97
106
  const ns = `${databaseName}.${collectionName}`;
@@ -99,7 +108,7 @@ function createInsertManyOps(context) {
99
108
 
100
109
  try {
101
110
  // 3. 执行批量插入操作
102
- const result = await nativeCollection.insertMany(documents, options);
111
+ const result = await nativeCollection.insertMany(convertedDocuments, options);
103
112
 
104
113
  // 4. 自动失效缓存
105
114
  if (cache) {
@@ -107,7 +116,7 @@ function createInsertManyOps(context) {
107
116
  // 使用标准命名空间模式删除该集合的所有缓存
108
117
  const nsObj = {
109
118
  iid: instanceId,
110
- type: "mongodb",
119
+ type: 'mongodb',
111
120
  db: databaseName,
112
121
  collection: collectionName
113
122
  };
@@ -186,7 +195,7 @@ function createInsertManyOps(context) {
186
195
  // MongoDB 重复键错误
187
196
  throw createError(
188
197
  ErrorCodes.DUPLICATE_KEY,
189
- "批量插入失败:违反唯一性约束",
198
+ '批量插入失败:违反唯一性约束',
190
199
  [{ message: error.message, writeErrors: error.writeErrors }],
191
200
  error
192
201
  );
@@ -5,7 +5,8 @@
5
5
 
6
6
  const { createError, ErrorCodes } = require('../../errors');
7
7
  const CacheFactory = require('../../cache');
8
- const { isInTransaction, getTransactionFromSession } = require("../common/transaction-aware");
8
+ const { isInTransaction, getTransactionFromSession } = require('../common/transaction-aware');
9
+ const { convertObjectIdStrings } = require('../../utils/objectid-converter');
9
10
 
10
11
  /**
11
12
  * 创建 insertOne 操作
@@ -35,7 +36,7 @@ function createInsertOneOps(context) {
35
36
  * @param {string} [options.comment] - 操作注释(用于日志追踪)
36
37
  * @returns {Promise<Object>} 插入结果 { acknowledged, insertedId }
37
38
  * @throws {Error} 当 document 参数无效时
38
- *
39
+ *
39
40
  * @example
40
41
  * const result = await collection('users').insertOne(
41
42
  * { name: 'Alice', age: 25 }
@@ -53,21 +54,29 @@ function createInsertOneOps(context) {
53
54
  const startTime = Date.now();
54
55
 
55
56
  // 1. 参数验证
56
- if (!document || typeof document !== "object" || Array.isArray(document)) {
57
+ if (!document || typeof document !== 'object' || Array.isArray(document)) {
57
58
  throw createError(
58
59
  ErrorCodes.DOCUMENT_REQUIRED,
59
- "document 必须是对象类型",
60
- [{ field: "document", type: "object.required", message: "document 是必需参数且必须是对象" }]
60
+ 'document 必须是对象类型',
61
+ [{ field: 'document', type: 'object.required', message: 'document 是必需参数且必须是对象' }]
61
62
  );
62
63
  }
63
64
 
65
+ // ✅ v1.3.0: 自动转换 ObjectId 字符串
66
+ const convertedDocument = convertObjectIdStrings(document, 'document', 0, new WeakSet(), {
67
+ logger: context.logger,
68
+ excludeFields: context.autoConvertConfig?.excludeFields,
69
+ customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
70
+ maxDepth: context.autoConvertConfig?.maxDepth
71
+ });
72
+
64
73
  // 2. 构建操作上下文
65
74
  const operation = 'insertOne';
66
75
  const ns = `${databaseName}.${collectionName}`;
67
76
 
68
77
  try {
69
78
  // 3. 执行插入操作
70
- const result = await nativeCollection.insertOne(document, options);
79
+ const result = await nativeCollection.insertOne(convertedDocument, options);
71
80
 
72
81
  // 4. 自动失效缓存
73
82
  if (cache) {
@@ -75,7 +84,7 @@ function createInsertOneOps(context) {
75
84
  // 使用标准命名空间模式删除该集合的所有缓存
76
85
  const ns = {
77
86
  iid: instanceId,
78
- type: "mongodb",
87
+ type: 'mongodb',
79
88
  db: databaseName,
80
89
  collection: collectionName
81
90
  };
@@ -151,8 +160,8 @@ function createInsertOneOps(context) {
151
160
  // MongoDB 重复键错误
152
161
  throw createError(
153
162
  ErrorCodes.DUPLICATE_KEY,
154
- "文档插入失败:违反唯一性约束",
155
- [{ field: "_id", message: error.message }],
163
+ '文档插入失败:违反唯一性约束',
164
+ [{ field: '_id', message: error.message }],
156
165
  error
157
166
  );
158
167
  }
@@ -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 } = require('../../utils/objectid-converter');
9
10
 
10
11
  /**
11
12
  * 创建 replaceOne 操作
@@ -59,39 +60,54 @@ function createReplaceOneOps(context) {
59
60
  const startTime = Date.now();
60
61
 
61
62
  // 1. 参数验证
62
- if (!filter || typeof filter !== "object" || Array.isArray(filter)) {
63
+ if (!filter || typeof filter !== 'object' || Array.isArray(filter)) {
63
64
  throw createError(
64
65
  ErrorCodes.INVALID_ARGUMENT,
65
- "filter 必须是对象类型",
66
- [{ field: "filter", type: "object.required", message: "filter 是必需参数且必须是对象" }]
66
+ 'filter 必须是对象类型',
67
+ [{ field: 'filter', type: 'object.required', message: 'filter 是必需参数且必须是对象' }]
67
68
  );
68
69
  }
69
70
 
70
- if (!replacement || typeof replacement !== "object" || Array.isArray(replacement)) {
71
+ if (!replacement || typeof replacement !== 'object' || Array.isArray(replacement)) {
71
72
  throw createError(
72
73
  ErrorCodes.INVALID_ARGUMENT,
73
- "replacement 必须是对象类型",
74
- [{ field: "replacement", type: "object.required", message: "replacement 是必需参数且必须是对象" }]
74
+ 'replacement 必须是对象类型',
75
+ [{ field: 'replacement', type: 'object.required', message: 'replacement 是必需参数且必须是对象' }]
75
76
  );
76
77
  }
77
78
 
78
79
  // 验证 replacement 不包含更新操作符(防止误用)
79
80
  const replacementKeys = Object.keys(replacement);
80
- if (replacementKeys.some(key => key.startsWith("$"))) {
81
+ if (replacementKeys.some(key => key.startsWith('$'))) {
81
82
  throw createError(
82
83
  ErrorCodes.INVALID_ARGUMENT,
83
- "replacement 不能包含更新操作符(如 $set, $inc 等)",
84
- [{ field: "replacement", type: "object.invalid", message: "replaceOne 用于完整替换文档,请使用 updateOne 进行部分更新" }]
84
+ 'replacement 不能包含更新操作符(如 $set, $inc 等)',
85
+ [{ field: 'replacement', type: 'object.invalid', message: 'replaceOne 用于完整替换文档,请使用 updateOne 进行部分更新' }]
85
86
  );
86
87
  }
87
88
 
89
+ // ✅ v1.3.0: 自动转换 ObjectId 字符串
90
+ const convertedFilter = convertObjectIdStrings(filter, 'filter', 0, new WeakSet(), {
91
+ logger: context.logger,
92
+ excludeFields: context.autoConvertConfig?.excludeFields,
93
+ customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
94
+ maxDepth: context.autoConvertConfig?.maxDepth
95
+ });
96
+
97
+ const convertedReplacement = convertObjectIdStrings(replacement, 'document', 0, new WeakSet(), {
98
+ logger: context.logger,
99
+ excludeFields: context.autoConvertConfig?.excludeFields,
100
+ customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
101
+ maxDepth: context.autoConvertConfig?.maxDepth
102
+ });
103
+
88
104
  // 2. 构建操作上下文
89
- const operation = "replaceOne";
105
+ const operation = 'replaceOne';
90
106
  const ns = `${databaseName}.${collectionName}`;
91
107
 
92
108
  try {
93
109
  // 3. 执行替换操作
94
- const result = await nativeCollection.replaceOne(filter, replacement, options);
110
+ const result = await nativeCollection.replaceOne(convertedFilter, convertedReplacement, options);
95
111
 
96
112
  // 4. 自动失效缓存
97
113
  if (cache && result.modifiedCount > 0) {
@@ -99,7 +115,7 @@ function createReplaceOneOps(context) {
99
115
  // 使用标准命名空间模式删除该集合的所有缓存
100
116
  const ns = {
101
117
  iid: instanceId,
102
- type: "mongodb",
118
+ type: 'mongodb',
103
119
  db: databaseName,
104
120
  collection: collectionName
105
121
  };
@@ -176,8 +192,8 @@ function createReplaceOneOps(context) {
176
192
  // MongoDB 重复键错误(可能在 upsert 时发生)
177
193
  throw createError(
178
194
  ErrorCodes.DUPLICATE_KEY,
179
- "替换失败:违反唯一性约束",
180
- [{ field: "_id", message: error.message }],
195
+ '替换失败:违反唯一性约束',
196
+ [{ field: '_id', message: error.message }],
181
197
  error
182
198
  );
183
199
  }
@@ -44,8 +44,8 @@ function detectDriverVersion() {
44
44
  _driverVersionChecked = true;
45
45
 
46
46
  try {
47
- const mongodb = require("mongodb");
48
- const versionString = mongodb.version || require("mongodb/package.json").version;
47
+ const mongodb = require('mongodb');
48
+ const versionString = mongodb.version || require('mongodb/package.json').version;
49
49
  const match = versionString.match(/^(\d+)\./);
50
50
  if (match) {
51
51
  _detectedDriverMajorVersion = parseInt(match[1], 10);
@@ -74,21 +74,21 @@ function warnUnsupportedDriverVersion(logger) {
74
74
  if (version < 6) {
75
75
  // 驱动版本小于 6.x,可能不兼容
76
76
  if (logger && logger.warn) {
77
- logger.warn("[result-handler] ⚠️ 检测到 MongoDB 驱动版本过旧", {
77
+ logger.warn('[result-handler] ⚠️ 检测到 MongoDB 驱动版本过旧', {
78
78
  detectedVersion: version,
79
- supportedVersion: "6.x",
80
- message: "monSQLize 专为 MongoDB 驱动 6.x 设计,旧版本可能存在兼容性问题",
81
- recommendation: "建议升级到 MongoDB Node.js 驱动 ^6.0.0"
79
+ supportedVersion: '6.x',
80
+ message: 'monSQLize 专为 MongoDB 驱动 6.x 设计,旧版本可能存在兼容性问题',
81
+ recommendation: '建议升级到 MongoDB Node.js 驱动 ^6.0.0'
82
82
  });
83
83
  }
84
84
  } else if (version > 6) {
85
85
  // 驱动版本大于 6.x,未经测试
86
86
  if (logger && logger.warn) {
87
- logger.warn("[result-handler] ⚠️ 检测到 MongoDB 驱动版本未经测试", {
87
+ logger.warn('[result-handler] ⚠️ 检测到 MongoDB 驱动版本未经测试', {
88
88
  detectedVersion: version,
89
- testedVersion: "6.x",
90
- message: "monSQLize 已针对 MongoDB 驱动 6.x 测试,新版本可能存在未知问题",
91
- recommendation: "如遇问题,请查看技术分析报告或回退到驱动 6.x"
89
+ testedVersion: '6.x',
90
+ message: 'monSQLize 已针对 MongoDB 驱动 6.x 测试,新版本可能存在未知问题',
91
+ recommendation: '如遇问题,请查看技术分析报告或回退到驱动 6.x'
92
92
  });
93
93
  }
94
94
  }
@@ -133,7 +133,7 @@ function handleFindOneAndResult(result, options = {}, logger = null) {
133
133
  // 情况 1:result 为 null 或 undefined(驱动在某些边界情况下可能返回 null)
134
134
  if (!result || result === null) {
135
135
  if (logger && logger.debug) {
136
- logger.debug("[result-handler] Result is null/undefined, returning empty result", {
136
+ logger.debug('[result-handler] Result is null/undefined, returning empty result', {
137
137
  includeMetadata: options.includeResultMetadata
138
138
  });
139
139
  }
@@ -152,14 +152,14 @@ function handleFindOneAndResult(result, options = {}, logger = null) {
152
152
  }
153
153
 
154
154
  // 情况 2:result 是对象但缺少 lastErrorObject(驱动版本差异或异常情况)
155
- if (typeof result === "object" && !result.lastErrorObject) {
155
+ if (typeof result === 'object' && !result.lastErrorObject) {
156
156
  if (logger && logger.warn) {
157
- logger.warn("[result-handler] ⚠️ Result missing lastErrorObject, possible driver version issue", {
157
+ logger.warn('[result-handler] ⚠️ Result missing lastErrorObject, possible driver version issue', {
158
158
  hasValue: result.value !== undefined,
159
159
  hasOk: result.ok !== undefined,
160
160
  resultKeys: Object.keys(result),
161
161
  driverVersion: detectDriverVersion(),
162
- recommendation: "这可能表明 MongoDB 驱动返回了非预期的格式,请检查驱动版本"
162
+ recommendation: '这可能表明 MongoDB 驱动返回了非预期的格式,请检查驱动版本'
163
163
  });
164
164
  }
165
165
 
@@ -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
  * 创建 updateMany 操作
@@ -60,39 +61,54 @@ function createUpdateManyOps(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 等更新操作符" }]
85
+ 'update 必须使用更新操作符(如 $set, $inc 等)',
86
+ [{ field: 'update', type: 'object.invalid', message: '请使用 $set, $inc, $push 等更新操作符' }]
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 = "updateMany";
106
+ const operation = 'updateMany';
91
107
  const ns = `${databaseName}.${collectionName}`;
92
108
 
93
109
  try {
94
- // 3. 执行更新操作
95
- const result = await nativeCollection.updateMany(filter, update, options);
110
+ // 3. 执行批量更新操作
111
+ const result = await nativeCollection.updateMany(convertedFilter, convertedUpdate, options);
96
112
 
97
113
  // 4. 自动失效缓存(只要有匹配,就失效缓存)
98
114
  if (cache && result.matchedCount > 0) {
@@ -100,7 +116,7 @@ function createUpdateManyOps(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
  };
@@ -182,8 +198,8 @@ function createUpdateManyOps(context) {
182
198
  // MongoDB 重复键错误(可能在 upsert 时发生)
183
199
  throw createError(
184
200
  ErrorCodes.DUPLICATE_KEY,
185
- "批量更新失败:违反唯一性约束",
186
- [{ field: "_id", message: error.message }],
201
+ '批量更新失败:违反唯一性约束',
202
+ [{ field: '_id', message: error.message }],
187
203
  error
188
204
  );
189
205
  }