monsqlize 1.2.2 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # 变更日志 (CHANGELOG)
2
2
 
3
3
  > **说明**: 版本概览摘要,详细变更见 [changelogs/](./changelogs/) 目录
4
- > **最后更新**: 2026-04-24
4
+ > **最后更新**: 2026-04-27
5
5
 
6
6
  ---
7
7
 
@@ -9,6 +9,8 @@
9
9
 
10
10
  | 版本 | 日期 | 变更摘要 | 详细 |
11
11
  |------|------|---------|------|
12
+ | [v1.3.0](./changelogs/v1.3.0.md) | 2026-04-27 | 🆕 **新功能**:链式池访问 API — `pool(name)` / `use(dbName)` / `scopedCollection()` / `scopedModel()` 四个公开方法,支持多连接池多数据库路由,connection 合并语义,TypeScript 类型签名完整覆盖 | [查看](./changelogs/v1.3.0.md) |
13
+ | [v1.2.3](./changelogs/v1.2.3.md) | 2026-04-26 | 🐛 **Bug Fix**:`model()` 方法修复注册 key 直接作为 MongoDB 集合名的问题,优先读 `definition.collection > definition.name`,注册 key 仅作 fallback,向后兼容 | [查看](./changelogs/v1.2.3.md) |
12
14
  | [v1.2.2](./changelogs/v1.2.2.md) | 2026-04-24 | 🆕 **新功能**:Model 数据源绑定 — `connection: { pool?, database? }` 支持 Model 绑定指定连接池和/或数据库,四种组合路由,向后兼容;🐛 **Bug Fix**:修复 `Model._clear()` 导致 `msq.model()` 实例缓存失效失效,populate 关系丢失问题 | [查看](./changelogs/v1.2.2.md) |
13
15
  | [v1.2.1](./changelogs/v1.2.1.md) | 2026-04-13 | 🐛 **Bug 修复**:`msq.model()` 实例缓存 + 索引去重 + 死代码清理 + `types/monsqlize.ts` 补全 `model()` / `collection()` 类型 | [查看](./changelogs/v1.2.1.md) |
14
16
  | [v1.2.0](./changelogs/v1.2.0.md) | 2026-04-13 | 🐛 **Bug 修复 + 新功能**:`findPage` 正式支持 `projection` 投影参数(修复静默忽略问题)+ 有效投影策略自动保护游标排序字段 + 8 个测试用例 | [查看](./changelogs/v1.2.0.md) |
package/README.md CHANGED
@@ -1465,27 +1465,27 @@ import type { Collection, MonSQLizeConfig } from 'monsqlize';
1465
1465
  ### 📦 MongoDB 原生功能
1466
1466
 
1467
1467
  ✅ **CRUD 操作**
1468
- - find / findOne
1469
- - insertOne / insertMany
1470
- - updateOne / updateMany ⭐ (支持聚合管道 v1.0.8+)
1471
- - deleteOne / deleteMany
1472
- - replaceOne
1473
- - findOneAndUpdate
1474
- - findOneAndReplace
1475
- - findOneAndDelete
1468
+ - [find](./docs/find.md) / [findOne](./docs/findOne.md)
1469
+ - [insertOne](./docs/insert-one.md) / [insertMany](./docs/insert-many.md)
1470
+ - [updateOne](./docs/update-one.md) / [updateMany](./docs/update-many.md) ⭐ (支持聚合管道 v1.0.8+)
1471
+ - [deleteOne](./docs/delete-one.md) / [deleteMany](./docs/delete-many.md)
1472
+ - [replaceOne](./docs/replace-one.md)
1473
+ - [findOneAndUpdate](./docs/find-one-and-update.md)
1474
+ - [findOneAndReplace](./docs/find-one-and-replace.md)
1475
+ - [findOneAndDelete](./docs/find-one-and-delete.md)
1476
1476
 
1477
1477
  ✅ **聚合 & 查询**
1478
- - aggregate
1479
- - count / distinct
1480
- - watch (Change Streams)
1481
- - explain
1478
+ - [aggregate](./docs/aggregate.md)
1479
+ - [count](./docs/count.md) / [distinct](./docs/distinct.md)
1480
+ - [watch (Change Streams)](./docs/watch.md)
1481
+ - [explain](./docs/explain.md)
1482
1482
 
