monsqlize 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2474 -0
- package/LICENSE +21 -0
- package/README.md +1368 -0
- package/index.d.ts +1052 -0
- package/lib/cache.js +491 -0
- package/lib/common/cursor.js +58 -0
- package/lib/common/docs-urls.js +72 -0
- package/lib/common/index-options.js +222 -0
- package/lib/common/log.js +60 -0
- package/lib/common/namespace.js +21 -0
- package/lib/common/normalize.js +33 -0
- package/lib/common/page-result.js +42 -0
- package/lib/common/runner.js +56 -0
- package/lib/common/server-features.js +231 -0
- package/lib/common/shape-builders.js +26 -0
- package/lib/common/validation.js +49 -0
- package/lib/connect.js +76 -0
- package/lib/constants.js +54 -0
- package/lib/count-queue.js +187 -0
- package/lib/distributed-cache-invalidator.js +259 -0
- package/lib/errors.js +157 -0
- package/lib/index.js +352 -0
- package/lib/logger.js +224 -0
- package/lib/model/examples/test.js +114 -0
- package/lib/mongodb/common/accessor-helpers.js +44 -0
- package/lib/mongodb/common/agg-pipeline.js +32 -0
- package/lib/mongodb/common/iid.js +27 -0
- package/lib/mongodb/common/lexicographic-expr.js +52 -0
- package/lib/mongodb/common/shape.js +31 -0
- package/lib/mongodb/common/sort.js +38 -0
- package/lib/mongodb/common/transaction-aware.js +24 -0
- package/lib/mongodb/connect.js +178 -0
- package/lib/mongodb/index.js +458 -0
- package/lib/mongodb/management/admin-ops.js +199 -0
- package/lib/mongodb/management/bookmark-ops.js +166 -0
- package/lib/mongodb/management/cache-ops.js +49 -0
- package/lib/mongodb/management/collection-ops.js +386 -0
- package/lib/mongodb/management/database-ops.js +201 -0
- package/lib/mongodb/management/index-ops.js +474 -0
- package/lib/mongodb/management/index.js +16 -0
- package/lib/mongodb/management/namespace.js +30 -0
- package/lib/mongodb/management/validation-ops.js +267 -0
- package/lib/mongodb/queries/aggregate.js +133 -0
- package/lib/mongodb/queries/chain.js +623 -0
- package/lib/mongodb/queries/count.js +88 -0
- package/lib/mongodb/queries/distinct.js +68 -0
- package/lib/mongodb/queries/find-and-count.js +183 -0
- package/lib/mongodb/queries/find-by-ids.js +235 -0
- package/lib/mongodb/queries/find-one-by-id.js +170 -0
- package/lib/mongodb/queries/find-one.js +61 -0
- package/lib/mongodb/queries/find-page.js +565 -0
- package/lib/mongodb/queries/find.js +161 -0
- package/lib/mongodb/queries/index.js +49 -0
- package/lib/mongodb/writes/delete-many.js +181 -0
- package/lib/mongodb/writes/delete-one.js +173 -0
- package/lib/mongodb/writes/find-one-and-delete.js +193 -0
- package/lib/mongodb/writes/find-one-and-replace.js +222 -0
- package/lib/mongodb/writes/find-one-and-update.js +223 -0
- package/lib/mongodb/writes/increment-one.js +243 -0
- package/lib/mongodb/writes/index.js +41 -0
- package/lib/mongodb/writes/insert-batch.js +498 -0
- package/lib/mongodb/writes/insert-many.js +218 -0
- package/lib/mongodb/writes/insert-one.js +171 -0
- package/lib/mongodb/writes/replace-one.js +199 -0
- package/lib/mongodb/writes/result-handler.js +236 -0
- package/lib/mongodb/writes/update-many.js +205 -0
- package/lib/mongodb/writes/update-one.js +207 -0
- package/lib/mongodb/writes/upsert-one.js +190 -0
- package/lib/multi-level-cache.js +189 -0
- package/lib/operators.js +330 -0
- package/lib/redis-cache-adapter.js +237 -0
- package/lib/transaction/CacheLockManager.js +161 -0
- package/lib/transaction/DistributedCacheLockManager.js +239 -0
- package/lib/transaction/Transaction.js +314 -0
- package/lib/transaction/TransactionManager.js +266 -0
- package/lib/transaction/index.js +10 -0
- package/package.json +111 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 集合验证相关操作方法工厂函数
|
|
3
|
+
* 提供 Schema 验证规则管理
|
|
4
|
+
* @module mongodb/management/validation-ops
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { createValidationError } = require('../../errors');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 创建验证操作方法
|
|
11
|
+
* @param {Object} context - 上下文对象
|
|
12
|
+
* @param {Object} context.db - MongoDB 数据库实例
|
|
13
|
+
* @param {Object} context.collection - MongoDB 集合实例
|
|
14
|
+
* @param {Object} context.logger - 日志记录器
|
|
15
|
+
* @returns {Object} 验证操作方法集合
|
|
16
|
+
*/
|
|
17
|
+
function createValidationOps(context) {
|
|
18
|
+
const { db, collection, logger } = context;
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
/**
|
|
22
|
+
* 设置集合的验证规则
|
|
23
|
+
* @param {Object} validator - 验证规则(JSON Schema 或查询表达式)
|
|
24
|
+
* @param {Object} [options] - 选项
|
|
25
|
+
* @param {string} [options.validationLevel] - 验证级别
|
|
26
|
+
* @param {string} [options.validationAction] - 验证失败时的行为
|
|
27
|
+
* @returns {Promise<Object>} 设置结果
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // 使用 JSON Schema 验证
|
|
31
|
+
* await collection.setValidator({
|
|
32
|
+
* $jsonSchema: {
|
|
33
|
+
* bsonType: 'object',
|
|
34
|
+
* required: ['name', 'email'],
|
|
35
|
+
* properties: {
|
|
36
|
+
* name: {
|
|
37
|
+
* bsonType: 'string',
|
|
38
|
+
* description: 'must be a string and is required'
|
|
39
|
+
* },
|
|
40
|
+
* email: {
|
|
41
|
+
* bsonType: 'string',
|
|
42
|
+
* pattern: '^.+@.+$',
|
|
43
|
+
* description: 'must be a valid email'
|
|
44
|
+
* },
|
|
45
|
+
* age: {
|
|
46
|
+
* bsonType: 'int',
|
|
47
|
+
* minimum: 0,
|
|
48
|
+
* maximum: 120,
|
|
49
|
+
* description: 'must be an integer between 0 and 120'
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* }
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // 使用查询表达式验证
|
|
56
|
+
* await collection.setValidator({
|
|
57
|
+
* $and: [
|
|
58
|
+
* { name: { $type: 'string' } },
|
|
59
|
+
* { email: { $regex: /@/ } }
|
|
60
|
+
* ]
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* // 同时设置验证级别和行为
|
|
64
|
+
* await collection.setValidator({
|
|
65
|
+
* $jsonSchema: { /* ... * / }
|
|
66
|
+
* }, {
|
|
67
|
+
* validationLevel: 'moderate',
|
|
68
|
+
* validationAction: 'warn'
|
|
69
|
+
* });
|
|
70
|
+
*/
|
|
71
|
+
async setValidator(validator, options = {}) {
|
|
72
|
+
try {
|
|
73
|
+
if (!validator || typeof validator !== 'object') {
|
|
74
|
+
throw createValidationError(
|
|
75
|
+
'Validator must be an object',
|
|
76
|
+
'INVALID_VALIDATOR'
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const command = {
|
|
81
|
+
collMod: collection.collectionName,
|
|
82
|
+
validator: validator
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (options.validationLevel) {
|
|
86
|
+
command.validationLevel = options.validationLevel;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (options.validationAction) {
|
|
90
|
+
command.validationAction = options.validationAction;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const result = await db.command(command);
|
|
94
|
+
|
|
95
|
+
logger.info('Validator set', {
|
|
96
|
+
collection: collection.collectionName,
|
|
97
|
+
validationLevel: options.validationLevel,
|
|
98
|
+
validationAction: options.validationAction
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
ok: result.ok,
|
|
103
|
+
collection: collection.collectionName,
|
|
104
|
+
validator: 'set'
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logger.error('setValidator failed', { error: error.message });
|
|
108
|
+
throw createValidationError(
|
|
109
|
+
`Failed to set validator: ${error.message}`,
|
|
110
|
+
'SET_VALIDATOR_ERROR'
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 设置验证级别
|
|
117
|
+
*
|
|
118
|
+
* 验证级别:
|
|
119
|
+
* - 'off': 禁用验证
|
|
120
|
+
* - 'strict': 对所有插入和更新进行验证(默认)
|
|
121
|
+
* - 'moderate': 只验证有效文档的更新,不验证现有无效文档
|
|
122
|
+
*
|
|
123
|
+
* @param {string} level - 验证级别('off'/'strict'/'moderate')
|
|
124
|
+
* @returns {Promise<Object>} 设置结果
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* // 严格验证(所有文档)
|
|
128
|
+
* await collection.setValidationLevel('strict');
|
|
129
|
+
*
|
|
130
|
+
* // 适度验证(只验证有效文档)
|
|
131
|
+
* await collection.setValidationLevel('moderate');
|
|
132
|
+
*
|
|
133
|
+
* // 禁用验证
|
|
134
|
+
* await collection.setValidationLevel('off');
|
|
135
|
+
*/
|
|
136
|
+
async setValidationLevel(level) {
|
|
137
|
+
try {
|
|
138
|
+
const validLevels = ['off', 'strict', 'moderate'];
|
|
139
|
+
if (!validLevels.includes(level)) {
|
|
140
|
+
throw createValidationError(
|
|
141
|
+
`Invalid validation level. Must be one of: ${validLevels.join(', ')}`,
|
|
142
|
+
'INVALID_VALIDATION_LEVEL'
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const result = await db.command({
|
|
147
|
+
collMod: collection.collectionName,
|
|
148
|
+
validationLevel: level
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
logger.info('Validation level set', {
|
|
152
|
+
collection: collection.collectionName,
|
|
153
|
+
level: level
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
ok: result.ok,
|
|
158
|
+
collection: collection.collectionName,
|
|
159
|
+
validationLevel: level
|
|
160
|
+
};
|
|
161
|
+
} catch (error) {
|
|
162
|
+
logger.error('setValidationLevel failed', { error: error.message });
|
|
163
|
+
throw createValidationError(
|
|
164
|
+
`Failed to set validation level: ${error.message}`,
|
|
165
|
+
'SET_VALIDATION_LEVEL_ERROR'
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 设置验证失败时的行为
|
|
172
|
+
*
|
|
173
|
+
* 验证行为:
|
|
174
|
+
* - 'error': 拒绝不符合规则的文档(默认)
|
|
175
|
+
* - 'warn': 允许写入但记录警告
|
|
176
|
+
*
|
|
177
|
+
* @param {string} action - 验证行为('error'/'warn')
|
|
178
|
+
* @returns {Promise<Object>} 设置结果
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* // 拒绝无效文档
|
|
182
|
+
* await collection.setValidationAction('error');
|
|
183
|
+
*
|
|
184
|
+
* // 允许但警告
|
|
185
|
+
* await collection.setValidationAction('warn');
|
|
186
|
+
*/
|
|
187
|
+
async setValidationAction(action) {
|
|
188
|
+
try {
|
|
189
|
+
const validActions = ['error', 'warn'];
|
|
190
|
+
if (!validActions.includes(action)) {
|
|
191
|
+
throw createValidationError(
|
|
192
|
+
`Invalid validation action. Must be one of: ${validActions.join(', ')}`,
|
|
193
|
+
'INVALID_VALIDATION_ACTION'
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const result = await db.command({
|
|
198
|
+
collMod: collection.collectionName,
|
|
199
|
+
validationAction: action
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
logger.info('Validation action set', {
|
|
203
|
+
collection: collection.collectionName,
|
|
204
|
+
action: action
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
ok: result.ok,
|
|
209
|
+
collection: collection.collectionName,
|
|
210
|
+
validationAction: action
|
|
211
|
+
};
|
|
212
|
+
} catch (error) {
|
|
213
|
+
logger.error('setValidationAction failed', { error: error.message });
|
|
214
|
+
throw createValidationError(
|
|
215
|
+
`Failed to set validation action: ${error.message}`,
|
|
216
|
+
'SET_VALIDATION_ACTION_ERROR'
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 获取集合的验证规则
|
|
223
|
+
* @returns {Promise<Object>} 验证规则信息
|
|
224
|
+
* @property {Object} validator - 验证规则
|
|
225
|
+
* @property {string} validationLevel - 验证级别
|
|
226
|
+
* @property {string} validationAction - 验证行为
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* const validation = await collection.getValidator();
|
|
230
|
+
* console.log('Validator:', validation.validator);
|
|
231
|
+
* console.log('Level:', validation.validationLevel);
|
|
232
|
+
* console.log('Action:', validation.validationAction);
|
|
233
|
+
*/
|
|
234
|
+
async getValidator() {
|
|
235
|
+
try {
|
|
236
|
+
const collections = await db.listCollections({
|
|
237
|
+
name: collection.collectionName
|
|
238
|
+
}).toArray();
|
|
239
|
+
|
|
240
|
+
if (collections.length === 0) {
|
|
241
|
+
throw createValidationError(
|
|
242
|
+
`Collection '${collection.collectionName}' not found`,
|
|
243
|
+
'COLLECTION_NOT_FOUND'
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const collInfo = collections[0];
|
|
248
|
+
const options = collInfo.options || {};
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
validator: options.validator || null,
|
|
252
|
+
validationLevel: options.validationLevel || 'strict',
|
|
253
|
+
validationAction: options.validationAction || 'error'
|
|
254
|
+
};
|
|
255
|
+
} catch (error) {
|
|
256
|
+
logger.error('getValidator failed', { error: error.message });
|
|
257
|
+
throw createValidationError(
|
|
258
|
+
`Failed to get validator: ${error.message}`,
|
|
259
|
+
'GET_VALIDATOR_ERROR'
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = { createValidationOps };
|
|
267
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aggregate 查询模块
|
|
3
|
+
* @description 提供 MongoDB 聚合管道功能,支持流式返回和缓存
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { AggregateChain } = require('./chain');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 创建 aggregate 查询操作
|
|
10
|
+
* @param {Object} context - 上下文对象
|
|
11
|
+
* @returns {Object} 包含 aggregate 方法的对象
|
|
12
|
+
*/
|
|
13
|
+
function createAggregateOps(context) {
|
|
14
|
+
const {
|
|
15
|
+
collection,
|
|
16
|
+
defaults,
|
|
17
|
+
run,
|
|
18
|
+
instanceId,
|
|
19
|
+
effectiveDbName,
|
|
20
|
+
logger,
|
|
21
|
+
emit,
|
|
22
|
+
mongoSlowLogShaper
|
|
23
|
+
} = context;
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
/**
|
|
27
|
+
* 聚合查询(MongoDB 聚合管道透传)
|
|
28
|
+
* @param {Array} pipeline - 聚合管道数组,如 [{ $match: {...} }, { $group: {...} }]
|
|
29
|
+
* @param {Object} [options={}] - 聚合选项
|
|
30
|
+
* @param {number} [options.cache=0] - 缓存时间(毫秒),默认不缓存(聚合通常动态性强)
|
|
31
|
+
* @param {number} [options.maxTimeMS] - 查询超时时间(毫秒)
|
|
32
|
+
* @param {boolean} [options.allowDiskUse=false] - 是否允许使用磁盘(默认 false)
|
|
33
|
+
* @param {Object} [options.collation] - 排序规则(可选)
|
|
34
|
+
* @param {string|Object} [options.hint] - 索引提示(可选)
|
|
35
|
+
* @param {string} [options.comment] - 查询注释(可选)
|
|
36
|
+
* @param {boolean|Object} [options.meta] - 是否返回耗时元信息
|
|
37
|
+
* @param {boolean} [options.stream] - 是否返回流式结果
|
|
38
|
+
* @param {number} [options.batchSize] - 批处理大小
|
|
39
|
+
* @param {boolean|string} [options.explain] - 是否返回查询执行计划,可选值:true/'queryPlanner'/'executionStats'/'allPlansExecution'
|
|
40
|
+
* @returns {Promise<Array>|ReadableStream|AggregateChain} 聚合结果数组或可读流(当 stream: true 时);当 explain=true 时返回执行计划;默认返回 AggregateChain 实例支持链式调用
|
|
41
|
+
*/
|
|
42
|
+
aggregate: (pipeline = [], options = {}) => {
|
|
43
|
+
// 如果没有提供 options 或 options 为空对象,返回 AggregateChain 以支持完整的链式调用
|
|
44
|
+
const hasOptions = options && Object.keys(options).length > 0;
|
|
45
|
+
|
|
46
|
+
if (!hasOptions) {
|
|
47
|
+
// 返回 AggregateChain 实例,支持 .hint().collation() 等链式调用
|
|
48
|
+
return new AggregateChain(context, pipeline, {});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 如果提供了 options,执行原有逻辑(向后兼容)
|
|
52
|
+
const {
|
|
53
|
+
maxTimeMS = defaults.maxTimeMS,
|
|
54
|
+
allowDiskUse = false,
|
|
55
|
+
collation,
|
|
56
|
+
hint,
|
|
57
|
+
comment,
|
|
58
|
+
stream = false,
|
|
59
|
+
batchSize,
|
|
60
|
+
explain
|
|
61
|
+
} = options;
|
|
62
|
+
|
|
63
|
+
// 构建 MongoDB 聚合选项
|
|
64
|
+
const aggOptions = { maxTimeMS, allowDiskUse };
|
|
65
|
+
if (collation) aggOptions.collation = collation;
|
|
66
|
+
if (hint) aggOptions.hint = hint;
|
|
67
|
+
if (comment) aggOptions.comment = comment;
|
|
68
|
+
if (batchSize !== undefined) aggOptions.batchSize = batchSize;
|
|
69
|
+
|
|
70
|
+
// 如果启用 explain,直接返回执行计划(不缓存)
|
|
71
|
+
if (explain) {
|
|
72
|
+
const verbosity = typeof explain === 'string' ? explain : 'queryPlanner';
|
|
73
|
+
const cursor = collection.aggregate(pipeline, aggOptions);
|
|
74
|
+
return cursor.explain(verbosity);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 如果启用流式返回,直接返回 MongoDB 游标流
|
|
78
|
+
if (stream) {
|
|
79
|
+
const cursor = collection.aggregate(pipeline, aggOptions);
|
|
80
|
+
const readableStream = cursor.stream();
|
|
81
|
+
|
|
82
|
+
// 添加慢查询日志支持
|
|
83
|
+
const startTime = Date.now();
|
|
84
|
+
let docCount = 0;
|
|
85
|
+
|
|
86
|
+
readableStream.on('data', () => {
|
|
87
|
+
docCount++;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
readableStream.on('end', () => {
|
|
91
|
+
const durationMs = Date.now() - startTime;
|
|
92
|
+
const slowQueryMs = defaults?.slowQueryMs || 500;
|
|
93
|
+
|
|
94
|
+
if (durationMs >= slowQueryMs) {
|
|
95
|
+
try {
|
|
96
|
+
const meta = {
|
|
97
|
+
op: 'aggregate-stream',
|
|
98
|
+
durationMs,
|
|
99
|
+
docCount,
|
|
100
|
+
iid: instanceId,
|
|
101
|
+
type: context.type,
|
|
102
|
+
db: effectiveDbName,
|
|
103
|
+
collection: collection.collectionName,
|
|
104
|
+
pipeline: mongoSlowLogShaper?.sanitize ? mongoSlowLogShaper.sanitize(pipeline) : pipeline,
|
|
105
|
+
};
|
|
106
|
+
logger?.warn?.('🐌 Slow aggregate stream', meta);
|
|
107
|
+
emit?.('slow-query', meta);
|
|
108
|
+
} catch (_) { }
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return readableStream;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 执行聚合的 Promise
|
|
116
|
+
const resultPromise = run(
|
|
117
|
+
'aggregate',
|
|
118
|
+
options,
|
|
119
|
+
async () => collection.aggregate(pipeline, aggOptions).toArray()
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// 添加 explain 方法支持链式调用(与原生 MongoDB 一致)
|
|
123
|
+
resultPromise.explain = async (verbosity = 'queryPlanner') => {
|
|
124
|
+
const cursor = collection.aggregate(pipeline, aggOptions);
|
|
125
|
+
return cursor.explain(verbosity);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return resultPromise;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = createAggregateOps;
|