monsqlize 1.0.1 → 1.0.5

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 (66) hide show
  1. package/CHANGELOG.md +204 -2464
  2. package/README.md +735 -1198
  3. package/index.d.ts +942 -18
  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 +173 -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 +225 -29
  17. package/lib/model/features/soft-delete.js +348 -0
  18. package/lib/model/features/version.js +156 -0
  19. package/lib/model/index.js +756 -0
  20. package/lib/mongodb/common/accessor-helpers.js +17 -3
  21. package/lib/mongodb/connect.js +68 -13
  22. package/lib/mongodb/index.js +153 -6
  23. package/lib/mongodb/management/collection-ops.js +4 -4
  24. package/lib/mongodb/management/index-ops.js +18 -18
  25. package/lib/mongodb/management/validation-ops.js +3 -3
  26. package/lib/mongodb/queries/aggregate.js +14 -5
  27. package/lib/mongodb/queries/chain.js +52 -45
  28. package/lib/mongodb/queries/count.js +16 -6
  29. package/lib/mongodb/queries/distinct.js +15 -6
  30. package/lib/mongodb/queries/find-and-count.js +22 -13
  31. package/lib/mongodb/queries/find-by-ids.js +5 -5
  32. package/lib/mongodb/queries/find-one-by-id.js +1 -1
  33. package/lib/mongodb/queries/find-one.js +12 -3
  34. package/lib/mongodb/queries/find-page.js +12 -0
  35. package/lib/mongodb/queries/find.js +15 -6
  36. package/lib/mongodb/queries/watch.js +11 -2
  37. package/lib/mongodb/writes/common/batch-retry.js +64 -0
  38. package/lib/mongodb/writes/delete-batch.js +322 -0
  39. package/lib/mongodb/writes/delete-many.js +20 -11
  40. package/lib/mongodb/writes/delete-one.js +18 -9
  41. package/lib/mongodb/writes/find-one-and-delete.js +19 -10
  42. package/lib/mongodb/writes/find-one-and-replace.js +36 -20
  43. package/lib/mongodb/writes/find-one-and-update.js +36 -20
  44. package/lib/mongodb/writes/increment-one.js +22 -7
  45. package/lib/mongodb/writes/index.js +17 -13
  46. package/lib/mongodb/writes/insert-batch.js +46 -37
  47. package/lib/mongodb/writes/insert-many.js +22 -13
  48. package/lib/mongodb/writes/insert-one.js +18 -9
  49. package/lib/mongodb/writes/replace-one.js +33 -17
  50. package/lib/mongodb/writes/result-handler.js +14 -14
  51. package/lib/mongodb/writes/update-batch.js +358 -0
  52. package/lib/mongodb/writes/update-many.js +34 -18
  53. package/lib/mongodb/writes/update-one.js +33 -17
  54. package/lib/mongodb/writes/upsert-one.js +25 -9
  55. package/lib/operators.js +1 -1
  56. package/lib/redis-cache-adapter.js +3 -3
  57. package/lib/slow-query-log/base-storage.js +69 -0
  58. package/lib/slow-query-log/batch-queue.js +96 -0
  59. package/lib/slow-query-log/config-manager.js +195 -0
  60. package/lib/slow-query-log/index.js +237 -0
  61. package/lib/slow-query-log/mongodb-storage.js +323 -0
  62. package/lib/slow-query-log/query-hash.js +38 -0
  63. package/lib/transaction/DistributedCacheLockManager.js +240 -5
  64. package/lib/transaction/Transaction.js +1 -1
  65. package/lib/utils/objectid-converter.js +566 -0
  66. package/package.json +18 -6
@@ -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