1483
1483
  ✅ **索引管理**
1484
1484
  - createIndex / createIndexes
1485
1485
  - listIndexes
1486
1486
  - dropIndex / dropIndexes
1487
1487
 
1488
- **事务支持**
1488
+ **[事务支持](./docs/transaction.md)**
1489
1489
  - withTransaction
1490
1490
  - startTransaction
1491
1491
 
@@ -1494,44 +1494,44 @@ import type { Collection, MonSQLizeConfig } from 'monsqlize';
1494
1494
 
1495
1495
  ### 🚀 增强功能
1496
1496
 
1497
- **企业级多连接池** (v1.0.8+)
1497
+ **[企业级多连接池](./docs/multi-pool.md)** (v1.0.8+)
1498
1498
  - ConnectionPoolManager
1499
1499
  - 5种智能选择策略
1500
1500
  - 实时健康检查
1501
1501
  - 自动故障转移
1502
1502
  - 完整统计收集
1503
1503
 
1504
- ✅ **Saga 分布式事务** (v1.1.0 计划)
1504
+ ✅ **[Saga 分布式事务](./docs/saga-transaction.md)** (v1.1.0 计划)
1505
1505
  - 跨服务事务(设计完成)
1506
1506
  - 自动补偿机制(设计完成)
1507
1507
  - 状态跟踪(设计完成)
1508
1508
  - 超时和重试(设计完成)
1509
1509
 
1510
- **智能缓存**
1510
+ **[智能缓存](./docs/cache.md)**
1511
1511
  - TTL 过期策略
1512
1512
  - LRU 淘汰策略
1513
1513
  - 自动失效机制
1514
1514
  - 并发去重
1515
- - 多层缓存 (内存+Redis)
1515
+ - [多层缓存 (内存+Redis)](./docs/cache-implementation.md)
1516
1516
 
1517
1517
  ✅ **便利方法**
1518
- - findOneById
1519
- - findByIds
1520
- - upsertOne
1521
- - incrementOne
1522
- - findAndCount
1518
+ - [findOneById](./docs/find-one-by-id.md)
1519
+ - [findByIds](./docs/find-by-ids.md)
1520
+ - [upsertOne](./docs/upsert-one.md)
1521
+ - [incrementOne](./docs/increment-one.md)
1522
+ - [findAndCount](./docs/find-and-count.md)
1523
1523
 
1524
1524
  ✅ **性能优化**
1525
- - insertBatch - 批量插入优化
1526
- - deleteBatch - 批量删除(流式+进度监控)
1527
- - updateBatch - 批量更新(流式+进度监控)
1525
+ - [insertBatch](./docs/insertBatch.md) - 批量插入优化
1526
+ - [deleteBatch](./docs/deleteBatch.md) - 批量删除(流式+进度监控)
1527
+ - [updateBatch](./docs/updateBatch.md) - 批量更新(流式+进度监控)
1528
1528
  - 只读事务优化
1529
- - Count 队列控制
1529
+ - [Count 队列控制](./docs/count-queue.md)
1530
1530
  - 连接池管理
1531
1531
 
1532
- **分布式支持**
1532
+ **[分布式支持](./docs/distributed-deployment.md)**
1533
1533
  - Redis 广播缓存失效
1534
- - 分布式锁
1534
+ - [分布式锁](./docs/business-lock.md)
1535
1535
  - 多实例一致性
1536
1536
 
1537
1537
  </td>
@@ -1540,28 +1540,28 @@ import type { Collection, MonSQLizeConfig } from 'monsqlize';
1540
1540
  ### 🛠️ 企业级特性
1541
1541
 
1542
1542
  ✅ **运维监控**
1543
- - 慢查询日志(支持持久化存储)🆕
1543
+ - [慢查询日志](./docs/slow-query-log.md)(支持持久化存储)🆕
1544
1544
  - 性能指标统计
1545
1545
  - 健康检查
1546
1546
  - 缓存命中率监控
1547
1547
 
1548
- **深度分页**
1548
+ **[深度分页](./docs/findPage.md)**
1549
1549
  - 游标分页
1550
1550
  - 异步总数统计
