monsqlize 1.3.0 → 2.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.
Files changed (172) hide show
  1. package/CHANGELOG.md +56 -60
  2. package/LICENSE +201 -21
  3. package/README.md +537 -1828
  4. package/changelogs/README.md +160 -0
  5. package/changelogs/v2.0.0.md +222 -0
  6. package/dist/cjs/index.cjs +10600 -0
  7. package/dist/cjs/mongodb/common/transaction-aware.cjs +10 -0
  8. package/dist/cjs/transaction/CacheLockManager.cjs +100 -0
  9. package/dist/cjs/transaction/Transaction.cjs +158 -0
  10. package/dist/cjs/transaction/TransactionManager.cjs +298 -0
  11. package/dist/esm/index.mjs +10650 -0
  12. package/dist/types/base.d.ts +81 -0
  13. package/dist/types/collection.d.ts +1031 -0
  14. package/dist/types/expression.d.ts +115 -0
  15. package/dist/types/index.d.ts +23 -0
  16. package/dist/types/lock.d.ts +74 -0
  17. package/dist/types/model.d.ts +526 -0
  18. package/dist/types/mongodb.d.ts +49 -0
  19. package/dist/types/monsqlize.d.ts +491 -0
  20. package/dist/types/pool.d.ts +84 -0
  21. package/dist/types/runtime.d.ts +362 -0
  22. package/dist/types/saga.d.ts +143 -0
  23. package/dist/types/slow-query-log.d.ts +126 -0
  24. package/dist/types/sync.d.ts +103 -0
  25. package/dist/types/transaction.d.ts +132 -0
  26. package/package.json +67 -69
  27. package/index.d.ts +0 -206
  28. package/index.mjs +0 -52
  29. package/lib/cache-invalidation.js +0 -279
  30. package/lib/cache.js +0 -530
  31. package/lib/common/cursor.js +0 -59
  32. package/lib/common/docs-urls.js +0 -73
  33. package/lib/common/index-options.js +0 -223
  34. package/lib/common/log.js +0 -61
  35. package/lib/common/namespace.js +0 -22
  36. package/lib/common/normalize.js +0 -34
  37. package/lib/common/page-result.js +0 -43
  38. package/lib/common/runner.js +0 -57
  39. package/lib/common/server-features.js +0 -232
  40. package/lib/common/shape-builders.js +0 -27
  41. package/lib/common/validation.js +0 -113
  42. package/lib/connect.js +0 -99
  43. package/lib/constants.js +0 -55
  44. package/lib/count-queue.js +0 -188
  45. package/lib/distributed-cache-invalidator.js +0 -260
  46. package/lib/errors.js +0 -168
  47. package/lib/expression/cache/ExpressionCache.js +0 -114
  48. package/lib/expression/compiler/ExpressionCompiler.js +0 -1090
  49. package/lib/expression/compiler/ExpressionCompilerExtensions.js +0 -531
  50. package/lib/expression/detector.js +0 -84
  51. package/lib/expression/factory.js +0 -29
  52. package/lib/expression/index.js +0 -19
  53. package/lib/function-cache.js +0 -533
  54. package/lib/index.js +0 -1251
  55. package/lib/infrastructure/ConnectionPoolManager.js +0 -464
  56. package/lib/infrastructure/HealthChecker.js +0 -281
  57. package/lib/infrastructure/PoolConfig.js +0 -199
  58. package/lib/infrastructure/PoolSelector.js +0 -225
  59. package/lib/infrastructure/PoolStats.js +0 -244
  60. package/lib/infrastructure/ssh-tunnel-ssh2.js +0 -212
  61. package/lib/infrastructure/ssh-tunnel.js +0 -41
  62. package/lib/infrastructure/uri-parser.js +0 -36
  63. package/lib/lock/Lock.js +0 -67
  64. package/lib/lock/errors.js +0 -28
  65. package/lib/lock/index.js +0 -13
  66. package/lib/logger.js +0 -225
  67. package/lib/model/examples/test.js +0 -311
  68. package/lib/model/features/defaults.js +0 -161
  69. package/lib/model/features/populate.js +0 -568
  70. package/lib/model/features/relations.js +0 -120
  71. package/lib/model/features/soft-delete.js +0 -349
  72. package/lib/model/features/version.js +0 -157
  73. package/lib/model/features/virtuals.js +0 -219
  74. package/lib/model/index.js +0 -1265
  75. package/lib/mongodb/common/accessor-helpers.js +0 -59
  76. package/lib/mongodb/common/agg-pipeline.js +0 -36
  77. package/lib/mongodb/common/aggregation-validator.js +0 -127
  78. package/lib/mongodb/common/iid.js +0 -28
  79. package/lib/mongodb/common/lexicographic-expr.js +0 -53
  80. package/lib/mongodb/common/shape.js +0 -32
  81. package/lib/mongodb/common/sort.js +0 -39
  82. package/lib/mongodb/common/transaction-aware.js +0 -25
  83. package/lib/mongodb/connect.js +0 -234
  84. package/lib/mongodb/index.js +0 -639
  85. package/lib/mongodb/management/admin-ops.js +0 -200
  86. package/lib/mongodb/management/bookmark-ops.js +0 -167
  87. package/lib/mongodb/management/cache-ops.js +0 -50
  88. package/lib/mongodb/management/collection-ops.js +0 -387
  89. package/lib/mongodb/management/database-ops.js +0 -202
  90. package/lib/mongodb/management/index-ops.js +0 -475
  91. package/lib/mongodb/management/index.js +0 -17
  92. package/lib/mongodb/management/namespace.js +0 -31
  93. package/lib/mongodb/management/validation-ops.js +0 -268
  94. package/lib/mongodb/queries/aggregate.js +0 -172
  95. package/lib/mongodb/queries/chain.js +0 -631
  96. package/lib/mongodb/queries/count.js +0 -99
  97. package/lib/mongodb/queries/distinct.js +0 -78
  98. package/lib/mongodb/queries/find-and-count.js +0 -193
  99. package/lib/mongodb/queries/find-by-ids.js +0 -236
  100. package/lib/mongodb/queries/find-one-by-id.js +0 -171
  101. package/lib/mongodb/queries/find-one.js +0 -71
  102. package/lib/mongodb/queries/find-page.js +0 -618
  103. package/lib/mongodb/queries/find.js +0 -171
  104. package/lib/mongodb/queries/index.js +0 -51
  105. package/lib/mongodb/queries/watch.js +0 -538
  106. package/lib/mongodb/writes/common/batch-retry.js +0 -65
  107. package/lib/mongodb/writes/delete-batch.js +0 -323
  108. package/lib/mongodb/writes/delete-many.js +0 -181
  109. package/lib/mongodb/writes/delete-one.js +0 -173
  110. package/lib/mongodb/writes/find-one-and-delete.js +0 -203
  111. package/lib/mongodb/writes/find-one-and-replace.js +0 -239
  112. package/lib/mongodb/writes/find-one-and-update.js +0 -240
  113. package/lib/mongodb/writes/increment-one.js +0 -259
  114. package/lib/mongodb/writes/index.js +0 -46
  115. package/lib/mongodb/writes/insert-batch.js +0 -508
  116. package/lib/mongodb/writes/insert-many.js +0 -223
  117. package/lib/mongodb/writes/insert-one.js +0 -169
  118. package/lib/mongodb/writes/replace-one.js +0 -226
  119. package/lib/mongodb/writes/result-handler.js +0 -237
  120. package/lib/mongodb/writes/update-batch.js +0 -416
  121. package/lib/mongodb/writes/update-many.js +0 -275
  122. package/lib/mongodb/writes/update-one.js +0 -273
  123. package/lib/mongodb/writes/upsert-one.js +0 -203
  124. package/lib/multi-level-cache.js +0 -244
  125. package/lib/operators.js +0 -330
  126. package/lib/redis-cache-adapter.js +0 -267
  127. package/lib/saga/SagaContext.js +0 -67
  128. package/lib/saga/SagaDefinition.js +0 -32
  129. package/lib/saga/SagaExecutor.js +0 -201
  130. package/lib/saga/SagaOrchestrator.js +0 -186
  131. package/lib/saga/index.js +0 -11
  132. package/lib/slow-query-log/base-storage.js +0 -70
  133. package/lib/slow-query-log/batch-queue.js +0 -97
  134. package/lib/slow-query-log/config-manager.js +0 -196
  135. package/lib/slow-query-log/index.js +0 -238
  136. package/lib/slow-query-log/mongodb-storage.js +0 -324
  137. package/lib/slow-query-log/query-hash.js +0 -39
  138. package/lib/sync/ChangeStreamSyncManager.js +0 -405
  139. package/lib/sync/ResumeTokenStore.js +0 -192
  140. package/lib/sync/SyncConfig.js +0 -127
  141. package/lib/sync/SyncTarget.js +0 -240
  142. package/lib/sync/index.js +0 -20
  143. package/lib/transaction/CacheLockManager.js +0 -162
  144. package/lib/transaction/DistributedCacheLockManager.js +0 -475
  145. package/lib/transaction/Transaction.js +0 -315
  146. package/lib/transaction/TransactionManager.js +0 -267
  147. package/lib/transaction/index.js +0 -11
  148. package/lib/utils/objectid-converter.js +0 -632
  149. package/types/README.md +0 -122
  150. package/types/base.ts +0 -94
  151. package/types/batch.ts +0 -187
  152. package/types/cache.ts +0 -71
  153. package/types/chain.ts +0 -254
  154. package/types/collection.ts +0 -357
  155. package/types/expression.ts +0 -109
  156. package/types/function-cache.d.ts +0 -135
  157. package/types/lock.ts +0 -95
  158. package/types/model/definition.ts +0 -152
  159. package/types/model/index.ts +0 -10
  160. package/types/model/instance.ts +0 -121
  161. package/types/model/relations.ts +0 -121
  162. package/types/model/virtuals.ts +0 -32
  163. package/types/monsqlize.ts +0 -245
  164. package/types/options.ts +0 -192
  165. package/types/pagination.ts +0 -154
  166. package/types/pool.ts +0 -125
  167. package/types/query.ts +0 -71
  168. package/types/saga.ts +0 -125
  169. package/types/stream.ts +0 -64
  170. package/types/sync.ts +0 -79
  171. package/types/transaction.ts +0 -79
  172. package/types/write.ts +0 -77
