monsqlize 1.0.1 → 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.
- package/CHANGELOG.md +91 -2471
- package/README.md +641 -1217
- package/index.d.ts +252 -15
- 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 +118 -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 +4 -4
- package/lib/mongodb/common/accessor-helpers.js +17 -3
- package/lib/mongodb/connect.js +68 -13
- package/lib/mongodb/index.js +136 -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/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 +16 -7
- package/lib/mongodb/writes/index.js +13 -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-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 +11 -5
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const { normalizeProjection, normalizeSort } = require('../../common/normalize');
|
|
7
7
|
const { FindChain } = require('./chain');
|
|
8
|
+
const { convertObjectIdStrings } = require('../../utils/objectid-converter');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* 创建 find 查询操作
|
|
@@ -51,12 +52,20 @@ function createFindOps(context) {
|
|
|
51
52
|
* @returns {Promise<Array>|ReadableStream|FindChain} 记录数组或可读流(当 stream: true 时);当 explain=true 时返回执行计划;默认返回 FindChain 实例支持链式调用
|
|
52
53
|
*/
|
|
53
54
|
find: (query = {}, options = {}) => {
|
|
55
|
+
// ✅ v1.3.0: 自动转换 ObjectId 字符串
|
|
56
|
+
const convertedQuery = convertObjectIdStrings(query, 'query', 0, new WeakSet(), {
|
|
57
|
+
logger: context.logger,
|
|
58
|
+
excludeFields: context.autoConvertConfig?.excludeFields,
|
|
59
|
+
customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
|
|
60
|
+
maxDepth: context.autoConvertConfig?.maxDepth
|
|
61
|
+
});
|
|
62
|
+
|
|
54
63
|
// 如果没有提供 options 或 options 为空对象,返回 FindChain 以支持完整的链式调用
|
|
55
64
|
const hasOptions = options && Object.keys(options).length > 0;
|
|
56
65
|
|
|
57
66
|
if (!hasOptions) {
|
|
58
67
|
// 返回 FindChain 实例,支持 .limit().skip().sort() 等链式调用
|
|
59
|
-
return new FindChain(context,
|
|
68
|
+
return new FindChain(context, convertedQuery, {});
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
// 如果提供了 options,执行原有逻辑(向后兼容)
|
|
@@ -81,13 +90,13 @@ function createFindOps(context) {
|
|
|
81
90
|
// 如果启用 explain,直接返回执行计划(不缓存)
|
|
82
91
|
if (explain) {
|
|
83
92
|
const verbosity = typeof explain === 'string' ? explain : 'queryPlanner';
|
|
84
|
-
const cursor = collection.find(
|
|
93
|
+
const cursor = collection.find(convertedQuery, driverOpts);
|
|
85
94
|
return cursor.explain(verbosity);
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
// 如果启用流式返回,直接返回 MongoDB 游标流
|
|
89
98
|
if (stream) {
|
|
90
|
-
const cursor = collection.find(
|
|
99
|
+
const cursor = collection.find(convertedQuery, driverOpts);
|
|
91
100
|
const readableStream = cursor.stream();
|
|
92
101
|
|
|
93
102
|
// 添加慢查询日志支持
|
|
@@ -127,13 +136,13 @@ function createFindOps(context) {
|
|
|
127
136
|
// 执行查询的 Promise
|
|
128
137
|
const resultPromise = run(
|
|
129
138
|
'find',
|
|
130
|
-
{ query, ...options },
|
|
131
|
-
async () => collection.find(
|
|
139
|
+
{ query: convertedQuery, ...options },
|
|
140
|
+
async () => collection.find(convertedQuery, driverOpts).toArray()
|
|
132
141
|
);
|
|
133
142
|
|
|
134
143
|
// 添加 explain 方法支持链式调用(与原生 MongoDB 一致)
|
|
135
144
|
resultPromise.explain = async (verbosity = 'queryPlanner') => {
|
|
136
|
-
const cursor = collection.find(
|
|
145
|
+
const cursor = collection.find(convertedQuery, driverOpts);
|
|
137
146
|
return cursor.explain(verbosity);
|
|
138
147
|
};
|
|
139
148
|
|
|
@@ -488,6 +488,15 @@ function createWatchOps(context) {
|
|
|
488
488
|
throw new Error('pipeline must be an array');
|
|
489
489
|
}
|
|
490
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
|
+
|
|
491
500
|
// 构建 MongoDB watch 选项
|
|
492
501
|
const watchOptions = {
|
|
493
502
|
fullDocument: options.fullDocument || 'updateLookup',
|
|
@@ -501,13 +510,13 @@ function createWatchOps(context) {
|
|
|
501
510
|
delete watchOptions.autoInvalidateCache;
|
|
502
511
|
|
|
503
512
|
// 创建 MongoDB ChangeStream
|
|
504
|
-
const changeStream = context.collection.watch(
|
|
513
|
+
const changeStream = context.collection.watch(convertedPipeline, watchOptions);
|
|
505
514
|
|
|
506
515
|
// 包装为 ChangeStreamWrapper
|
|
507
516
|
const wrapper = new ChangeStreamWrapper(
|
|
508
517
|
changeStream,
|
|
509
518
|
context.collection,
|
|
510
|
-
|
|
519
|
+
convertedPipeline,
|
|
511
520
|
options,
|
|
512
521
|
context
|
|
513
522
|
);
|
|
@@ -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
|
* 创建 deleteMany 操作
|
|
@@ -61,36 +62,44 @@ function createDeleteManyOps(context) {
|
|
|
61
62
|
const startTime = Date.now();
|
|
62
63
|
|
|
63
64
|
// 1. 参数验证
|
|
64
|
-
if (!filter || typeof filter !==
|
|
65
|
+
if (!filter || typeof filter !== 'object' || Array.isArray(filter)) {
|
|
65
66
|
throw createError(
|
|
66
67
|
ErrorCodes.INVALID_ARGUMENT,
|
|
67
|
-
|
|
68
|
-
[{ field:
|
|
68
|
+
'filter 必须是对象类型',
|
|
69
|
+
[{ field: 'filter', type: 'object.required', message: 'filter 是必需参数且必须是对象' }]
|
|
69
70
|
);
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
// ✅ v1.3.0: 自动转换 ObjectId 字符串
|
|
74
|
+
const convertedFilter = convertObjectIdStrings(filter, 'filter', 0, new WeakSet(), {
|
|
75
|
+
logger: context.logger,
|
|
76
|
+
excludeFields: context.autoConvertConfig?.excludeFields,
|
|
77
|
+
customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
|
|
78
|
+
maxDepth: context.autoConvertConfig?.maxDepth
|
|
79
|
+
});
|
|
80
|
+
|
|
72
81
|
// 2. 警告:空 filter 会删除所有文档
|
|
73
|
-
if (Object.keys(
|
|
74
|
-
logger.warn(
|
|
82
|
+
if (Object.keys(convertedFilter).length === 0) {
|
|
83
|
+
logger.warn('[deleteMany] 警告: 空 filter 将删除集合中的所有文档', {
|
|
75
84
|
ns: `${databaseName}.${collectionName}`,
|
|
76
85
|
comment: options.comment
|
|
77
86
|
});
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
// 3. 构建操作上下文
|
|
81
|
-
const operation =
|
|
90
|
+
const operation = 'deleteMany';
|
|
82
91
|
const ns = `${databaseName}.${collectionName}`;
|
|
83
92
|
|
|
84
93
|
try {
|
|
85
94
|
// 4. 执行删除操作
|
|
86
|
-
const result = await nativeCollection.deleteMany(
|
|
95
|
+
const result = await nativeCollection.deleteMany(convertedFilter, options);
|
|
87
96
|
|
|
88
97
|
// 5. 自动失效缓存(如果有文档被删除)
|
|
89
98
|
if (cache && result.deletedCount > 0) {
|
|
90
99
|
try {
|
|
91
100
|
const ns = {
|
|
92
101
|
iid: instanceId,
|
|
93
|
-
type:
|
|
102
|
+
type: 'mongodb',
|
|
94
103
|
db: databaseName,
|
|
95
104
|
collection: collectionName
|
|
96
105
|
};
|
|
@@ -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
|
* 创建 deleteOne 操作
|
|
@@ -61,28 +62,36 @@ function createDeleteOneOps(context) {
|
|
|
61
62
|
const startTime = Date.now();
|
|
62
63
|
|
|
63
64
|
// 1. 参数验证
|
|
64
|
-
if (!filter || typeof filter !==
|
|
65
|
+
if (!filter || typeof filter !== 'object' || Array.isArray(filter)) {
|
|
65
66
|
throw createError(
|
|
66
67
|
ErrorCodes.INVALID_ARGUMENT,
|
|
67
|
-
|
|
68
|
-
[{ field:
|
|
68
|
+
'filter 必须是对象类型',
|
|
69
|
+
[{ field: 'filter', type: 'object.required', message: 'filter 是必需参数且必须是对象' }]
|
|
69
70
|
);
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
// ✅ v1.3.0: 自动转换 ObjectId 字符串
|
|
74
|
+
const convertedFilter = convertObjectIdStrings(filter, 'filter', 0, new WeakSet(), {
|
|
75
|
+
logger: context.logger,
|
|
76
|
+
excludeFields: context.autoConvertConfig?.excludeFields,
|
|
77
|
+
customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
|
|
78
|
+
maxDepth: context.autoConvertConfig?.maxDepth
|
|
79
|
+
});
|
|
80
|
+
|
|
72
81
|
// 2. 构建操作上下文
|
|
73
|
-
const operation =
|
|
82
|
+
const operation = 'deleteOne';
|
|
74
83
|
const ns = `${databaseName}.${collectionName}`;
|
|
75
84
|
|
|
76
85
|
try {
|
|
77
86
|
// 3. 执行删除操作
|
|
78
|
-
const result = await nativeCollection.deleteOne(
|
|
87
|
+
const result = await nativeCollection.deleteOne(convertedFilter, options);
|
|
79
88
|
|
|
80
89
|
// 4. 自动失效缓存(如果有文档被删除)
|
|
81
90
|
if (cache && result.deletedCount > 0) {
|
|
82
91
|
try {
|
|
83
92
|
const ns = {
|
|
84
93
|
iid: instanceId,
|
|
85
|
-
type:
|
|
94
|
+
type: 'mongodb',
|
|
86
95
|
db: databaseName,
|
|
87
96
|
collection: collectionName
|
|
88
97
|
};
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* 原子地查找并删除单个文档
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { createError, ErrorCodes } = require(
|
|
7
|
-
const CacheFactory = require(
|
|
8
|
-
const { handleFindOneAndResult, wasDocumentModified } = require(
|
|
9
|
-
const { isInTransaction, getTransactionFromSession } = require(
|
|
6
|
+
const { createError, ErrorCodes } = require('../../errors');
|
|
7
|
+
const CacheFactory = require('../../cache');
|
|
8
|
+
const { handleFindOneAndResult, wasDocumentModified } = require('./result-handler');
|
|
9
|
+
const { isInTransaction, getTransactionFromSession } = require('../common/transaction-aware');
|
|
10
|
+
const { convertObjectIdStrings } = require('../../utils/objectid-converter');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 创建 findOneAndDelete 操作
|
|
@@ -78,23 +79,31 @@ function createFindOneAndDeleteOps(context) {
|
|
|
78
79
|
const startTime = Date.now();
|
|
79
80
|
|
|
80
81
|
// 1. 参数验证
|
|
81
|
-
if (!filter || typeof filter !==
|
|
82
|
+
if (!filter || typeof filter !== 'object' || Array.isArray(filter)) {
|
|
82
83
|
throw createError(
|
|
83
84
|
ErrorCodes.INVALID_ARGUMENT,
|
|
84
|
-
|
|
85
|
-
[{ field:
|
|
85
|
+
'filter 必须是对象类型',
|
|
86
|
+
[{ field: 'filter', type: 'object.required', message: 'filter 是必需参数且必须是对象' }]
|
|
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
|
+
|
|
89
98
|
// 2. 构建操作上下文
|
|
90
|
-
const operation =
|
|
99
|
+
const operation = 'findOneAndDelete';
|
|
91
100
|
const ns = `${databaseName}.${collectionName}`;
|
|
92
101
|
|
|
93
102
|
try {
|
|
94
103
|
// 3. 执行查找并删除操作
|
|
95
104
|
// MongoDB 驱动 6.x: 默认返回文档,需要 includeResultMetadata=true 获取完整元数据
|
|
96
105
|
const driverOptions = { ...options, includeResultMetadata: true };
|
|
97
|
-
const result = await nativeCollection.findOneAndDelete(
|
|
106
|
+
const result = await nativeCollection.findOneAndDelete(convertedFilter, driverOptions);
|
|
98
107
|
|
|
99
108
|
// 4. 自动失效缓存(如果有文档被删除)
|
|
100
109
|
// 使用安全的修改判断函数
|
|
@@ -103,7 +112,7 @@ function createFindOneAndDeleteOps(context) {
|
|
|
103
112
|
try {
|
|
104
113
|
const ns = {
|
|
105
114
|
iid: instanceId,
|
|
106
|
-
type:
|
|
115
|
+
type: 'mongodb',
|
|
107
116
|
db: databaseName,
|
|
108
117
|
collection: collectionName
|
|
109
118
|
};
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* 原子地查找并替换单个文档
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { createError, ErrorCodes } = require(
|
|
7
|
-
const CacheFactory = require(
|
|
8
|
-
const { handleFindOneAndResult, wasDocumentModified } = require(
|
|
9
|
-
const { isInTransaction, getTransactionFromSession } = require(
|
|
6
|
+
const { createError, ErrorCodes } = require('../../errors');
|
|
7
|
+
const CacheFactory = require('../../cache');
|
|
8
|
+
const { handleFindOneAndResult, wasDocumentModified } = require('./result-handler');
|
|
9
|
+
const { isInTransaction, getTransactionFromSession } = require('../common/transaction-aware');
|
|
10
|
+
const { convertObjectIdStrings } = require('../../utils/objectid-converter');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 创建 findOneAndReplace 操作
|
|
@@ -75,41 +76,56 @@ function createFindOneAndReplaceOps(context) {
|
|
|
75
76
|
const startTime = Date.now();
|
|
76
77
|
|
|
77
78
|
// 1. 参数验证
|
|
78
|
-
if (!filter || typeof filter !==
|
|
79
|
+
if (!filter || typeof filter !== 'object' || Array.isArray(filter)) {
|
|
79
80
|
throw createError(
|
|
80
81
|
ErrorCodes.INVALID_ARGUMENT,
|
|
81
|
-
|
|
82
|
-
[{ field:
|
|
82
|
+
'filter 必须是对象类型',
|
|
83
|
+
[{ field: 'filter', type: 'object.required', message: 'filter 是必需参数且必须是对象' }]
|
|
83
84
|
);
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
if (!replacement || typeof replacement !==
|
|
87
|
+
if (!replacement || typeof replacement !== 'object' || Array.isArray(replacement)) {
|
|
87
88
|
throw createError(
|
|
88
89
|
ErrorCodes.INVALID_ARGUMENT,
|
|
89
|
-
|
|
90
|
-
[{ field:
|
|
90
|
+
'replacement 必须是对象类型',
|
|
91
|
+
[{ field: 'replacement', type: 'object.required', message: 'replacement 是必需参数且必须是对象' }]
|
|
91
92
|
);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
// 验证 replacement 不包含更新操作符
|
|
95
96
|
const replacementKeys = Object.keys(replacement);
|
|
96
|
-
if (replacementKeys.some(key => key.startsWith(
|
|
97
|
+
if (replacementKeys.some(key => key.startsWith('$'))) {
|
|
97
98
|
throw createError(
|
|
98
99
|
ErrorCodes.INVALID_ARGUMENT,
|
|
99
|
-
|
|
100
|
-
[{ field:
|
|
100
|
+
'replacement 不能包含更新操作符(如 $set, $inc 等)',
|
|
101
|
+
[{ field: 'replacement', type: 'object.invalid', message: 'findOneAndReplace 用于完整替换文档,请使用 findOneAndUpdate 进行部分更新' }]
|
|
101
102
|
);
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
// ✅ v1.3.0: 自动转换 ObjectId 字符串
|
|
106
|
+
const convertedFilter = convertObjectIdStrings(filter, 'filter', 0, new WeakSet(), {
|
|
107
|
+
logger: context.logger,
|
|
108
|
+
excludeFields: context.autoConvertConfig?.excludeFields,
|
|
109
|
+
customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
|
|
110
|
+
maxDepth: context.autoConvertConfig?.maxDepth
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const convertedReplacement = convertObjectIdStrings(replacement, 'document', 0, new WeakSet(), {
|
|
114
|
+
logger: context.logger,
|
|
115
|
+
excludeFields: context.autoConvertConfig?.excludeFields,
|
|
116
|
+
customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
|
|
117
|
+
maxDepth: context.autoConvertConfig?.maxDepth
|
|
118
|
+
});
|
|
119
|
+
|
|
104
120
|
// 2. 构建操作上下文
|
|
105
|
-
const operation =
|
|
121
|
+
const operation = 'findOneAndReplace';
|
|
106
122
|
const ns = `${databaseName}.${collectionName}`;
|
|
107
123
|
|
|
108
124
|
try {
|
|
109
125
|
// 3. 执行查找并替换操作
|
|
110
126
|
// MongoDB 驱动 6.x: 默认返回文档,需要 includeResultMetadata=true 获取完整元数据
|
|
111
127
|
const driverOptions = { ...options, includeResultMetadata: true };
|
|
112
|
-
const result = await nativeCollection.findOneAndReplace(
|
|
128
|
+
const result = await nativeCollection.findOneAndReplace(convertedFilter, convertedReplacement, driverOptions);
|
|
113
129
|
|
|
114
130
|
// 4. 自动失效缓存(如果有文档被修改)
|
|
115
131
|
// 使用安全的修改判断函数
|
|
@@ -118,7 +134,7 @@ function createFindOneAndReplaceOps(context) {
|
|
|
118
134
|
try {
|
|
119
135
|
const ns = {
|
|
120
136
|
iid: instanceId,
|
|
121
|
-
type:
|
|
137
|
+
type: 'mongodb',
|
|
122
138
|
db: databaseName,
|
|
123
139
|
collection: collectionName
|
|
124
140
|
};
|
|
@@ -166,7 +182,7 @@ function createFindOneAndReplaceOps(context) {
|
|
|
166
182
|
replacementKeys: Object.keys(replacement),
|
|
167
183
|
found: result && result.value !== null,
|
|
168
184
|
upserted: result?.lastErrorObject?.upserted ? true : false,
|
|
169
|
-
returnDocument: options.returnDocument ||
|
|
185
|
+
returnDocument: options.returnDocument || 'before',
|
|
170
186
|
comment: options.comment
|
|
171
187
|
});
|
|
172
188
|
} else {
|
|
@@ -175,7 +191,7 @@ function createFindOneAndReplaceOps(context) {
|
|
|
175
191
|
duration,
|
|
176
192
|
found: result && result.value !== null,
|
|
177
193
|
upserted: result?.lastErrorObject?.upserted ? true : false,
|
|
178
|
-
returnDocument: options.returnDocument ||
|
|
194
|
+
returnDocument: options.returnDocument || 'before'
|
|
179
195
|
});
|
|
180
196
|
}
|
|
181
197
|
|
|
@@ -199,8 +215,8 @@ function createFindOneAndReplaceOps(context) {
|
|
|
199
215
|
if (error.code === 11000) {
|
|
200
216
|
throw createError(
|
|
201
217
|
ErrorCodes.DUPLICATE_KEY,
|
|
202
|
-
|
|
203
|
-
[{ field:
|
|
218
|
+
'查找并替换失败:违反唯一性约束',
|
|
219
|
+
[{ field: '_id', message: error.message }],
|
|
204
220
|
error
|
|
205
221
|
);
|
|
206
222
|
}
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* 原子地查找并更新单个文档
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { createError, ErrorCodes } = require(
|
|
7
|
-
const CacheFactory = require(
|
|
8
|
-
const { handleFindOneAndResult, wasDocumentModified } = require(
|
|
9
|
-
const { isInTransaction, getTransactionFromSession } = require(
|
|
6
|
+
const { createError, ErrorCodes } = require('../../errors');
|
|
7
|
+
const CacheFactory = require('../../cache');
|
|
8
|
+
const { handleFindOneAndResult, wasDocumentModified } = require('./result-handler');
|
|
9
|
+
const { isInTransaction, getTransactionFromSession } = require('../common/transaction-aware');
|
|
10
|
+
const { convertObjectIdStrings, convertUpdateDocument } = require('../../utils/objectid-converter');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 创建 findOneAndUpdate 操作
|
|
@@ -76,41 +77,56 @@ function createFindOneAndUpdateOps(context) {
|
|
|
76
77
|
const startTime = Date.now();
|
|
77
78
|
|
|
78
79
|
// 1. 参数验证
|
|
79
|
-
if (!filter || typeof filter !==
|
|
80
|
+
if (!filter || typeof filter !== 'object' || Array.isArray(filter)) {
|
|
80
81
|
throw createError(
|
|
81
82
|
ErrorCodes.INVALID_ARGUMENT,
|
|
82
|
-
|
|
83
|
-
[{ field:
|
|
83
|
+
'filter 必须是对象类型',
|
|
84
|
+
[{ field: 'filter', type: 'object.required', message: 'filter 是必需参数且必须是对象' }]
|
|
84
85
|
);
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
if (!update || typeof update !==
|
|
88
|
+
if (!update || typeof update !== 'object' || Array.isArray(update)) {
|
|
88
89
|
throw createError(
|
|
89
90
|
ErrorCodes.INVALID_ARGUMENT,
|
|
90
|
-
|
|
91
|
-
[{ field:
|
|
91
|
+
'update 必须是对象类型',
|
|
92
|
+
[{ field: 'update', type: 'object.required', message: 'update 是必需参数且必须是对象' }]
|
|
92
93
|
);
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
// 验证 update 包含更新操作符
|
|
96
97
|
const updateKeys = Object.keys(update);
|
|
97
|
-
if (updateKeys.length > 0 && !updateKeys.some(key => key.startsWith(
|
|
98
|
+
if (updateKeys.length > 0 && !updateKeys.some(key => key.startsWith('$'))) {
|
|
98
99
|
throw createError(
|
|
99
100
|
ErrorCodes.INVALID_ARGUMENT,
|
|
100
|
-
|
|
101
|
-
[{ field:
|
|
101
|
+
'update 必须使用更新操作符(如 $set, $inc 等)',
|
|
102
|
+
[{ field: 'update', type: 'object.invalid', message: '请使用 $set, $inc, $push 等更新操作符,或使用 findOneAndReplace 进行整体替换' }]
|
|
102
103
|
);
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
// ✅ v1.3.0: 自动转换 ObjectId 字符串
|
|
107
|
+
const convertedFilter = convertObjectIdStrings(filter, 'filter', 0, new WeakSet(), {
|
|
108
|
+
logger: context.logger,
|
|
109
|
+
excludeFields: context.autoConvertConfig?.excludeFields,
|
|
110
|
+
customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
|
|
111
|
+
maxDepth: context.autoConvertConfig?.maxDepth
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const convertedUpdate = convertUpdateDocument(update, {
|
|
115
|
+
logger: context.logger,
|
|
116
|
+
excludeFields: context.autoConvertConfig?.excludeFields,
|
|
117
|
+
customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
|
|
118
|
+
maxDepth: context.autoConvertConfig?.maxDepth
|
|
119
|
+
});
|
|
120
|
+
|
|
105
121
|
// 2. 构建操作上下文
|
|
106
|
-
const operation =
|
|
122
|
+
const operation = 'findOneAndUpdate';
|
|
107
123
|
const ns = `${databaseName}.${collectionName}`;
|
|
108
124
|
|
|
109
125
|
try {
|
|
110
126
|
// 3. 执行查找并更新操作
|
|
111
127
|
// MongoDB 驱动 6.x: 默认返回文档,需要 includeResultMetadata=true 获取完整元数据
|
|
112
128
|
const driverOptions = { ...options, includeResultMetadata: true };
|
|
113
|
-
const result = await nativeCollection.findOneAndUpdate(
|
|
129
|
+
const result = await nativeCollection.findOneAndUpdate(convertedFilter, convertedUpdate, driverOptions);
|
|
114
130
|
|
|
115
131
|
// 4. 自动失效缓存(如果有文档被修改)
|
|
116
132
|
// 使用安全的修改判断函数
|
|
@@ -119,7 +135,7 @@ function createFindOneAndUpdateOps(context) {
|
|
|
119
135
|
try {
|
|
120
136
|
const ns = {
|
|
121
137
|
iid: instanceId,
|
|
122
|
-
type:
|
|
138
|
+
type: 'mongodb',
|
|
123
139
|
db: databaseName,
|
|
124
140
|
collection: collectionName
|
|
125
141
|
};
|
|
@@ -167,7 +183,7 @@ function createFindOneAndUpdateOps(context) {
|
|
|
167
183
|
updateKeys: Object.keys(update),
|
|
168
184
|
found: result && result.value !== null,
|
|
169
185
|
upserted: result?.lastErrorObject?.upserted ? true : false,
|
|
170
|
-
returnDocument: options.returnDocument ||
|
|
186
|
+
returnDocument: options.returnDocument || 'before',
|
|
171
187
|
comment: options.comment
|
|
172
188
|
});
|
|
173
189
|
} else {
|
|
@@ -176,7 +192,7 @@ function createFindOneAndUpdateOps(context) {
|
|
|
176
192
|
duration,
|
|
177
193
|
found: result && result.value !== null,
|
|
178
194
|
upserted: result?.lastErrorObject?.upserted ? true : false,
|
|
179
|
-
returnDocument: options.returnDocument ||
|
|
195
|
+
returnDocument: options.returnDocument || 'before'
|
|
180
196
|
});
|
|
181
197
|
}
|
|
182
198
|
|
|
@@ -200,8 +216,8 @@ function createFindOneAndUpdateOps(context) {
|
|
|
200
216
|
if (error.code === 11000) {
|
|
201
217
|
throw createError(
|
|
202
218
|
ErrorCodes.DUPLICATE_KEY,
|
|
203
|
-
|
|
204
|
-
[{ field:
|
|
219
|
+
'查找并更新失败:违反唯一性约束',
|
|
220
|
+
[{ field: '_id', message: error.message }],
|
|
205
221
|
error
|
|
206
222
|
);
|
|
207
223
|
}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
const { ObjectId } = require('mongodb');
|
|
7
7
|
const { createError, ErrorCodes } = require('../../errors');
|
|
8
|
-
const { isInTransaction, getTransactionFromSession } = require(
|
|
9
|
-
const { handleFindOneAndResult, wasDocumentModified } = require(
|
|
8
|
+
const { isInTransaction, getTransactionFromSession } = require('../common/transaction-aware');
|
|
9
|
+
const { handleFindOneAndResult, wasDocumentModified } = require('./result-handler');
|
|
10
|
+
const { convertObjectIdStrings } = require('../../utils/objectid-converter');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 创建 incrementOne 操作
|
|
@@ -91,6 +92,14 @@ function createIncrementOneOps(context) {
|
|
|
91
92
|
);
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
// ✅ v1.3.0: 自动转换 ObjectId 字符串
|
|
96
|
+
const convertedFilter = convertObjectIdStrings(filter, 'filter', 0, new WeakSet(), {
|
|
97
|
+
logger: context.logger,
|
|
98
|
+
excludeFields: context.autoConvertConfig?.excludeFields,
|
|
99
|
+
customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
|
|
100
|
+
maxDepth: context.autoConvertConfig?.maxDepth
|
|
101
|
+
});
|
|
102
|
+
|
|
94
103
|
// 2. 构建 $inc 更新对象
|
|
95
104
|
let incUpdate;
|
|
96
105
|
|
|
@@ -138,7 +147,7 @@ function createIncrementOneOps(context) {
|
|
|
138
147
|
const projection = actualOptions.projection;
|
|
139
148
|
|
|
140
149
|
const updateOptions = {
|
|
141
|
-
returnDocument
|
|
150
|
+
returnDocument,
|
|
142
151
|
includeResultMetadata: true,
|
|
143
152
|
maxTimeMS
|
|
144
153
|
};
|
|
@@ -148,7 +157,7 @@ function createIncrementOneOps(context) {
|
|
|
148
157
|
// 4. 执行 findOneAndUpdate 操作
|
|
149
158
|
let result;
|
|
150
159
|
try {
|
|
151
|
-
result = await collection.findOneAndUpdate(
|
|
160
|
+
result = await collection.findOneAndUpdate(convertedFilter, incUpdate, updateOptions);
|
|
152
161
|
} catch (error) {
|
|
153
162
|
throw error;
|
|
154
163
|
}
|
|
@@ -201,13 +210,13 @@ function createIncrementOneOps(context) {
|
|
|
201
210
|
operation: 'incrementOne',
|
|
202
211
|
durationMs: duration,
|
|
203
212
|
iid: instanceId,
|
|
204
|
-
type
|
|
213
|
+
type,
|
|
205
214
|
db: effectiveDbName,
|
|
206
215
|
collection: collection.collectionName,
|
|
207
216
|
found: result.value !== null,
|
|
208
217
|
filter: mongoSlowLogShaper?.sanitize ? mongoSlowLogShaper.sanitize(filter) : filter,
|
|
209
218
|
update: mongoSlowLogShaper?.sanitize ? mongoSlowLogShaper.sanitize(incUpdate) : incUpdate,
|
|
210
|
-
comment
|
|
219
|
+
comment
|
|
211
220
|
};
|
|
212
221
|
logger?.warn?.('🐌 Slow query: incrementOne', meta);
|
|
213
222
|
emit?.('slow-query', meta);
|
|
@@ -219,7 +228,7 @@ function createIncrementOneOps(context) {
|
|
|
219
228
|
// 7. 日志记录
|
|
220
229
|
logger?.debug?.('[incrementOne] 操作完成', {
|
|
221
230
|
ns: `${effectiveDbName}.${collection.collectionName}`,
|
|
222
|
-
duration
|
|
231
|
+
duration,
|
|
223
232
|
found: result && result.value !== null,
|
|
224
233
|
modified: wasDocumentModified(result)
|
|
225
234
|
});
|
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
* 统一导出所有写操作的工厂函数
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { createInsertOneOps } = require(
|
|
7
|
-
const { createInsertManyOps } = require(
|
|
8
|
-
const { createInsertBatchOps } = require(
|
|
9
|
-
const { createUpdateOneOps } = require(
|
|
10
|
-
const { createUpdateManyOps } = require(
|
|
11
|
-
const { createReplaceOneOps } = require(
|
|
12
|
-
const { createUpsertOneOps } = require(
|
|
13
|
-
const { createIncrementOneOps } = require(
|
|
14
|
-
const { createFindOneAndUpdateOps } = require(
|
|
15
|
-
const { createFindOneAndReplaceOps } = require(
|
|
16
|
-
const { createDeleteOneOps } = require(
|
|
17
|
-
const { createDeleteManyOps } = require(
|
|
18
|
-
const { createFindOneAndDeleteOps } = require(
|
|
6
|
+
const { createInsertOneOps } = require('./insert-one');
|
|
7
|
+
const { createInsertManyOps } = require('./insert-many');
|
|
8
|
+
const { createInsertBatchOps } = require('./insert-batch');
|
|
9
|
+
const { createUpdateOneOps } = require('./update-one');
|
|
10
|
+
const { createUpdateManyOps } = require('./update-many');
|
|
11
|
+
const { createReplaceOneOps } = require('./replace-one');
|
|
12
|
+
const { createUpsertOneOps } = require('./upsert-one'); // upsertOne 便利方法
|
|
13
|
+
const { createIncrementOneOps } = require('./increment-one'); // 新增:incrementOne 便利方法
|
|
14
|
+
const { createFindOneAndUpdateOps } = require('./find-one-and-update');
|
|
15
|
+
const { createFindOneAndReplaceOps } = require('./find-one-and-replace');
|
|
16
|
+
const { createDeleteOneOps } = require('./delete-one');
|
|
17
|
+
const { createDeleteManyOps } = require('./delete-many');
|
|
18
|
+
const { createFindOneAndDeleteOps } = require('./find-one-and-delete');
|
|
19
19
|
|
|
20
20
|
module.exports = {
|
|
21
21
|
// Insert 操作
|