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,568 +0,0 @@
1
- /**
2
- * PopulateBuilder - populate 构建器
3
- *
4
- * 职责:
5
- * - 管理 populate 路径配置
6
- * - 执行关联数据填充
7
- * - 处理批量查询优化
8
- *
9
- * @class PopulateBuilder
10
- */
11
- class PopulateBuilder {
12
- /**
13
- * 构造函数
14
- * @param {Model} model - 所属的 Model 实例
15
- * @param {Collection} collection - monSQLize collection 实例
16
- */
17
- constructor(model, collection) {
18
- this.model = model;
19
- this.collection = collection;
20
- this.populatePaths = []; // 待填充的路径
21
- }
22
-
23
- /**
24
- * 添加 populate 路径
25
- * @param {string|Array|Object} path - 路径或配置对象
26
- * @param {Object} [options={}] - populate 选项
27
- * @returns {PopulateBuilder} 返回自身,支持链式调用
28
- */
29
- populate(path, options = {}) {
30
- if (Array.isArray(path)) {
31
- // 数组形式:populate(['profile', 'posts'])
32
- path.forEach(p => this.populate(p, options));
33
- return this;
34
- }
35
-
36
- if (typeof path === 'object' && path.path) {
37
- // 对象形式:populate({ path: 'posts', select: '...' })
38
- this.populatePaths.push(path);
39
- } else if (typeof path === 'string') {
40
- // 字符串形式:populate('profile')
41
- this.populatePaths.push({ path, ...options });
42
- } else {
43
- throw new Error('populate 参数必须是字符串、数组或对象');
44
- }
45
-
46
- return this;
47
- }
48
-
49
- /**
50
- * 执行 populate(填充关联数据)
51
- * @param {Array} docs - 查询结果文档
52
- * @returns {Promise<Array>} 填充后的文档
53
- */
54
- async execute(docs) {
55
- // 如果没有文档或没有 populate 路径,直接返回
56
- if (!docs || docs.length === 0) return docs;
57
- if (this.populatePaths.length === 0) return docs;
58
-
59
- // 按顺序执行每个 populate 路径
60
- for (const populateConfig of this.populatePaths) {
61
- docs = await this._populatePath(docs, populateConfig);
62
- }
63
-
64
- return docs;
65
- }
66
-
67
- /**
68
- * 填充单个路径(核心逻辑)
69
- * @private
70
- * @param {Array} docs - 文档数组
71
- * @param {Object} config - populate 配置
72
- * @returns {Promise<Array>}
73
- */
74
- async _populatePath(docs, config) {
75
- const { path, select, sort, limit, skip, match, populate: nestedPopulate } = config;
76
-
77
- // 1. 获取关系定义
78
- const relation = this.model._relations.get(path);
79
- if (!relation) {
80
- throw new Error(`未定义的关系: ${path}`);
81
- }
82
-
83
- // 2. 收集外键值
84
- const foreignIds = this._collectForeignIds(docs, relation);
85
- if (foreignIds.length === 0) {
86
- // 没有外键值,填充 null 或空数组
87
- this._fillEmptyRelation(docs, path, relation);
88
- return docs;
89
- }
90
-
91
- // 3. 获取关联的集合
92
- const relatedCollection = this.model.msq.collection(relation.from);
93
-
94
- // 4. 构建查询条件
95
- const query = { [relation.foreignField]: { $in: foreignIds } };
96
- if (match) {
97
- Object.assign(query, match);
98
- }
99
-
100
- // 5. 查询关联文档
101
- let relatedDocs = await relatedCollection.find(query).toArray();
102
-
103
- // 6. 🔴 处理 limit=0 的特殊情况:返回空结果
104
- if (limit === 0) {
105
- this._fillEmptyRelation(docs, path, relation);
106
- return docs;
107
- }
108
-
109
- // 7. 🆕 处理嵌套 populate(深度填充)
110
- if (nestedPopulate && relatedDocs.length > 0) {
111
- relatedDocs = await this._executeNestedPopulate(
112
- relatedDocs,
113
- nestedPopulate,
114
- relation.from
115
- );
116
- }
117
-
118
- // 8. 应用选项(修复:select时保留外键字段)
119
- if (select) {
120
- relatedDocs = relatedDocs.map(doc => this._selectFields(doc, select, relation.foreignField));
121
- }
122
- if (sort) {
123
- relatedDocs = this._sortDocs(relatedDocs, sort);
124
- }
125
- if (skip || limit) {
126
- const startIndex = skip || 0;
127
- const endIndex = limit ? startIndex + limit : relatedDocs.length;
128
- relatedDocs = relatedDocs.slice(startIndex, endIndex);
129
- }
130
-
131
- // 9. 构建映射表
132
- const relatedMap = this._buildRelationMap(relatedDocs, relation);
133
-
134
- // 10. 填充文档
135
- this._fillDocuments(docs, path, relation, relatedMap);
136
-
137
- return docs;
138
- }
139
-
140
- /**
141
- * 收集外键值
142
- * @private
143
- * @param {Array} docs - 文档数组
144
- * @param {Object} relation - 关系定义
145
- * @returns {Array} 外键值数组(去重)
146
- */
147
- _collectForeignIds(docs, relation) {
148
- const ids = new Set();
149
-
150
- for (const doc of docs) {
151
- const localValue = doc[relation.localField];
152
-
153
- if (localValue === null || localValue === undefined) {
154
- continue;
155
- }
156
-
157
- if (Array.isArray(localValue)) {
158
- // 外键数组
159
- localValue.forEach(id => {
160
- if (id !== null && id !== undefined) {
161
- ids.add(String(id));
162
- }
163
- });
164
- } else {
165
- // 单个外键
166
- ids.add(String(localValue));
167
- }
168
- }
169
-
170
- return Array.from(ids);
171
- }
172
-
173
- /**
174
- * 构建关系映射表
175
- * @private
176
- * @param {Array} relatedDocs - 关联文档数组
177
- * @param {Object} relation - 关系定义
178
- * @returns {Map} 映射表
179
- */
180
- _buildRelationMap(relatedDocs, relation) {
181
- const map = new Map();
182
-
183
- for (const doc of relatedDocs) {
184
- const key = String(doc[relation.foreignField]);
185
-
186
- if (relation.single) {
187
- // single: true - 单文档,直接存储
188
- map.set(key, doc);
189
- } else {
190
- // single: false - 数组,追加存储
191
- if (!map.has(key)) {
192
- map.set(key, []);
193
- }
194
- map.get(key).push(doc);
195
- }
196
- }
197
-
198
- return map;
199
- }
200
-
201
- /**
202
- * 填充文档
203
- * @private
204
- * @param {Array} docs - 文档数组
205
- * @param {string} path - 填充路径
206
- * @param {Object} relation - 关系定义
207
- * @param {Map} relatedMap - 关系映射表
208
- */
209
- _fillDocuments(docs, path, relation, relatedMap) {
210
- for (const doc of docs) {
211
- const localValue = doc[relation.localField];
212
-
213
- if (localValue === null || localValue === undefined) {
214
- // 外键为空,填充 null 或空数组
215
- doc[path] = relation.single ? null : [];
216
- continue;
217
- }
218
-
219
- if (relation.single) {
220
- // single: true - 返回单文档
221
- const key = String(localValue);
222
- doc[path] = relatedMap.get(key) || null;
223
- } else {
224
- // single: false - 返回数组
225
- const keys = Array.isArray(localValue)
226
- ? localValue.map(String)
227
- : [String(localValue)];
228
-
229
- doc[path] = [];
230
- for (const key of keys) {
231
- const related = relatedMap.get(key);
232
- if (related) {
233
- if (Array.isArray(related)) {
234
- doc[path].push(...related);
235
- } else {
236
- doc[path].push(related);
237
- }
238
- }
239
- }
240
- }
241
- }
242
- }
243
-
244
- /**
245
- * 填充空关系
246
- * @private
247
- * @param {Array} docs - 文档数组
248
- * @param {string} path - 填充路径
249
- * @param {Object} relation - 关系定义
250
- */
251
- _fillEmptyRelation(docs, path, relation) {
252
- for (const doc of docs) {
253
- doc[path] = relation.single ? null : [];
254
- }
255
- }
256
-
257
- /**
258
- * 选择字段
259
- * @private
260
- * @param {Object} doc - 文档对象
261
- * @param {string} select - 字段选择器(空格分隔)
262
- * @param {string} [keepField] - 必须保留的字段(如外键字段)
263
- * @returns {Object} 选择后的文档
264
- */
265
- _selectFields(doc, select, keepField) {
266
- const fields = select.split(/\s+/).filter(f => f);
267
- const result = {};
268
-
269
- // 始终包含 _id
270
- if (doc._id !== undefined) {
271
- result._id = doc._id;
272
- }
273
-
274
- for (const field of fields) {
275
- if (doc[field] !== undefined) {
276
- result[field] = doc[field];
277
- }
278
- }
279
-
280
- // 🔴 保留外键字段(用于构建关系映射)
281
- if (keepField && doc[keepField] !== undefined && !fields.includes(keepField)) {
282
- result[keepField] = doc[keepField];
283
- }
284
-
285
- return result;
286
- }
287
-
288
- /**
289
- * 排序文档
290
- * @private
291
- * @param {Array} docs - 文档数组
292
- * @param {Object} sort - 排序规则
293
- * @returns {Array} 排序后的文档数组
294
- */
295
- _sortDocs(docs, sort) {
296
- return docs.slice().sort((a, b) => {
297
- for (const [field, order] of Object.entries(sort)) {
298
- const aVal = a[field];
299
- const bVal = b[field];
300
-
301
- if (aVal < bVal) return order === 1 ? -1 : 1;
302
- if (aVal > bVal) return order === 1 ? 1 : -1;
303
- }
304
- return 0;
305
- });
306
- }
307
-
308
- /**
309
- * 执行嵌套 populate(深度填充)
310
- * @private
311
- * @param {Array} docs - 关联文档数组
312
- * @param {string|Array|Object} nestedPopulate - 嵌套 populate 配置
313
- * @param {string} collectionName - 当前集合名称
314
- * @returns {Promise<Array>} 填充后的文档
315
- */
316
- async _executeNestedPopulate(docs, nestedPopulate, collectionName) {
317
- // 1. 获取当前集合对应的 Model 定义
318
- const Model = require('../../model');
319
- if (!Model.has(collectionName)) {
320
- // 集合没有定义 Model,跳过嵌套 populate(不报错,也不添加字段)
321
- return docs;
322
- }
323
-
324
- // 2. 创建临时 ModelInstance 用于嵌套 populate
325
- const modelDef = Model.get(collectionName);
326
- const collection = this.model.msq.collection(collectionName);
327
- const { ModelInstance } = require('../index');
328
- const tempModel = new ModelInstance(collection, modelDef.definition, this.model.msq);
329
-
330
- // 3. 验证嵌套 populate 配置类型
331
- if (
332
- typeof nestedPopulate !== 'string' &&
333
- !Array.isArray(nestedPopulate) &&
334
- !(typeof nestedPopulate === 'object' && nestedPopulate.path)
335
- ) {
336
- throw new Error('嵌套 populate 参数必须是字符串、数组或对象');
337
- }
338
-
339
- // 4. 创建新的 PopulateBuilder
340
- const nestedBuilder = new PopulateBuilder(tempModel, collection);
341
-
342
- // 5. 添加嵌套 populate 路径(此时会验证关系是否存在)
343
- if (Array.isArray(nestedPopulate)) {
344
- // 数组形式:populate: ['comments', 'likes']
345
- nestedPopulate.forEach(p => nestedBuilder.populate(p));
346
- } else if (typeof nestedPopulate === 'object' && nestedPopulate.path) {
347
- // 对象形式:populate: { path: 'comments', select: '...' }
348
- nestedBuilder.populate(nestedPopulate);
349
- } else if (typeof nestedPopulate === 'string') {
350
- // 字符串形式:populate: 'comments'
351
- nestedBuilder.populate(nestedPopulate);
352
- }
353
-
354
- // 6. 执行嵌套 populate
355
- const populatedDocs = await nestedBuilder.execute(docs);
356
-
357
- return populatedDocs;
358
- }
359
- }
360
-
361
- /**
362
- * PopulateProxy - populate 代理类
363
- *
364
- * 职责:
365
- * - 提供链式 populate 调用接口
366
- * - 实现 Promise 接口(then/catch)
367
- * - 处理单文档和数组文档的返回
368
- *
369
- * @class PopulateProxy
370
- */
371
- class PopulateProxy {
372
- /**
373
- * 构造函数
374
- * @param {Array|Promise<Array>} docs - 文档数组或返回文档数组的 Promise
375
- * @param {PopulateBuilder} builder - PopulateBuilder 实例
376
- * @param {boolean} [singleDoc=false] - 是否返回单文档
377
- */
378
- constructor(docs, builder, singleDoc = false) {
379
- this._docsOrPromise = docs;
380
- this._builder = builder;
381
- this._singleDoc = singleDoc;
382
- }
383
-
384
- /**
385
- * 获取文档数组(如果是 Promise 则先 await)
386
- * @private
387
- * @returns {Promise<Array>}
388
- */
389
- async _getDocs() {
390
- // 如果是 Promise,先 await
391
- if (this._docsOrPromise && typeof this._docsOrPromise.then === 'function') {
392
- const result = await this._docsOrPromise;
393
- // 标准化为数组
394
- return result === null ? [] : (Array.isArray(result) ? result : [result]);
395
- }
396
- // 否则直接返回
397
- return this._docsOrPromise;
398
- }
399
-
400
- /**
401
- * 添加 populate 路径(链式调用)
402
- * @param {string|Array|Object} path - 路径或配置对象
403
- * @param {Object} [options={}] - populate 选项
404
- * @returns {PopulateProxy} 返回自身,支持链式调用
405
- */
406
- populate(path, options = {}) {
407
- this._builder.populate(path, options);
408
- return this; // 返回自己,支持链式调用
409
- }
410
-
411
- /**
412
- * Promise then 接口
413
- * @param {Function} resolve - 成功回调
414
- * @param {Function} reject - 失败回调
415
- * @returns {Promise}
416
- */
417
- async then(resolve, reject) {
418
- try {
419
- // 获取文档(如果是 Promise 则先 await)
420
- const docs = await this._getDocs();
421
-
422
- // 执行 populate
423
- const populatedDocs = await this._builder.execute(docs);
424
-
425
- // 如果是单文档查询(findOne),返回第一个元素或 null
426
- // 如果是批量查询(find),返回数组
427
- const result = this._singleDoc ? (populatedDocs[0] || null) : populatedDocs;
428
-
429
- return resolve(result);
430
- } catch (error) {
431
- return reject ? reject(error) : Promise.reject(error);
432
- }
433
- }
434
-
435
- /**
436
- * Promise catch 接口
437
- * @param {Function} reject - 失败回调
438
- * @returns {Promise}
439
- */
440
- catch(reject) {
441
- return this.then(result => result, reject);
442
- }
443
-
444
- /**
445
- * Promise finally 接口
446
- * @param {Function} onFinally - finally 回调
447
- * @returns {Promise}
448
- */
449
- finally(onFinally) {
450
- return this.then(
451
- result => {
452
- onFinally();
453
- return result;
454
- },
455
- error => {
456
- onFinally();
457
- throw error;
458
- }
459
- );
460
- }
461
- }
462
-
463
- /**
464
- * SpecialPopulateProxy - 用于 findAndCount 和 findPage 的特殊 PopulateProxy
465
- *
466
- * 这些方法返回特殊结构:
467
- * - findAndCount: { data: [...], total: 100 }
468
- * - findPage: { data: [...], page: 1, pageSize: 10, total: 100, hasNext: true }
469
- *
470
- * 需要只对 data 部分进行 populate,保持其他字段不变
471
- *
472
- * @class SpecialPopulateProxy
473
- */
474
- class SpecialPopulateProxy {
475
- /**
476
- * 构造函数
477
- * @param {Promise} queryPromise - 返回特殊结构的查询 Promise
478
- * @param {PopulateBuilder} builder - PopulateBuilder 实例
479
- * @param {string} method - 方法名(findAndCount 或 findPage)
480
- */
481
- constructor(queryPromise, builder, method) {
482
- this._queryPromise = queryPromise;
483
- this._builder = builder;
484
- this._method = method;
485
- }
486
-
487
- /**
488
- * 添加 populate 路径(链式调用)
489
- * @param {string|Array|Object} path - 路径或配置对象
490
- * @param {Object} [options={}] - populate 选项
491
- * @returns {SpecialPopulateProxy} 返回自身,支持链式调用
492
- */
493
- populate(path, options = {}) {
494
- this._builder.populate(path, options);
495
- return this; // 返回自己,支持链式调用
496
- }
497
-
498
- /**
499
- * Promise then 接口
500
- * @param {Function} resolve - 成功回调
501
- * @param {Function} reject - 失败回调
502
- * @returns {Promise}
503
- */
504
- async then(resolve, reject) {
505
- try {
506
- // 1. 获取查询结果(特殊结构)
507
- const result = await this._queryPromise;
508
-
509
- // 2. 提取数据部分(智能检测字段名)
510
- // 优先检查实际存在的字段,以支持mock数据和真实数据
511
- let dataField, data;
512
- if (result.items !== undefined) {
513
- // 真实的findPage返回 { items: [...], pageInfo: {...}, totals: {...} }
514
- dataField = 'items';
515
- data = result.items || [];
516
- } else {
517
- // findAndCount或mock数据返回 { data: [...], total: 100 }
518
- // 兜底:如果没有items,使用data字段(即使不存在也没关系)
519
- dataField = 'data';
520
- data = result.data || [];
521
- }
522
-
523
- // 3. 对数据部分执行 populate
524
- const populatedData = await this._builder.execute(data);
525
-
526
- // 4. 重新组装结果(保持原结构,只替换数据字段)
527
- const finalResult = {
528
- ...result,
529
- [dataField]: populatedData
530
- };
531
-
532
- return resolve(finalResult);
533
- } catch (error) {
534
- return reject ? reject(error) : Promise.reject(error);
535
- }
536
- }
537
-
538
- /**
539
- * Promise catch 接口
540
- * @param {Function} reject - 失败回调
541
- * @returns {Promise}
542
- */
543
- catch(reject) {
544
- return this.then(result => result, reject);
545
- }
546
-
547
- /**
548
- * Promise finally 接口
549
- * @param {Function} onFinally - finally 回调
550
- * @returns {Promise}
551
- */
552
- finally(onFinally) {
553
- return this.then(
554
- result => {
555
- onFinally();
556
- return result;
557
- },
558
- error => {
559
- onFinally();
560
- throw error;
561
- }
562
- );
563
- }
564
- }
565
-
566
- module.exports = { PopulateBuilder, PopulateProxy, SpecialPopulateProxy };
567
-
568
-
@@ -1,120 +0,0 @@
1
- /**
2
- * RelationManager - 关系定义管理器
3
- *
4
- * 职责:
5
- * - 注册和管理 Model 之间的关系定义
6
- * - 验证关系配置的正确性
7
- * - 提供关系查询接口
8
- *
9
- * @class RelationManager
10
- */
11
- class RelationManager {
12
- /**
13
- * 构造函数
14
- * @param {Model} model - 所属的 Model 实例
15
- */
16
- constructor(model) {
17
- this.model = model;
18
- this.relations = new Map(); // 关系定义缓存
19
- }
20
-
21
- /**
22
- * 注册关系定义
23
- * @param {string} name - 关系名称
24
- * @param {Object} config - 关系配置
25
- * @param {string} config.from - 关联的集合名称(MongoDB 原生)
26
- * @param {string} config.localField - 本地字段名
27
- * @param {string} config.foreignField - 外部字段名
28
- * @param {boolean} [config.single=false] - 是否返回单个文档
29
- */
30
- define(name, config) {
31
- this.validate(config); // 验证配置
32
- this.relations.set(name, this.normalize(config));
33
- }
34
-
35
- /**
36
- * 标准化配置(设置默认值)
37
- * @param {Object} config - 原始配置
38
- * @returns {Object} 标准化后的配置
39
- */
40
- normalize(config) {
41
- return {
42
- from: config.from,
43
- localField: config.localField,
44
- foreignField: config.foreignField,
45
- single: config.single !== undefined ? config.single : false // 默认返回数组
46
- };
47
- }
48
-
49
- /**
50
- * 获取关系定义
51
- * @param {string} name - 关系名称
52
- * @returns {Object|null} 关系配置,不存在返回 null
53
- */
54
- get(name) {
55
- return this.relations.get(name) || null;
56
- }
57
-
58
- /**
59
- * 验证关系配置
60
- * @param {Object} config - 关系配置
61
- * @throws {Error} 配置不合法时抛出错误
62
- */
63
- validate(config) {
64
- // 验证必需字段
65
- const required = ['from', 'localField', 'foreignField'];
66
- for (const field of required) {
67
- if (!config[field]) {
68
- throw new Error(`relations 配置缺少必需字段: ${field}`);
69
- }
70
- }
71
-
72
- // 验证 from 必须是字符串(集合名)
73
- if (typeof config.from !== 'string') {
74
- throw new Error('relations.from 必须是字符串(集合名称)');
75
- }
76
-
77
- // 验证 localField 和 foreignField 必须是字符串
78
- if (typeof config.localField !== 'string') {
79
- throw new Error('relations.localField 必须是字符串');
80
- }
81
-
82
- if (typeof config.foreignField !== 'string') {
83
- throw new Error('relations.foreignField 必须是字符串');
84
- }
85
-
86
- // 验证 single 必须是布尔值(如果提供)
87
- if (config.single !== undefined && typeof config.single !== 'boolean') {
88
- throw new Error('relations.single 必须是布尔值');
89
- }
90
- }
91
-
92
- /**
93
- * 获取所有关系定义
94
- * @returns {Map} 所有关系定义
95
- */
96
- getAll() {
97
- return this.relations;
98
- }
99
-
100
- /**
101
- * 检查关系是否存在
102
- * @param {string} name - 关系名称
103
- * @returns {boolean}
104
- */
105
- has(name) {
106
- return this.relations.has(name);
107
- }
108
-
109
- /**
110
- * 获取所有关系名称
111
- * @returns {string[]}
112
- */
113
- getNames() {
114
- return Array.from(this.relations.keys());
115
- }
116
- }
117
-
118
- module.exports = RelationManager;
119
-
120
-