1551
- - 书签管理
1551
+ - [书签管理](./docs/bookmarks.md)
1552
1552
  - 跳页优化
1553
1553
 
1554
1554
  ✅ **数据库管理**
1555
1555
  - 跨库访问
1556
- - Schema 验证
1557
- - 集合管理
1556
+ - [Schema 验证](./docs/model.md)
1557
+ - [集合管理](./docs/collection-management.md)
1558
1558
  - 数据库命令
1559
1559
 
1560
1560
  ✅ **开发体验**
1561
1561
  - TypeScript 支持
1562
- - 链式调用 API ⭐
1562
+ - [链式调用 API](./docs/chaining-api.md)
1563
1563
  - ESM/CommonJS 双模式
1564
- - ObjectId 自动转换 ⭐
1564
+ - [ObjectId 自动转换](./docs/objectid-auto-convert.md)
1565
1565
  - 77% 测试覆盖率
1566
1566
 
1567
1567
  </td>
package/lib/index.js CHANGED
@@ -737,11 +737,18 @@ module.exports = class {
737
737
  // 获取 Model 定义
738
738
  const modelDef = Model.get(collectionName);
739
739
 
740
+ // [v1.2.3 patch] 实际 MongoDB 集合名可能不同于注册 key
741
+ // 优先读 definition.collection > definition.name > key(fallback,向后兼容)
742
+ const actualCollectionName =
743
+ modelDef.definition.collection ||
744
+ modelDef.definition.name ||
745
+ collectionName;
746
+
740
747
  // 获取 collection 实例(v1.2.2+ 支持 connection 路由)
741
748
  const connection = modelDef.definition.connection;
742
749
  const collection = (connection && (connection.pool || connection.database))
743
- ? this._resolveModelCollection(collectionName, connection)
744
- : this.dbInstance.collection(collectionName);
750
+ ? this._resolveModelCollection(actualCollectionName, connection)
751
+ : this.dbInstance.collection(actualCollectionName);
745
752
 
746
753
  // 创建 ModelInstance 并缓存
747
754
  const instance = new Model.ModelInstance(collection, modelDef.definition, this);
@@ -823,6 +830,140 @@ module.exports = class {
823
830
  return this.dbInstance.collection(collectionName);
824
831
  }
825
832
 
833
+ // ============================================================
834
+ // v1.3.0 — scoped access APIs
835
+ // ============================================================
836
+
837
+ /**
838
+ * 获取限定池/库的 collection(公开底层方法)
839
+ *
840
+ * opts.pool / opts.database 均为可选;均不填时退化为默认路由。
841
+ *
842
+ * @param {string} collectionName
843
+ * @param {{ pool?: string, database?: string }} [opts={}]
844
+ * @returns {Object} Collection 实例
845
+ * @throws {Error} NOT_CONNECTED / NO_POOL_MANAGER / POOL_NOT_FOUND
846
+ * @since v1.3.0
847
+ */
848
+ scopedCollection(collectionName, opts = {}) {
849
+ if (!this.dbInstance) {
850
+ const err = new Error('Database is not connected. Call connect() first.');
851
+ err.code = 'NOT_CONNECTED';
852
+ throw err;
853
+ }
854
+ const { pool, database } = opts;
855
+ if (!pool && !database) {
856
+ return this.collection(collectionName);
857
+ }
858
+ return this._resolveModelCollection(collectionName, { pool, database });
859
+ }
860
+
861
+ /**
862
+ * 获取限定池/库的 Model 实例(公开底层方法,不走 _modelInstances 缓存)
863
+ *
864
+ * connection 合并语义:opts 字段优先,definition.connection 作 fallback。
865
+ * 例:billing/invoice.ts 有 connection:{database:'billing'},
866
+ * 调用 scopedModel('BillingInvoice', { pool:'cn' }) →
867
+ * merged = { database:'billing', pool:'cn' },路由到 cn 池 billing 库。
868
+ *
869
+ * @param {string} key - 已注册的 Model key(或 alias)
870
+ * @param {{ pool?: string, database?: string }} [opts={}]
871
+ * @returns {ModelInstance}
872
+ * @throws {Error} NOT_CONNECTED / MODEL_NOT_DEFINED / NO_POOL_MANAGER / POOL_NOT_FOUND
873
+ * @since v1.3.0
874
+ */
875
+ scopedModel(key, opts = {}) {
876
+ if (!this.dbInstance) {
877
+ const err = new Error('Database is not connected. Call connect() first.');
878
+ err.code = 'NOT_CONNECTED';
879
+ throw err;
880
+ }
881
+ const Model = require('./model');
882
+ if (!Model.has(key)) {
883
+ const err = new Error(`Model '${key}' is not defined. Call Model.define() first.`);
884
+ err.code = 'MODEL_NOT_DEFINED';
885
+ throw err;
886
+ }
887
+ const modelDef = Model.get(key);
888
+ const actualCollectionName =
889
+ modelDef.definition.collection ||
890
+ modelDef.definition.name ||
891
+ key;
892
+ const merged = { ...(modelDef.definition.connection || {}), ...opts };
893
+ const { pool, database } = merged;
894
+ const collection = (pool || database)
895
+ ? this._resolveModelCollection(actualCollectionName, { pool, database })
896
+ : this.dbInstance.collection(actualCollectionName);
897
+ return new Model.ModelInstance(collection, modelDef.definition, this);
898
+ }
899
+
900
+ /**
901
+ * 获取指定连接池的链式访问器
902
+ *
903
+ * 前置校验(立即执行):NOT_CONNECTED / NO_POOL_MANAGER / POOL_NOT_FOUND。
904
+ *
905
+ * @param {string} poolName - 连接池名称
906
+ * @returns {{ collection, model, use }} PoolAccessor 纯对象
907
+ * @throws {Error} NOT_CONNECTED / NO_POOL_MANAGER / POOL_NOT_FOUND
908
+ * @since v1.3.0
909
+ */
910
+ pool(poolName) {
911
+ if (!this.dbInstance) {
912
+ const err = new Error('Database is not connected. Call connect() first.');
913
+ err.code = 'NOT_CONNECTED';
914
+ throw err;
915
+ }
916
+ if (!this._poolManager) {
917
+ const err = new Error('No pool manager configured. Add pools to MonSQLize constructor options.');
918
+ err.code = 'NO_POOL_MANAGER';
919
+ throw err;
920
+ }
921
+ if (!this._poolManager._getPool(poolName)) {
922
+ const available = this._poolManager.getPoolNames?.() ?? [];
923
+ const err = new Error(`Pool '${poolName}' not found. Available pools: [${available.join(', ')}]`);
924
+ err.code = 'POOL_NOT_FOUND';
925
+ err.available = available;
926
+ throw err;
927
+ }
928
+ return {
929
+ collection: (name) =>
930
+ this.scopedCollection(name, { pool: poolName }),
931
+ model: (key) =>
932
+ this.scopedModel(key, { pool: poolName }),
933
+ use: (dbName) => ({
934
+ collection: (name) =>
935
+ this.scopedCollection(name, { pool: poolName, database: dbName }),
936
+ model: (key) =>
937
+ this.scopedModel(key, { pool: poolName, database: dbName }),
938
+ }),
939
+ };
940
+ }
941
+
942
+ /**
943
+ * 获取「默认池 + 指定库」访问器
944
+ *
945
+ * 适用于单连接多库场景(无需配置多连接池)。
946
+ * 只校验 NOT_CONNECTED,不涉及 poolManager。
947
+ *
948
+ * @param {string} dbName - 数据库名称
949
+ * @returns {{ collection, model }} ScopedAccessor 纯对象
950
+ * @throws {Error} NOT_CONNECTED
951
+ * @since v1.3.0
952
+ */
953
+ use(dbName) {
954
+ if (!this.dbInstance) {
955
+ const err = new Error('Database is not connected. Call connect() first.');
956
+ err.code = 'NOT_CONNECTED';
957
+ throw err;
958
+ }
959
+ return {
960
+ collection: (name) =>
961
+ this.scopedCollection(name, { database: dbName }),
962
+ model: (key) =>
963
+ this.scopedModel(key, { database: dbName }),
964
+ };
965
+ }
966
+
826
967
  /**
827
968
  * 自动加载 Model 文件
828
969
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monsqlize",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "A lightweight MongoDB ORM with multi-level caching, transaction support, distributed features, Saga distributed transactions, unified expression system with 122 operators, and universal function caching (100% MongoDB support)",
5
5
  "main": "lib/index.js",
6
6
  "module": "index.mjs",
package/types/base.ts CHANGED
@@ -68,6 +68,10 @@ export const enum ErrorCodes {
68
68
  JUMP_TOO_FAR = 'JUMP_TOO_FAR',
69
69
  STREAM_NO_JUMP = 'STREAM_NO_JUMP',
70
70
  STREAM_NO_TOTALS = 'STREAM_NO_TOTALS',
71
+ NOT_CONNECTED = 'NOT_CONNECTED',
72
+ NO_POOL_MANAGER = 'NO_POOL_MANAGER',
73
+ POOL_NOT_FOUND = 'POOL_NOT_FOUND',
74
+ MODEL_NOT_DEFINED = 'MODEL_NOT_DEFINED',
71
75
  CONNECTION_TIMEOUT = 'CONNECTION_TIMEOUT',
72
76
  CONNECTION_FAILED = 'CONNECTION_FAILED',
73
77
  CONNECTION_CLOSED = 'CONNECTION_CLOSED',
@@ -70,6 +70,69 @@ export interface MonSQLize {
70
70
  */
71
71
  collection(collectionName: string): Collection;
72
72
 
73
+ /**
74
+ * 获取限定池/库的 collection(公开底层方法)
75
+ *
76
+ * opts.pool / opts.database 均为可选;均不填时退化为默认路由。
77
+ *
78
+ * @param collectionName - MongoDB 集合名
79
+ * @param opts.pool - 连接池名称(可选)
80
+ * @param opts.database - 数据库名称(可选)
81
+ * @throws NOT_CONNECTED / NO_POOL_MANAGER / POOL_NOT_FOUND
82
+ * @since 1.3.0
83
+ */
84
+ scopedCollection(
85
+ collectionName: string,
86
+ opts?: { pool?: string; database?: string }
87
+ ): ReturnType<MonSQLize['collection']>;
88
+
89
+ /**
90
+ * 获取限定池/库的 Model 实例(不走 _modelInstances 缓存)
91
+ *
92
+ * connection 合并语义:opts 字段优先,definition.connection 作 fallback。
93
+ *
94
+ * @param key - 已注册的 Model key(或 alias)
95
+ * @param opts - 路由选项,字段优先于 definition.connection
96
+ * @throws NOT_CONNECTED / MODEL_NOT_DEFINED / NO_POOL_MANAGER / POOL_NOT_FOUND
97
+ * @since 1.3.0
98
+ */
99
+ scopedModel(
100
+ key: string,
101
+ opts?: { pool?: string; database?: string }
102
+ ): ReturnType<MonSQLize['model']>;
103
+
104
+ /**
105
+ * 获取指定连接池的链式访问器
106
+ *
107
+ * 立即校验:NOT_CONNECTED / NO_POOL_MANAGER / POOL_NOT_FOUND。
108
+ *
109
+ * @param poolName - 连接池名称
110
+ * @throws NOT_CONNECTED / NO_POOL_MANAGER / POOL_NOT_FOUND
111
+ * @since 1.3.0
112
+ */
113
+ pool(poolName: string): {
114
+ collection: (name: string) => ReturnType<MonSQLize['collection']>;
115
+ model: (key: string) => ReturnType<MonSQLize['model']>;
116
+ use: (dbName: string) => {
117
+ collection: (name: string) => ReturnType<MonSQLize['collection']>;
118
+ model: (key: string) => ReturnType<MonSQLize['model']>;
119
+ };
120
+ };
121
+
122
+ /**
123
+ * 获取「默认池 + 指定库」访问器
124
+ *
125
+ * 适用于单连接多库场景(无需配置多连接池)。
126
+ *
127
+ * @param dbName - 数据库名称
128
+ * @throws NOT_CONNECTED
129
+ * @since 1.3.0
130
+ */
131
+ use(dbName: string): {
132
+ collection: (name: string) => ReturnType<MonSQLize['collection']>;
133
+ model: (key: string) => ReturnType<MonSQLize['model']>;
134
+ };
135
+
73
136
 
74
137
  // ============================================================================
75
138
  // 事件系统