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,223 +0,0 @@
1
- /**
2
- * 索引选项验证和归一化工具
3
- *
4
- * @module common/index-options
5
- * @since 2025-11-17
6
- */
7
-
8
- const { createError } = require('../errors');
9
-
10
- /**
11
- * 验证索引键定义
12
- *
13
- * @param {Object} keys - 索引键定义
14
- * @throws {Error} 如果索引键无效
15
- *
16
- * @example
17
- * validateIndexKeys({ email: 1 }); // ✓
18
- * validateIndexKeys({ age: -1 }); // ✓
19
- * validateIndexKeys({ name: "text" }); // ✓
20
- * validateIndexKeys({ "$**": 1 }); // ✓ 通配符索引
21
- * validateIndexKeys({}); // ✗ 抛出错误
22
- * validateIndexKeys({ email: 2 }); // ✗ 抛出错误
23
- */
24
- function validateIndexKeys(keys) {
25
- if (!keys || typeof keys !== 'object') {
26
- throw createError(
27
- 'INVALID_ARGUMENT',
28
- '索引键必须是对象',
29
- { keys }
30
- );
31
- }
32
-
33
- const keyNames = Object.keys(keys);
34
-
35
- if (keyNames.length === 0) {
36
- throw createError(
37
- 'INVALID_ARGUMENT',
38
- '索引键不能为空',
39
- { keys }
40
- );
41
- }
42
-
43
- // 验证每个键的值
44
- for (const key of keyNames) {
45
- const value = keys[key];
46
-
47
- // 允许的值:1(升序)、-1(降序)、"text"(文本索引)、"2d"(地理索引)、"2dsphere"、"hashed"、"columnstore"
48
- const validValues = [1, -1, '1', '-1', 'text', '2d', '2dsphere', 'geoHaystack', 'hashed', 'columnstore'];
49
-
50
- if (!validValues.includes(value)) {
51
- throw createError(
52
- 'INVALID_ARGUMENT',
53
- `索引键 "${key}" 的值无效,允许的值: 1, -1, "text", "2d", "2dsphere", "hashed", "columnstore"`,
54
- { key, value, validValues }
55
- );
56
- }
57
- }
58
-
59
- return true;
60
- }
61
-
62
- /**
63
- * 归一化索引选项
64
- * 验证并转换为 MongoDB 驱动接受的格式
65
- *
66
- * @param {Object} options - 用户提供的索引选项
67
- * @returns {Object} 归一化后的选项
68
- *
69
- * @example
70
- * normalizeIndexOptions({ unique: true, name: "email_idx" });
71
- * // 返回: { unique: true, name: "email_idx" }
72
- */
73
- function normalizeIndexOptions(options = {}) {
74
- const normalized = {};
75
-
76
- // 允许的选项列表
77
- const allowedOptions = [
78
- // 通用选项
79
- 'name', // 索引名称
80
- 'unique', // 唯一索引
81
- 'sparse', // 稀疏索引
82
- 'background', // 后台创建(已废弃但保留兼容)
83
-
84
- // TTL 索引
85
- 'expireAfterSeconds', // 文档过期时间
86
-
87
- // 部分索引
88
- 'partialFilterExpression', // 部分索引表达式
89
-
90
- // 排序规则
91
- 'collation', // 排序规则
92
-
93
- // 高级选项
94
- 'hidden', // 隐藏索引(MongoDB 4.4+)
95
- 'wildcardProjection', // 通配符投影
96
-
97
- // 存储引擎
98
- 'storageEngine', // 存储引擎配置
99
-
100
- // 文本索引
101
- 'weights', // 文本权重
102
- 'default_language', // 默认语言
103
- 'language_override', // 语言覆盖字段
104
- 'textIndexVersion', // 文本索引版本
105
-
106
- // 2dsphere 索引
107
- '2dsphereIndexVersion', // 2dsphere 索引版本
108
-
109
- // 其他
110
- 'bits', // 2d 索引精度
111
- 'min', // 2d 索引最小值
112
- 'max', // 2d 索引最大值
113
- 'bucketSize' // geoHaystack 索引桶大小
114
- ];
115
-
116
- // 复制允许的选项
117
- for (const option of allowedOptions) {
118
- if (options.hasOwnProperty(option)) {
119
- normalized[option] = options[option];
120
- }
121
- }
122
-
123
- // 特殊处理:background 已废弃,但保留兼容性
124
- if (normalized.background !== undefined) {
125
- // MongoDB 4.2+ 会忽略 background 选项,但不会报错
126
- // 可以添加警告日志(如果有 logger)
127
- }
128
-
129
- // 验证特定选项
130
- if (normalized.unique !== undefined && typeof normalized.unique !== 'boolean') {
131
- throw createError(
132
- 'INVALID_ARGUMENT',
133
- 'unique 选项必须是布尔值',
134
- { unique: normalized.unique }
135
- );
136
- }
137
-
138
- if (normalized.sparse !== undefined && typeof normalized.sparse !== 'boolean') {
139
- throw createError(
140
- 'INVALID_ARGUMENT',
141
- 'sparse 选项必须是布尔值',
142
- { sparse: normalized.sparse }
143
- );
144
- }
145
-
146
- if (normalized.hidden !== undefined && typeof normalized.hidden !== 'boolean') {
147
- throw createError(
148
- 'INVALID_ARGUMENT',
149
- 'hidden 选项必须是布尔值',
150
- { hidden: normalized.hidden }
151
- );
152
- }
153
-
154
- if (normalized.expireAfterSeconds !== undefined) {
155
- const ttl = normalized.expireAfterSeconds;
156
- if (typeof ttl !== 'number' || ttl < 0 || !Number.isInteger(ttl)) {
157
- throw createError(
158
- 'INVALID_ARGUMENT',
159
- 'expireAfterSeconds 必须是非负整数',
160
- { expireAfterSeconds: ttl }
161
- );
162
- }
163
- }
164
-
165
- if (normalized.name !== undefined && typeof normalized.name !== 'string') {
166
- throw createError(
167
- 'INVALID_ARGUMENT',
168
- 'name 选项必须是字符串',
169
- { name: normalized.name }
170
- );
171
- }
172
-
173
- if (normalized.partialFilterExpression !== undefined) {
174
- if (typeof normalized.partialFilterExpression !== 'object' || normalized.partialFilterExpression === null) {
175
- throw createError(
176
- 'INVALID_ARGUMENT',
177
- 'partialFilterExpression 必须是对象',
178
- { partialFilterExpression: normalized.partialFilterExpression }
179
- );
180
- }
181
- }
182
-
183
- if (normalized.collation !== undefined) {
184
- if (typeof normalized.collation !== 'object' || normalized.collation === null) {
185
- throw createError(
186
- 'INVALID_ARGUMENT',
187
- 'collation 必须是对象',
188
- { collation: normalized.collation }
189
- );
190
- }
191
- }
192
-
193
- return normalized;
194
- }
195
-
196
- /**
197
- * 生成索引名称(当用户未指定时)
198
- *
199
- * @param {Object} keys - 索引键定义
200
- * @returns {string} 生成的索引名称
201
- *
202
- * @example
203
- * generateIndexName({ email: 1 }); // "email_1"
204
- * generateIndexName({ userId: 1, status: -1 }); // "userId_1_status_-1"
205
- * generateIndexName({ name: "text" }); // "name_text"
206
- */
207
- function generateIndexName(keys) {
208
- const parts = [];
209
-
210
- for (const [field, direction] of Object.entries(keys)) {
211
- parts.push(`${field}_${direction}`);
212
- }
213
-
214
- return parts.join('_');
215
- }
216
-
217
- module.exports = {
218
- validateIndexKeys,
219
- normalizeIndexOptions,
220
- generateIndexName
221
- };
222
-
223
-
package/lib/common/log.js DELETED
@@ -1,61 +0,0 @@
1
- /**
2
- * 通用慢查询日志工具
3
- * - getSlowQueryThreshold: 读取阈值(默认 500ms)
4
- * - withSlowQueryLog: 统一包装执行并在超阈值时输出去敏慢日志
5
- */
6
-
7
- function getSlowQueryThreshold(defaults){
8
- const d = defaults || {};
9
- return (d.slowQueryMs && Number(d.slowQueryMs)) || 500;
10
- }
11
-
12
- /**
13
- *
14
- * @param {import('../..').LoggerLike} logger
15
- * @param {object} defaults
16
- * @param {string} op
17
- * @param {{iid?:string,type?:string,db:string,coll:string}} ns
18
- * @param {object} options
19
- * @param {() => Promise<any>} exec
20
- * @param {(options:any)=>object} [slowLogShaper]
21
- * @returns {Promise<any>}
22
- */
23
- async function withSlowQueryLog(logger, defaults, op, ns, options, exec, slowLogShaper, onEmit){
24
- const t0 = Date.now();
25
- const res = await exec();
26
- const ms = Date.now() - t0;
27
- const threshold = getSlowQueryThreshold(defaults);
28
- if (ms > threshold) {
29
- const scope = defaults?.namespace?.scope;
30
- const iid = ns?.iid;
31
- const base = {
32
- event: (defaults?.log?.slowQueryTag?.event) || 'slow_query',
33
- code: (defaults?.log?.slowQueryTag?.code) || 'SLOW_QUERY',
34
- category: 'performance',
35
- type: ns?.type || 'mongodb',
36
- iid,
37
- scope,
38
- db: ns.db,
39
- coll: ns.coll,
40
- op,
41
- ms,
42
- threshold,
43
- ts: new Date().toISOString(),
44
- ...(typeof slowLogShaper === 'function' ? slowLogShaper(options) : {})
45
- };
46
- try {
47
- if (typeof defaults?.log?.formatSlowQuery === 'function') {
48
- const formatted = defaults.log.formatSlowQuery(base) || base;
49
- logger.warn('\u23f1\ufe0f Slow query', formatted);
50
- if (typeof onEmit === 'function') { try { onEmit(formatted); } catch(_) {} }
51
- } else {
52
- logger.warn('\u23f1\ufe0f Slow query', base);
53
- if (typeof onEmit === 'function') { try { onEmit(base); } catch(_) {} }
54
- }
55
- } catch (_) { /* ignore logging errors */ }
56
- }
57
- return res;
58
- }
59
-
60
- module.exports = { getSlowQueryThreshold, withSlowQueryLog };
61
-
@@ -1,22 +0,0 @@
1
- /**
2
- * 命名空间实例 id(iid)解析流程(通用)
3
- * 适配器需提供 genInstanceId(databaseName, uri, explicitId?) 实现。
4
- */
5
-
6
- /**
7
- * @param {{ genInstanceId: (db:string, uri?:string, explicitId?:string)=>string }} adapter
8
- * @param {object} defaults
9
- * @param {string} currentDb
10
- * @param {string} initialDb
11
- * @param {string} uri
12
- */
13
- function resolveInstanceId(adapter, defaults, currentDb, initialDb, uri) {
14
- const explicit = defaults?.namespace?.instanceId;
15
- if (explicit) return String(explicit);
16
- const scope = defaults?.namespace?.scope; // 'database'|'connection'
17
- const dbName = scope === 'connection' ? initialDb : (currentDb || initialDb);
18
- return adapter.genInstanceId(dbName, uri);
19
- }
20
-
21
- module.exports = { resolveInstanceId };
22
-
@@ -1,34 +0,0 @@
1
- /**
2
- * 通用规范化工具
3
- * - normalizeProjection: 将数组形式的投影转换为对象形式;对象原样返回;其他为 undefined。
4
- * - normalizeSort: 仅当为对象时返回;否则 undefined。
5
- */
6
-
7
- /**
8
- * 规范化投影参数
9
- * @param {string[]|Record<string, any>|undefined} p
10
- * @returns {Record<string, 1>|undefined}
11
- */
12
- function normalizeProjection(p) {
13
- if (!p) return undefined;
14
- if (Array.isArray(p)) {
15
- const obj = {};
16
- for (const k of p) {
17
- if (typeof k === 'string') obj[k] = 1;
18
- }
19
- return Object.keys(obj).length ? obj : undefined;
20
- }
21
- return (p && typeof p === 'object') ? p : undefined;
22
- }
23
-
24
- /**
25
- * 规范化排序参数
26
- * @param {Record<string, 1|-1>|undefined} s
27
- * @returns {Record<string, 1|-1>|undefined}
28
- */
29
- function normalizeSort(s) {
30
- return (s && typeof s === 'object') ? s : undefined;
31
- }
32
-
33
- module.exports = { normalizeProjection, normalizeSort };
34
-
@@ -1,43 +0,0 @@
1
- /**
2
- * 通用分页结果计算
3
- * - 采用 limit+1 探测是否有更多,然后裁剪为 `items` 与 `pageInfo`。
4
- * - 游标生成委托给适配器提供的 `pickAnchor`(用于从行/文档提取锚点字段值)。
5
- */
6
-
7
- const { encodeCursor } = require('./cursor');
8
-
9
- /**
10
- * 生成分页结果
11
- * @param {any[]} rows - 后端返回的数组(长度可能为 limit 或 limit+1)
12
- * @param {object} ctx
13
- * @param {number} ctx.limit
14
- * @param {Record<string,1|-1>} ctx.stableSort
15
- * @param {'after'|'before'|null} ctx.direction
16
- * @param {boolean} ctx.hasCursor
17
- * @param {(doc:any, sort:Record<string,1|-1>)=>Record<string,any>} ctx.pickAnchor
18
- * @returns {{ items:any[], pageInfo:{ hasNext:boolean, hasPrev:boolean, startCursor:string|null, endCursor:string|null } }}
19
- */
20
- function makePageResult(rows, { limit, stableSort, direction, hasCursor, pickAnchor }) {
21
- const hasMore = rows.length > limit;
22
- const items = hasMore ? rows.slice(0, limit) : rows;
23
-
24
- const first = items[0] || null;
25
- const last = items[items.length - 1] || null;
26
-
27
- // 修复:游标不应该包含方向信息,方向由使用游标的参数(after/before)决定
28
- // 游标只是一个位置标记,不包含使用方向
29
- const makeCur = (doc) => encodeCursor({ v: 1, s: stableSort, a: pickAnchor(doc, stableSort) });
30
-
31
- return {
32
- items,
33
- pageInfo: {
34
- hasNext: direction === 'before' ? Boolean(hasCursor) : hasMore,
35
- hasPrev: direction === 'before' ? hasMore : Boolean(hasCursor),
36
- startCursor: first ? makeCur(first) : null,
37
- endCursor: last ? makeCur(last) : null,
38
- }
39
- };
40
- }
41
-
42
- module.exports = { makePageResult };
43
-
@@ -1,57 +0,0 @@
1
- const CacheFactory = require('../cache');
2
- const { withSlowQueryLog } = require('./log');
3
-
4
- /**
5
- * 统一执行器:包装缓存与慢日志,并可选返回 meta(耗时等)与发出 query 事件
6
- * @param {import('../..').CacheLike|any} cache
7
- * @param {{iid:string, type:string, db:string, collection:string}} nsAll
8
- * @param {import('../..').LoggerLike} logger
9
- * @param {object} defaults
10
- * @param {{ keyBuilder?: (op:string, options:any)=>any, slowLogShaper?: (options:any)=>object, onSlowQueryEmit?: (meta:any)=>void, onQueryEmit?: (meta:any)=>void }} hooks
11
- */
12
- function createCachedRunner(cache, nsAll, logger, defaults, hooks = {}) {
13
- const cached = CacheFactory.createCachedReader(cache, nsAll);
14
- return async (op, options = {}, exec) => {
15
- const optsForKey = typeof hooks.keyBuilder === 'function' ? hooks.keyBuilder(op, options || {}) : (options || {});
16
- const runExec = () => cached(op, optsForKey, exec);
17
- const ns = { db: nsAll.db, coll: nsAll.collection, iid: nsAll.iid, type: nsAll.type };
18
- const t0 = Date.now();
19
- let res;
20
- let err;
21
- try {
22
- res = await withSlowQueryLog(logger, defaults, op, ns, options, runExec, hooks.slowLogShaper, hooks.onSlowQueryEmit);
23
- } catch (e) {
24
- err = e;
25
- throw e;
26
- } finally {
27
- const endTs = Date.now();
28
- const durationMs = endTs - t0;
29
- const wantMeta = !!options.meta;
30
- const meta = {
31
- op,
32
- ns,
33
- startTs: endTs - durationMs,
34
- endTs,
35
- durationMs,
36
- maxTimeMS: options && options.maxTimeMS,
37
- };
38
- if (err) meta.error = { code: err.code, message: String(err && (err.message || err)) };
39
- // emit query event if enabled
40
- if (defaults && defaults.metrics && defaults.metrics.emitQueryEvent && typeof hooks.onQueryEmit === 'function') {
41
- try { hooks.onQueryEmit(meta); } catch (_) {}
42
- }
43
- if (wantMeta) {
44
- // 按方法统一返回:findPage 在对象上挂 meta,其它读 API 返回 { data, meta }
45
- if (op === 'findPage' && res && typeof res === 'object') {
46
- res.meta = meta;
47
- } else {
48
- res = { data: res, meta };
49
- }
50
- }
51
- }
52
- return res;
53
- };
54
- }
55
-
56
- module.exports = { createCachedRunner };
57
-
@@ -1,232 +0,0 @@
1
- /**
2
- * MongoDB Server 特性探测模块
3
- * 检测 MongoDB Server 版本和支持的特性
4
- *
5
- * 使用方式:
6
- * ```javascript
7
- * const ServerFeatures = require('./lib/common/server-features');
8
- *
9
- * // 方式 1: 传入 monSQLize 实例
10
- * const msq = new MonSQLize({ ... });
11
- * await msq.connect();
12
- * const features = new ServerFeatures(msq);
13
- * const report = await features.generateFeatureReport();
14
- *
15
- * // 方式 2: 传入原生 MongoDB client
16
- * const { MongoClient } = require('mongodb');
17
- * const client = await MongoClient.connect(uri);
18
- * const features = new ServerFeatures(client);
19
- * ```
20
- *
21
- * @module lib/common/server-features
22
- */
23
-
24
- /**
25
- * Server 特性探测类
26
- */
27
- class ServerFeatures {
28
- constructor(clientOrInstance) {
29
- // 智能识别输入类型
30
- if (clientOrInstance._adapter && clientOrInstance._adapter.client) {
31
- // monSQLize 实例(有 _adapter 属性)
32
- this.client = clientOrInstance._adapter.client;
33
- } else if (clientOrInstance.client && typeof clientOrInstance.client.db === 'function') {
34
- // adapter 实例(有 client 属性)
35
- this.client = clientOrInstance.client;
36
- } else if (typeof clientOrInstance.db === 'function') {
37
- // 原生 MongoDB client
38
- this.client = clientOrInstance;
39
- } else {
40
- // 无法识别的类型
41
- throw new Error('ServerFeatures: Invalid client type. Expected MongoClient, adapter, or monSQLize instance.');
42
- }
43
-
44
- this.serverVersion = null;
45
- this.serverInfo = null;
46
- }
47
-
48
- /**
49
- * 获取 MongoDB Server 版本信息
50
- * @returns {Promise<Object>} 版本信息
51
- */
52
- async getServerInfo() {
53
- if (this.serverInfo) {
54
- return this.serverInfo;
55
- }
56
-
57
- try {
58
- const admin = this.client.db().admin();
59
- const buildInfo = await admin.buildInfo();
60
-
61
- this.serverInfo = {
62
- version: buildInfo.version,
63
- versionArray: buildInfo.versionArray,
64
- gitVersion: buildInfo.gitVersion,
65
- bits: buildInfo.bits,
66
- maxBsonObjectSize: buildInfo.maxBsonObjectSize,
67
- };
68
-
69
- this.serverVersion = this.serverInfo.versionArray;
70
-
71
- return this.serverInfo;
72
- } catch (error) {
73
- console.error('获取 Server 信息失败:', error);
74
- return null;
75
- }
76
- }
77
-
78
- /**
79
- * 获取主版本号
80
- * @returns {Promise<number>} 主版本号
81
- */
82
- async getMajorVersion() {
83
- if (!this.serverVersion) {
84
- await this.getServerInfo();
85
- }
86
- return this.serverVersion ? this.serverVersion[0] : 0;
87
- }
88
-
89
- /**
90
- * 获取次版本号
91
- * @returns {Promise<number>} 次版本号
92
- */
93
- async getMinorVersion() {
94
- if (!this.serverVersion) {
95
- await this.getServerInfo();
96
- }
97
- return this.serverVersion ? this.serverVersion[1] : 0;
98
- }
99
-
100
- /**
101
- * 检查是否支持事务
102
- * @returns {Promise<boolean>}
103
- */
104
- async supportsTransactions() {
105
- const major = await this.getMajorVersion();
106
- // MongoDB 4.0+ 支持事务(副本集)
107
- return major >= 4;
108
- }
109
-
110
- /**
111
- * 检查是否支持多文档事务
112
- * @returns {Promise<boolean>}
113
- */
114
- async supportsMultiDocumentTransactions() {
115
- const major = await this.getMajorVersion();
116
- // MongoDB 4.0+ 副本集支持,4.2+ 分片集群支持
117
- return major >= 4;
118
- }
119
-
120
- /**
121
- * 检查是否支持通配符索引
122
- * @returns {Promise<boolean>}
123
- */
124
- async supportsWildcardIndexes() {
125
- const major = await this.getMajorVersion();
126
- const minor = await this.getMinorVersion();
127
- // MongoDB 4.2+ 支持通配符索引
128
- return major > 4 || (major === 4 && minor >= 2);
129
- }
130
-
131
- /**
132
- * 检查是否支持 $function 操作符
133
- * @returns {Promise<boolean>}
134
- */
135
- async supportsFunctionOperator() {
136
- const major = await this.getMajorVersion();
137
- const minor = await this.getMinorVersion();
138
- // MongoDB 4.4+ 支持 $function
139
- return major > 4 || (major === 4 && minor >= 4);
140
- }
141
-
142
- /**
143
- * 检查是否支持 $setWindowFields 操作符
144
- * @returns {Promise<boolean>}
145
- */
146
- async supportsSetWindowFields() {
147
- const major = await this.getMajorVersion();
148
- // MongoDB 5.0+ 支持 $setWindowFields
149
- return major >= 5;
150
- }
151
-
152
- /**
153
- * 检查是否支持时间序列集合
154
- * @returns {Promise<boolean>}
155
- */
156
- async supportsTimeSeriesCollections() {
157
- const major = await this.getMajorVersion();
158
- // MongoDB 5.0+ 支持时间序列集合
159
- return major >= 5;
160
- }
161
-
162
- /**
163
- * 检查是否支持加密字段
164
- * @returns {Promise<boolean>}
165
- */
166
- async supportsEncryptedFields() {
167
- const major = await this.getMajorVersion();
168
- // MongoDB 6.0+ 支持 Queryable Encryption
169
- return major >= 6;
170
- }
171
-
172
- /**
173
- * 检查是否支持聚合表达式
174
- * @param {string} expression - 表达式名称(如 '$dateAdd')
175
- * @returns {Promise<boolean>}
176
- */
177
- async supportsAggregationExpression(expression) {
178
- const major = await this.getMajorVersion();
179
- const minor = await this.getMinorVersion();
180
-
181
- // 常见聚合表达式的版本要求
182
- const expressionVersions = {
183
- '$function': { major: 4, minor: 4 },
184
- '$setWindowFields': { major: 5, minor: 0 },
185
- '$dateAdd': { major: 5, minor: 0 },
186
- '$dateDiff': { major: 5, minor: 0 },
187
- '$dateSubtract': { major: 5, minor: 0 },
188
- '$dateTrunc': { major: 5, minor: 0 },
189
- '$getField': { major: 5, minor: 0 },
190
- '$setField': { major: 5, minor: 0 },
191
- };
192
-
193
- const required = expressionVersions[expression];
194
- if (!required) {
195
- // 未知表达式,假设支持
196
- return true;
197
- }
198
-
199
- return major > required.major ||
200
- (major === required.major && minor >= required.minor);
201
- }
202
-
203
- /**
204
- * 生成特性支持报告
205
- * @returns {Promise<Object>} 特性报告
206
- */
207
- async generateFeatureReport() {
208
- const info = await this.getServerInfo();
209
-
210
- return {
211
- serverVersion: info ? info.version : 'Unknown',
212
- features: {
213
- transactions: await this.supportsTransactions(),
214
- multiDocumentTransactions: await this.supportsMultiDocumentTransactions(),
215
- wildcardIndexes: await this.supportsWildcardIndexes(),
216
- functionOperator: await this.supportsFunctionOperator(),
217
- setWindowFields: await this.supportsSetWindowFields(),
218
- timeSeriesCollections: await this.supportsTimeSeriesCollections(),
219
- encryptedFields: await this.supportsEncryptedFields(),
220
- },
221
- aggregationExpressions: {
222
- '$function': await this.supportsAggregationExpression('$function'),
223
- '$setWindowFields': await this.supportsAggregationExpression('$setWindowFields'),
224
- '$dateAdd': await this.supportsAggregationExpression('$dateAdd'),
225
- },
226
- };
227
- }
228
- }
229
-
230
- module.exports = ServerFeatures;
231
-
232
-