@@ -1,99 +0,0 @@
1
- /**
2
- * count 查询模块
3
- * @description 提供文档计数功能,使用 MongoDB 原生推荐的 countDocuments() 和 estimatedDocumentCount() 方法
4
- */
5
-
6
- const { convertObjectIdStrings } = require('../../utils/objectid-converter');
7
-
8
- /**
9
- * 创建 count 查询操作
10
- * @param {Object} context - 上下文对象
11
- * @returns {Object} 包含 count 方法的对象
12
- */
13
- function createCountOps(context) {
14
- const { collection, defaults, run, effectiveDbName } = context;
15
-
16
- return {
17
- /**
18
- * 统计文档数量
19
- * @description 根据查询条件统计匹配的文档数量。空查询时使用 estimatedDocumentCount(基于元数据,快速),有查询条件时使用 countDocuments(精确统计)
20
- * @param {Object} [query={}] - 查询条件,使用 MongoDB 查询语法,空对象表示统计所有文档
21
- * @param {Object} [options={}] - 查询选项配置对象
22
- * @param {number} [options.cache=0] - 缓存时间(毫秒),0表示不缓存,>0时结果将被缓存指定时间
23
- * @param {number} [options.maxTimeMS] - 查询超时时间(毫秒),防止长时间查询阻塞
24
- * @param {boolean|string} [options.explain] - 是否返回查询执行计划,可选值:true/'queryPlanner'/'executionStats'/'allPlansExecution'
25
- * @param {string} [options.hint] - 索引提示,指定使用的索引名称或索引规范(仅 countDocuments)
26
- * @param {Object} [options.collation] - 排序规则配置(仅 countDocuments)
27
- * @param {number} [options.skip] - 跳过的文档数量(仅 countDocuments)
28
- * @param {number} [options.limit] - 限制统计的文档数量(仅 countDocuments)
29
- * @param {string} [options.comment] - 查询注释,用于日志和性能分析
30
- * @returns {Promise<number>} 匹配的文档数量;当 explain=true 时返回执行计划对象
31
- */
32
- count: async (query = {}, options = {}) => {
33
- // ✅ v1.3.0: 自动转换 ObjectId 字符串
34
- const convertedQuery = convertObjectIdStrings(query, 'query', 0, new WeakSet(), {
35
- logger: context.logger,
36
- excludeFields: context.autoConvertConfig?.excludeFields,
37
- customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
38
- maxDepth: context.autoConvertConfig?.maxDepth
39
- });
40
-
41
- const { maxTimeMS = defaults.maxTimeMS, explain, comment } = options;
42
-
43
- // 如果启用 explain,直接返回查询执行计划(不缓存)
44
- if (explain) {
45
- const verbosity = typeof explain === 'string' ? explain : 'queryPlanner';
46
- const isEmptyQuery = !query || Object.keys(query).length === 0;
47
-
48
- if (isEmptyQuery) {
49
- // estimatedDocumentCount 没有 explain,返回集合统计信息
50
- return {
51
- queryPlanner: { plannerVersion: 1, namespace: `${effectiveDbName}.${collection.collectionName}` },
52
- executionStats: { executionSuccess: true, estimatedCount: true },
53
- command: { estimatedDocumentCount: collection.collectionName }
54
- };
55
- } else {
56
- // countDocuments 通过聚合管道实现,使用 aggregate 获取 explain
57
- const pipeline = [{ $match: convertedQuery }, { $count: 'total' }];
58
- const aggOpts = {
59
- maxTimeMS,
60
- ...(options.hint && { hint: options.hint }),
61
- ...(options.collation && { collation: options.collation })
62
- };
63
- if (comment) aggOpts.comment = comment;
64
- return await collection.aggregate(pipeline, aggOpts).explain(verbosity);
65
- }
66
- }
67
-
68
- // 性能优化:判断是否为空查询
69
- const isEmptyQuery = !convertedQuery || Object.keys(convertedQuery).length === 0;
70
-
71
- return run(
72
- 'count',
73
- { query: convertedQuery, ...options },
74
- () => {
75
- if (isEmptyQuery) {
76
- // 空查询使用 estimatedDocumentCount(快速,基于集合元数据)
77
- const estOpts = { maxTimeMS };
78
- if (comment) estOpts.comment = comment;
79
- return collection.estimatedDocumentCount(estOpts);
80
- } else {
81
- // 有查询条件使用 countDocuments(精确统计)
82
- const countOpts = {
83
- maxTimeMS,
84
- ...(options.hint && { hint: options.hint }),
85
- ...(options.collation && { collation: options.collation }),
86
- ...(options.skip && { skip: options.skip }),
87
- ...(options.limit && { limit: options.limit })
88
- };
89
- if (comment) countOpts.comment = comment;
90
- return collection.countDocuments(convertedQuery, countOpts);
91
- }
92
- }
93
- );
94
- }
95
- };
96
- }
97
-
98
- module.exports = createCountOps;
99
-
@@ -1,78 +0,0 @@
1
- /**
2
- * distinct 查询模块
3
- * @description 提供字段去重查询功能,使用 MongoDB 原生 distinct() 方法
4
- */
5
-
6
- const { convertObjectIdStrings } = require('../../utils/objectid-converter');
7
-
8
- /**
9
- * 创建 distinct 查询操作
10
- * @param {Object} context - 上下文对象
11
- * @returns {Object} 包含 distinct 方法的对象
12
- */
13
- function createDistinctOps(context) {
14
- const { collection, defaults, run } = context;
15
-
16
- return {
17
- /**
18
- * 字段去重查询
19
- * @description 对指定字段进行去重查询,返回该字段的所有唯一值数组。支持嵌套字段和数组字段(自动展开去重)
20
- * @param {string} field - 要去重的字段名,支持嵌套字段(如 'user.name'、'address.city')
21
- * @param {Object} [query={}] - 查询条件,只对匹配的文档进行去重,使用 MongoDB 查询语法
22
- * @param {Object} [options={}] - 查询选项配置对象
23
- * @param {number} [options.maxTimeMS] - 查询超时时间(毫秒),防止长时间查询阻塞
24
- * @param {Object} [options.collation] - 排序规则配置,用于字符串比较和去重(如不区分大小写)
25
- * @param {string} [options.comment] - 查询注释,用于日志和性能分析
26
- * @param {number} [options.cache=0] - 缓存时间(毫秒),0表示不缓存,>0时结果将被缓存指定时间
27
- * @param {boolean|string} [options.explain] - 是否返回查询执行计划,可选值:true/'queryPlanner'/'executionStats'/'allPlansExecution'
28
- * @returns {Promise<Array>} 返回去重后的值数组;当 explain=true 时返回执行计划对象
29
- */
30
- distinct: async (field, query = {}, options = {}) => {
31
- // ✅ v1.3.0: 自动转换 ObjectId 字符串
32
- const convertedQuery = convertObjectIdStrings(query, 'query', 0, new WeakSet(), {
33
- logger: context.logger,
34
- excludeFields: context.autoConvertConfig?.excludeFields,
35
- customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
36
- maxDepth: context.autoConvertConfig?.maxDepth
37
- });
38
- const {
39
- maxTimeMS = defaults.maxTimeMS,
40
- collation,
41
- comment,
42
- explain
43
- } = options;
44
-
45
- // 构建驱动选项
46
- const driverOpts = { maxTimeMS };
47
- if (collation) driverOpts.collation = collation;
48
- if (comment) driverOpts.comment = comment;
49
-
50
- // 如果启用 explain,通过 aggregate 模拟 distinct 并返回执行计划
51
- // 注意:MongoDB 原生 distinct 命令不支持 explain,需要通过聚合管道模拟
52
- if (explain) {
53
- const verbosity = typeof explain === 'string' ? explain : 'queryPlanner';
54
- // distinct 命令通过聚合管道模拟:$match + $group
55
- const pipeline = [];
56
- if (convertedQuery && Object.keys(convertedQuery).length > 0) {
57
- pipeline.push({ $match: convertedQuery });
58
- }
59
- pipeline.push({ $group: { _id: `$${field}` } });
60
-
61
- const aggOpts = { maxTimeMS };
62
- if (collation) aggOpts.collation = collation;
63
- if (comment) aggOpts.comment = comment;
64
-
65
- return await collection.aggregate(pipeline, aggOpts).explain(verbosity);
66
- }
67
-
68
- return run(
69
- 'distinct',
70
- { field, query: convertedQuery, ...options },
71
- () => collection.distinct(field, convertedQuery, driverOpts)
72
- );
73
- }
74
- };
75
- }
76
-
77
- module.exports = createDistinctOps;
78
-
@@ -1,193 +0,0 @@
1
- /**
2
- * findAndCount 查询操作模块
3
- * @description 便利方法:同时返回数据和总数
4
- */
5
-
6
- const { createError, ErrorCodes } = require('../../errors');
7
- const { convertObjectIdStrings } = require('../../utils/objectid-converter');
8
-
9
- /**
10
- * 创建 findAndCount 操作
11
- * @param {Object} context - 上下文对象
12
- * @returns {Function} findAndCount 方法
13
- */
14
- function createFindAndCountOps(context) {
15
- const {
16
- collection,
17
- defaults,
18
- instanceId,
19
- effectiveDbName,
20
- logger,
21
- emit,
22
- mongoSlowLogShaper,
23
- cache,
24
- type
25
- } = context;
26
-
27
- /**
28
- * 查询数据并返回总数(同时执行)
29
- * @param {Object} [query={}] - 查询条件
30
- * @param {Object} [options={}] - 查询选项
31
- * @param {Object} [options.projection] - 字段投影
32
- * @param {Object} [options.sort] - 排序方式
33
- * @param {number} [options.limit] - 限制返回数量
34
- * @param {number} [options.skip] - 跳过数量
35
- * @param {number} [options.cache] - 缓存时间(毫秒)
36
- * @param {number} [options.maxTimeMS] - 查询超时(毫秒)
37
- * @param {string} [options.comment] - 查询注释
38
- * @returns {Promise<Object>} { data, total }
39
- *
40
- * @example
41
- * // 基础用法
42
- * const { data, total } = await collection('users').findAndCount(
43
- * { status: 'active' },
44
- * { limit: 10, skip: 0 }
45
- * );
46
- *
47
- * @example
48
- * // 分页查询
49
- * const page = 1;
50
- * const pageSize = 20;
51
- * const { data, total } = await collection('users').findAndCount(
52
- * { role: 'user' },
53
- * { limit: pageSize, skip: (page - 1) * pageSize }
54
- * );
55
- * const totalPages = Math.ceil(total / pageSize);
56
- */
57
- const findAndCount = async function findAndCount(query = {}, options = {}) {
58
- const startTime = Date.now();
59
-
60
- // 1. 参数验证和归一化
61
- if (query !== null && typeof query !== 'object' || Array.isArray(query)) {
62
- throw createError(
63
- ErrorCodes.INVALID_ARGUMENT,
64
- 'query 必须是对象',
65
- [{ field: 'query', type: 'type', message: 'query 必须是对象', received: typeof query }]
66
- );
67
- }
68
-
69
- // 将 null 转为空对象
70
- if (query === null) {
71
- query = {};
72
- }
73
-
74
- // ✅ v1.3.0: 自动转换 ObjectId 字符串
75
- const convertedQuery = convertObjectIdStrings(query, 'query', 0, new WeakSet(), {
76
- logger: context.logger,
77
- excludeFields: context.autoConvertConfig?.excludeFields,
78
- customFieldPatterns: context.autoConvertConfig?.customFieldPatterns,
79
- maxDepth: context.autoConvertConfig?.maxDepth
80
- });
81
-
82
- // 2. 提取选项
83
- const projection = options.projection;
84
- const sort = options.sort;
85
- const limit = options.limit; // 不使用默认值,未指定时查询所有
86
- const skip = options.skip || 0;
87
- const cacheTime = options.cache !== undefined ? options.cache : defaults.cache;
88
- const maxTimeMS = options.maxTimeMS !== undefined ? options.maxTimeMS : defaults.maxTimeMS;
89
- const comment = options.comment;
90
-
91
- // 3. 缓存键(包含 query, projection, sort, limit, skip)
92
- const cacheKey = cache ? `${instanceId}:${type}:${effectiveDbName}:${collection.collectionName}:findAndCount:${JSON.stringify({ query: convertedQuery, projection, sort, limit, skip })}` : null;
93
-
94
- // 4. 检查缓存
95
- if (cache && cacheTime > 0) {
96
- try {
97
- const cached = await cache.get(cacheKey);
98
- // 必须检查 !== null 和 !== undefined,因为 undefined 也会被缓存
99
- if (cached !== null && cached !== undefined) {
100
- logger?.debug?.('[findAndCount] 缓存命中', {
101
- ns: `${effectiveDbName}.${collection.collectionName}`,
102
- query
103
- });
104
- return cached;
105
- }
106
- } catch (cacheError) {
107
- logger?.warn?.('[findAndCount] 缓存读取失败', { error: cacheError.message });
108
- }
109
- }
110
-
111
- // 5. 构建查询选项
112
- const findOptions = { maxTimeMS };
113
- if (projection) findOptions.projection = projection;
114
- if (sort) findOptions.sort = sort;
115
- // limit: undefined/null 表示不限制,0 表示返回0条,其他数字表示限制数量
116
- if (limit !== undefined && limit !== null) {
117
- findOptions.limit = limit;
118
- }
119
- if (skip) findOptions.skip = skip;
120
- if (comment) findOptions.comment = comment;
121
-
122
- const countOptions = { maxTimeMS };
123
- if (comment) countOptions.comment = comment;
124
-
125
- // 6. 并行执行查询和计数
126
- let data, total;
127
- try {
128
- [data, total] = await Promise.all([
129
- collection.find(convertedQuery, findOptions).toArray(),
130
- collection.countDocuments(convertedQuery, countOptions)
131
- ]);
132
- } catch (error) {
133
- throw error;
134
- }
135
-
136
- // 7. 构建结果
137
- const result = { data, total };
138
-
139
- // 8. 写入缓存
140
- if (cache && cacheTime > 0) {
141
- try {
142
- await cache.set(cacheKey, result, cacheTime);
143
- } catch (cacheError) {
144
- logger?.warn?.('[findAndCount] 缓存写入失败', { error: cacheError.message });
145
- }
146
- }
147
-
148
- // 9. 慢查询日志
149
- const duration = Date.now() - startTime;
150
- const slowQueryMs = defaults?.slowQueryMs || 1000;
151
-
152
- if (duration >= slowQueryMs) {
153
- try {
154
- const meta = {
155
- operation: 'findAndCount',
156
- durationMs: duration,
157
- iid: instanceId,
158
- type,
159
- db: effectiveDbName,
160
- collection: collection.collectionName,
161
- dataCount: data.length,
162
- total,
163
- query: mongoSlowLogShaper?.sanitize ? mongoSlowLogShaper.sanitize(query) : query,
164
- projection,
165
- sort,
166
- limit,
167
- skip,
168
- comment
169
- };
170
- logger?.warn?.('🐌 Slow query: findAndCount', meta);
171
- emit?.('slow-query', meta);
172
- } catch (_) {
173
- // 忽略日志错误
174
- }
175
- }
176
-
177
- // 10. 日志记录
178
- logger?.debug?.('[findAndCount] 查询完成', {
179
- ns: `${effectiveDbName}.${collection.collectionName}`,
180
- duration,
181
- dataCount: data.length,
182
- total
183
- });
184
-
185
- return result;
186
- };
187
-
188
- return { findAndCount };
189
- }
190
-
191
- module.exports = { createFindAndCountOps };
192
-
193
-
@@ -1,236 +0,0 @@
1
- /**
2
- * findByIds 查询操作模块
3
- * @description 便利方法:批量通过 _id 数组查询多个文档
4
- */
5
-
6
- const { ObjectId } = require('mongodb');
7
- const { createError, ErrorCodes } = require('../../errors');
8
-
9
- /**
10
- * 创建 findByIds 操作
11
- * @param {Object} context - 上下文对象
12
- * @returns {Function} findByIds 方法
13
- */
14
- function createFindByIdsOps(context) {
15
- const {
16
- collection,
17
- defaults,
18
- instanceId,
19
- effectiveDbName,
20
- logger,
21
- emit,
22
- mongoSlowLogShaper,
23
- cache,
24
- type
25
- } = context;
26
-
27
- /**
28
- * 批量通过 _id 查询多个文档
29
- * @param {Array<string|ObjectId>} ids - _id 数组(支持字符串和 ObjectId)
30
- * @param {Object} [options={}] - 查询选项
31
- * @param {Object} [options.projection] - 字段投影
32
- * @param {Object} [options.sort] - 排序方式
33
- * @param {number} [options.cache] - 缓存时间(毫秒)
34
- * @param {number} [options.maxTimeMS] - 查询超时(毫秒)
35
- * @param {string} [options.comment] - 查询注释
36
- * @param {boolean} [options.preserveOrder=false] - 是否保持 ids 数组的顺序
37
- * @returns {Promise<Array>} 文档数组
38
- *
39
- * @example
40
- * // 基础用法
41
- * const users = await collection('users').findByIds([
42
- * '507f1f77bcf86cd799439011',
43
- * '507f1f77bcf86cd799439012'
44
- * ]);
45
- *
46
- * @example
47
- * // 带选项
48
- * const users = await collection('users').findByIds(
49
- * ['507f1f77bcf86cd799439011', '507f1f77bcf86cd799439012'],
50
- * {
51
- * projection: { name: 1, email: 1 },
52
- * preserveOrder: true
53
- * }
54
- * );
55
- */
56
- const findByIds = async function findByIds(ids, options = {}) {
57
- const startTime = Date.now();
58
-
59
- // 1. 参数验证
60
- if (!Array.isArray(ids)) {
61
- throw createError(
62
- ErrorCodes.INVALID_ARGUMENT,
63
- 'ids 必须是数组',
64
- [{ field: 'ids', type: 'type', message: 'ids 必须是数组', received: typeof ids }]
65
- );
66
- }
67
-
68
- if (ids.length === 0) {
69
- // 空数组直接返回空结果
70
- return [];
71
- }
72
-
73
- // 2. 转换所有 ID 为 ObjectId
74
- const objectIds = [];
75
- const invalidIds = [];
76
-
77
- for (let i = 0; i < ids.length; i++) {
78
- const id = ids[i];
79
-
80
- if (id instanceof ObjectId) {
81
- objectIds.push(id);
82
- } else if (typeof id === 'string') {
83
- // 验证字符串是否是有效的 ObjectId 格式(24 个十六进制字符)
84
- if (!/^[0-9a-fA-F]{24}$/.test(id)) {
85
- invalidIds.push({ index: i, value: id });
86
- } else {
87
- objectIds.push(new ObjectId(id));
88
- }
89
- } else {
90
- // 拒绝其他类型(数字、对象等)
91
- invalidIds.push({ index: i, value: id, type: typeof id });
92
- }
93
- }
94
-
95
- // 如果有无效 ID,抛出错误
96
- if (invalidIds.length > 0) {
97
- throw createError(
98
- ErrorCodes.INVALID_ARGUMENT,
99
- `ids 数组包含 ${invalidIds.length} 个无效 ID`,
100
- invalidIds.map(item => ({
101
- field: `ids[${item.index}]`,
102
- type: 'format',
103
- message: '无效的 ObjectId 格式',
104
- received: item.value
105
- }))
106
- );
107
- }
108
-
109
- // 3. 去重(避免重复查询)
110
- const uniqueIds = [...new Set(objectIds.map(id => id.toString()))].map(id => new ObjectId(id));
111
-
112
- // 4. 提取选项
113
- const projection = options.projection;
114
- const sort = options.sort;
115
- const cacheTime = options.cache !== undefined ? options.cache : defaults.cache;
116
- const maxTimeMS = options.maxTimeMS !== undefined ? options.maxTimeMS : defaults.maxTimeMS;
117
- const comment = options.comment;
118
- const preserveOrder = options.preserveOrder === true;
119
-
120
- // 5. 构建查询
121
- const query = { _id: { $in: uniqueIds } };
122
-
123
- // 6. 缓存键
124
- const cacheKey = cache ? `${instanceId}:${type}:${effectiveDbName}:${collection.collectionName}:findByIds:${JSON.stringify({ ids: uniqueIds.map(id => id.toString()), projection, sort })}` : null;
125
-
126
- // 7. 检查缓存
127
- if (cache && cacheTime > 0) {
128
- try {
129
- const cached = await cache.get(cacheKey);
130
- if (cached != null) {
131
- logger?.debug?.('[findByIds] 缓存命中', {
132
- ns: `${effectiveDbName}.${collection.collectionName}`,
133
- idsCount: ids.length,
134
- uniqueCount: uniqueIds.length
135
- });
136
-
137
- // 如果需要保持顺序,重新排序结果
138
- if (preserveOrder) {
139
- return reorderResults(cached, objectIds);
140
- }
141
- return cached;
142
- }
143
- } catch (cacheError) {
144
- logger?.warn?.('[findByIds] 缓存读取失败', { error: cacheError.message });
145
- }
146
- }
147
-
148
- // 8. 构建查询选项
149
- const findOptions = { maxTimeMS };
150
- if (projection) findOptions.projection = projection;
151
- if (sort) findOptions.sort = sort;
152
- if (comment) findOptions.comment = comment;
153
-
154
- // 9. 执行查询
155
- let results;
156
- try {
157
- results = await collection.find(query, findOptions).toArray();
158
- } catch (error) {
159
- throw error;
160
- }
161
-
162
- // 10. 写入缓存
163
- if (cache && cacheTime > 0 && results) {
164
- try {
165
- await cache.set(cacheKey, results, cacheTime);
166
- } catch (cacheError) {
167
- logger?.warn?.('[findByIds] 缓存写入失败', { error: cacheError.message });
168
- }
169
- }
170
-
171
- // 11. 慢查询日志
172
- const duration = Date.now() - startTime;
173
- const slowQueryMs = defaults?.slowQueryMs || 1000;
174
-
175
- if (duration >= slowQueryMs) {
176
- try {
177
- const meta = {
178
- operation: 'findByIds',
179
- durationMs: duration,
180
- iid: instanceId,
181
- type,
182
- db: effectiveDbName,
183
- collection: collection.collectionName,
184
- idsCount: ids.length,
185
- uniqueCount: uniqueIds.length,
186
- resultCount: results.length,
187
- query: mongoSlowLogShaper?.sanitize ? mongoSlowLogShaper.sanitize(query) : query,
188
- projection,
189
- sort,
190
- comment
191
- };
192
- logger?.warn?.('🐌 Slow query: findByIds', meta);
193
- emit?.('slow-query', meta);
194
- } catch (_) {
195
- // 忽略日志错误
196
- }
197
- }
198
-
199
- // 12. 日志记录
200
- logger?.debug?.('[findByIds] 查询完成', {
201
- ns: `${effectiveDbName}.${collection.collectionName}`,
202
- duration,
203
- idsCount: ids.length,
204
- uniqueCount: uniqueIds.length,
205
- resultCount: results.length
206
- });
207
-
208
- // 13. 如果需要保持顺序,重新排序结果
209
- if (preserveOrder) {
210
- return reorderResults(results, objectIds);
211
- }
212
-
213
- return results;
214
- };
215
-
216
- /**
217
- * 根据原始 ID 顺序重新排序结果
218
- * @param {Array} results - 查询结果
219
- * @param {Array<ObjectId>} orderedIds - 原始 ID 顺序
220
- * @returns {Array} 排序后的结果
221
- */
222
- function reorderResults(results, orderedIds) {
223
- const resultMap = new Map();
224
- results.forEach(doc => {
225
- resultMap.set(doc._id.toString(), doc);
226
- });
227
-
228
- return orderedIds.map(id => resultMap.get(id.toString())).filter(doc => doc !== undefined);
229
- }
230
-
231
- return { findByIds };
232
- }
233
-
234
- module.exports = { createFindByIdsOps };
235
-
236
-