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.
- package/CHANGELOG.md +204 -2464
- package/README.md +735 -1198
- package/index.d.ts +942 -18
- package/lib/cache.js +8 -8
- package/lib/common/validation.js +64 -1
- package/lib/connect.js +3 -3
- package/lib/errors.js +10 -0
- package/lib/index.js +173 -9
- package/lib/infrastructure/ssh-tunnel-ssh2.js +211 -0
- package/lib/infrastructure/ssh-tunnel.js +40 -0
- package/lib/infrastructure/uri-parser.js +35 -0
- package/lib/lock/Lock.js +66 -0
- package/lib/lock/errors.js +27 -0
- package/lib/lock/index.js +12 -0
- package/lib/logger.js +1 -1
- package/lib/model/examples/test.js +225 -29
- package/lib/model/features/soft-delete.js +348 -0
- package/lib/model/features/version.js +156 -0
- package/lib/model/index.js +756 -0
- package/lib/mongodb/common/accessor-helpers.js +17 -3
- package/lib/mongodb/connect.js +68 -13
- package/lib/mongodb/index.js +153 -6
- package/lib/mongodb/management/collection-ops.js +4 -4
- package/lib/mongodb/management/index-ops.js +18 -18
- package/lib/mongodb/management/validation-ops.js +3 -3
- package/lib/mongodb/queries/aggregate.js +14 -5
- package/lib/mongodb/queries/chain.js +52 -45
- package/lib/mongodb/queries/count.js +16 -6
- package/lib/mongodb/queries/distinct.js +15 -6
- package/lib/mongodb/queries/find-and-count.js +22 -13
- package/lib/mongodb/queries/find-by-ids.js +5 -5
- package/lib/mongodb/queries/find-one-by-id.js +1 -1
- package/lib/mongodb/queries/find-one.js +12 -3
- package/lib/mongodb/queries/find-page.js +12 -0
- package/lib/mongodb/queries/find.js +15 -6
- package/lib/mongodb/queries/watch.js +11 -2
- package/lib/mongodb/writes/common/batch-retry.js +64 -0
- package/lib/mongodb/writes/delete-batch.js +322 -0
- package/lib/mongodb/writes/delete-many.js +20 -11
- package/lib/mongodb/writes/delete-one.js +18 -9
- package/lib/mongodb/writes/find-one-and-delete.js +19 -10
- package/lib/mongodb/writes/find-one-and-replace.js +36 -20
- package/lib/mongodb/writes/find-one-and-update.js +36 -20
- package/lib/mongodb/writes/increment-one.js +22 -7
- package/lib/mongodb/writes/index.js +17 -13
- package/lib/mongodb/writes/insert-batch.js +46 -37
- package/lib/mongodb/writes/insert-many.js +22 -13
- package/lib/mongodb/writes/insert-one.js +18 -9
- package/lib/mongodb/writes/replace-one.js +33 -17
- package/lib/mongodb/writes/result-handler.js +14 -14
- package/lib/mongodb/writes/update-batch.js +358 -0
- package/lib/mongodb/writes/update-many.js +34 -18
- package/lib/mongodb/writes/update-one.js +33 -17
- package/lib/mongodb/writes/upsert-one.js +25 -9
- package/lib/operators.js +1 -1
- package/lib/redis-cache-adapter.js +3 -3
- package/lib/slow-query-log/base-storage.js +69 -0
- package/lib/slow-query-log/batch-queue.js +96 -0
- package/lib/slow-query-log/config-manager.js +195 -0
- package/lib/slow-query-log/index.js +237 -0
- package/lib/slow-query-log/mongodb-storage.js +323 -0
- package/lib/slow-query-log/query-hash.js +38 -0
- package/lib/transaction/DistributedCacheLockManager.js +240 -5
- package/lib/transaction/Transaction.js +1 -1
- package/lib/utils/objectid-converter.js +566 -0
- 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(
|
|
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
|
-
|
|
66
|
-
[{ field:
|
|
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
|
-
|
|
74
|
-
[{ field:
|
|
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 !==
|
|
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
|
-
|
|
87
|
+
'documents 中的所有元素必须是对象类型',
|
|
87
88
|
invalidDocs.map((doc, idx) => ({
|
|
88
89
|
field: `documents[${idx}]`,
|
|
89
|
-
type:
|
|
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(
|
|
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:
|
|
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(
|
|
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 !==
|
|
57
|
+
if (!document || typeof document !== 'object' || Array.isArray(document)) {
|
|
57
58
|
throw createError(
|
|
58
59
|
ErrorCodes.DOCUMENT_REQUIRED,
|
|
59
|
-
|
|
60
|
-
[{ field:
|
|
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(
|
|
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:
|
|
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:
|
|
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(
|
|
7
|
-
const CacheFactory = require(
|
|
8
|
-
const { isInTransaction, getTransactionFromSession } = require(
|
|
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 !==
|
|
63
|
+
if (!filter || typeof filter !== 'object' || Array.isArray(filter)) {
|
|
63
64
|
throw createError(
|
|
64
65
|
ErrorCodes.INVALID_ARGUMENT,
|
|
65
|
-
|
|
66
|
-
[{ field:
|
|
66
|
+
'filter 必须是对象类型',
|
|
67
|
+
[{ field: 'filter', type: 'object.required', message: 'filter 是必需参数且必须是对象' }]
|
|
67
68
|
);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
if (!replacement || typeof replacement !==
|
|
71
|
+
if (!replacement || typeof replacement !== 'object' || Array.isArray(replacement)) {
|
|
71
72
|
throw createError(
|
|
72
73
|
ErrorCodes.INVALID_ARGUMENT,
|
|
73
|
-
|
|
74
|
-
[{ field:
|
|
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
|
-
|
|
84
|
-
[{ field:
|
|
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 =
|
|
105
|
+
const operation = 'replaceOne';
|
|
90
106
|
const ns = `${databaseName}.${collectionName}`;
|
|
91
107
|
|
|
92
108
|
try {
|
|
93
109
|
// 3. 执行替换操作
|
|
94
|
-
const result = await nativeCollection.replaceOne(
|
|
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:
|
|
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:
|
|
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(
|
|
48
|
-
const versionString = mongodb.version || require(
|
|
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(
|
|
77
|
+
logger.warn('[result-handler] ⚠️ 检测到 MongoDB 驱动版本过旧', {
|
|
78
78
|
detectedVersion: version,
|
|
79
|
-
supportedVersion:
|
|
80
|
-
message:
|
|
81
|
-
recommendation:
|
|
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(
|
|
87
|
+
logger.warn('[result-handler] ⚠️ 检测到 MongoDB 驱动版本未经测试', {
|
|
88
88
|
detectedVersion: version,
|
|
89
|
-
testedVersion:
|
|
90
|
-
message:
|
|
91
|
-
recommendation:
|
|
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(
|
|
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 ===
|
|
155
|
+
if (typeof result === 'object' && !result.lastErrorObject) {
|
|
156
156
|
if (logger && logger.warn) {
|
|
157
|
-
logger.warn(
|
|
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:
|
|
162
|
+
recommendation: '这可能表明 MongoDB 驱动返回了非预期的格式,请检查驱动版本'
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
165
|
|