monsqlize 1.2.0 → 1.2.2

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-13
4
+ > **最后更新**: 2026-04-24
5
5
 
6
6
  ---
7
7
 
@@ -9,6 +9,8 @@
9
9
 
10
10
  | 版本 | 日期 | 变更摘要 | 详细 |
11
11
  |------|------|---------|------|
12
+ | [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
+ | [v1.2.1](./changelogs/v1.2.1.md) | 2026-04-13 | 🐛 **Bug 修复**:`msq.model()` 实例缓存 + 索引去重 + 死代码清理 + `types/monsqlize.ts` 补全 `model()` / `collection()` 类型 | [查看](./changelogs/v1.2.1.md) |
12
14
  | [v1.2.0](./changelogs/v1.2.0.md) | 2026-04-13 | 🐛 **Bug 修复 + 新功能**:`findPage` 正式支持 `projection` 投影参数(修复静默忽略问题)+ 有效投影策略自动保护游标排序字段 + 8 个测试用例 | [查看](./changelogs/v1.2.0.md) |
13
15
  | [v1.1.9](./changelogs/v1.1.9.md) | 2026-04-02 | 🚨 **P1 Bug 修复**:MultiLevelCache L2→L1 回填 TTL 缺失(null 永久驻留 L1)+ 新增 `backfillLocalTTL` 配置 + Redis `getWithTTL` 方法 + 14 个回归测试 | [查看](./changelogs/v1.1.9.md) |
14
16
  | [v1.1.8](./changelogs/v1.1.8.md) | 2026-03-16 | 🆕 **新功能**:Model 热重载支持(`undefine()` + `redefine()` + `_loadModels` reload 模式)+ 22个测试 (100%通过) | [查看](./changelogs/v1.1.8.md) |
package/README.md CHANGED
@@ -314,78 +314,102 @@ await msq.close();
314
314
 
315
315
  > **📦 依赖说明**: Model 层需要 `schema-dsl` 包支持(已随 monsqlize 自动安装,无需额外操作)
316
316
 
317
+ Model 层有两种使用方式,二者**互斥**,选其一即可:
318
+
319
+ | 方式 | 适合场景 |
320
+ |------|---------|
321
+ | **手动注册**(`Model.define()`)| 少量 Model、测试环境、需要精确控制加载顺序 |
322
+ | **自动加载**(`models:` 配置项)| 生产环境,Model 文件统一放在一个目录下 |
323
+
324
+ #### 方式一:手动注册
325
+
317
326
  ```javascript
318
327
  const MonSQLize = require('monsqlize');
319
328
  const { Model } = MonSQLize;
320
329
 
321
- const msq = new MonSQLize({
322
- type: 'mongodb',
323
- databaseName: 'mydb',
324
- config: { uri: 'mongodb://localhost:27017' },
325
- cache: { enabled: true },
326
- models: './models' // 🆕 v1.0.7: 自动加载 Model 文件
327
- });
328
-
329
- await msq.connect(); // 自动加载 models/*.model.js
330
-
331
- // 1. 定义 Model(带 Schema 验证、Relations 和 Hooks)
330
+ // 1. 先在 connect() 之前调用 Model.define() 注册所有 Model
332
331
  Model.define('users', {
333
- // 🔴 Schema 验证(默认启用,v1.0.7+,基于 schema-dsl 库)
334
332
  schema: (dsl) => dsl({
335
- username: 'string:3-32!', // 必需,3-32 字符
336
- email: 'email!', // 必需,邮箱格式
337
- password: 'string:6-!', // 必需,至少 6 字符
338
- age: 'number:0-120', // 可选,0-120 范围
339
- role: 'string?' // 可选字符串
333
+ username: 'string:3-32!',
334
+ email: 'email!',
335
+ password: 'string:6-!',
336
+ age: 'number:0-120'
340
337
  }),
341
-
342
- // Relations(关联查询)
343
338
  relations: {
344
- posts: { // 用户的文章
345
- from: 'posts',
346
- localField: '_id',
347
- foreignField: 'userId',
348
- single: false
349
- }
339
+ posts: { from: 'posts', localField: '_id', foreignField: 'userId', single: false }
350
340
  },
351
-
352
- // Hooks(生命周期钩子)
353
341
  hooks: (model) => ({
354
- insert: {
355
- before: async (ctx, doc) => {
356
- doc.createdAt = new Date(); // 自动添加时间戳
357
- return doc;
358
- }
359
- }
342
+ insert: { before: async (ctx, doc) => { doc.createdAt = new Date(); return doc; } }
360
343
  }),
361
-
362
- // 自定义方法
363
344
  methods: (model) => ({
364
- instance: {
365
- // 文档方法(注入到查询结果)
366
- checkPassword(password) {
367
- return this.password === password;
368
- }
369
- },
370
- static: {
371
- // Model 方法
372
- async findByUsername(username) {
373
- return await model.findOne({ username });
374
- }
375
- }
345
+ instance: { checkPassword(password) { return this.password === password; } },
346
+ static: { async findByUsername(username) { return await model.findOne({ username }); } }
376
347
  })
377
348
  });
378
349
 
379
350
  Model.define('posts', {
380
- schema: (dsl) => dsl({
381
- title: 'string:1-200!',
382
- content: 'string!',
383
- userId: 'string!'
384
- })
351
+ schema: (dsl) => dsl({ title: 'string:1-200!', content: 'string!', userId: 'string!' })
385
352
  });
386
353
 
387
- // 2. 使用 Model
354
+ // 2. 创建实例并连接
355
+ const msq = new MonSQLize({
356
+ type: 'mongodb',
357
+ databaseName: 'mydb',
358
+ config: { uri: 'mongodb://localhost:27017' },
359
+ cache: { enabled: true }
360
+ });
361
+ await msq.connect();
362
+
363
+ // 3. 获取 Model 并使用
388
364
  const User = msq.model('users');
365
+ ```
366
+
367
+ #### 方式二:自动加载(推荐用于生产环境)
368
+
369
+ 将每个 Model 单独放在一个文件里,`connect()` 时自动扫描目录加载,无需手动 `Model.define()`:
370
+
371
+ ```javascript
372
+ // app.js
373
+ const path = require('path');
374
+
375
+ const msq = new MonSQLize({
376
+ type: 'mongodb',
377
+ databaseName: 'mydb',
378
+ config: { uri: 'mongodb://localhost:27017' },
379
+ // 推荐用绝对路径,避免受 process.cwd() 影响
380
+ models: path.join(__dirname, 'models')
381
+ // 或完整配置:
382
+ // models: { path: path.join(__dirname, 'models'), pattern: '*.model.js', recursive: true }
383
+ });
384
+
385
+ await msq.connect(); // ← 自动扫描 models/*.model.{js,ts,mjs,cjs}
386
+
387
+ const User = msq.model('users'); // 直接使用,无需 Model.define()
388
+ ```
389
+
390
+ > 相对路径(如 `'./models'`)以 `process.cwd()` 为基准,即 Node.js 进程的启动目录。为避免歧义,推荐始终使用 `path.join(__dirname, 'models')` 这样的绝对路径。
391
+
392
+ ```javascript
393
+ // models/user.model.js ← 每个 Model 独立一个文件
394
+ module.exports = {
395
+ name: 'users', // 集合名称(必需)
396
+ schema: (dsl) => dsl({
397
+ username: 'string:3-32!',
398
+ email: 'email!'
399
+ }),
400
+ methods: (model) => ({
401
+ static: {
402
+ async findByUsername(username) { return await model.findOne({ username }); }
403
+ }
404
+ })
405
+ };
406
+ ```
407
+
408
+ > 详细说明(完整配置项、文件格式、错误处理)见 [docs/model.md — Model 自动加载](./docs/model.md#model-自动加载v107)
409
+
410
+ #### 两种方式共同的后续操作
411
+
412
+ ```javascript
389
413
 
390
414
  // ✅ Schema 验证自动生效
391
415
  try {
@@ -432,6 +456,35 @@ await User.insertOne(doc, { skipValidation: true });
432
456
  - ✅ **Relations** - 定义表关系(hasOne/hasMany/belongsTo)
433
457
  - ✅ **自定义方法** - instance 方法注入到文档,static 方法挂载到 Model
434
458
  - ✅ **自动缓存** - Populate 查询结果也会缓存
459
+ - ✅ **数据源绑定** - `connection: { pool?, database? }` 绑定指定连接池和/或数据库(v1.2.2+)
460
+
461
+ **数据源绑定示例(v1.2.2+)**:
462
+
463
+ ```js
464
+ // 在多连接池场景中,将 Model 绑定到指定的连接池 + 数据库
465
+ const msq = new MonSQLize({
466
+ uri: 'mongodb://localhost:27017',
467
+ databaseName: 'main_db',
468
+ pools: [{ name: 'analytics', uri: 'mongodb://analytics-host:27017' }]
469
+ });
470
+
471
+ // 绑定到不同数据库(使用默认连接池)
472
+ Model.define('AuditLog', {
473
+ schema: (dsl) => dsl({ action: 'string!', userId: 'string!' }),
474
+ connection: { database: 'audit_db' }
475
+ });
476
+
477
+ // 绑定到不同连接池 + 不同数据库
478
+ Model.define('AnalyticsReport', {
479
+ schema: (dsl) => dsl({ reportId: 'string!', data: 'object' }),
480
+ connection: { pool: 'analytics', database: 'reports_db' }
481
+ });
482
+
483
+ // 调用时自动路由,无需手动切换
484
+ const AuditLogModel = msq.model('AuditLog'); // → audit_db(默认池)
485
+ const ReportModel = msq.model('AnalyticsReport'); // → reports_db(analytics 池)
486
+ const UserModel = msq.model('users'); // → main_db(原逻辑,向后兼容)
487
+ ```
435
488
 
436
489
  📖 **详细文档**:[Model 层完整指南](./docs/model.md) | [Populate API](./docs/populate.md) | [Hooks API](./docs/hooks.md) | [Schema 验证](./docs/model.md#schema-验证)
437
490
 
package/index.d.ts CHANGED
@@ -178,6 +178,7 @@ declare module 'monsqlize' {
178
178
  export import DefaultValue = Model.DefaultValue;
179
179
  export import HookContext = Model.HookContext;
180
180
  export import ValidationResult = Model.ValidationResult;
181
+ export import ModelConnection = Model.ModelConnection;
181
182
  export import ModelDefinition = Model.ModelDefinition;
182
183
  export import RelationConfig = Model.RelationConfig;
183
184
  export import PopulateConfig = Model.PopulateConfig;
package/lib/index.js CHANGED
@@ -678,7 +678,10 @@ module.exports = class {
678
678
  }
679
679
 
680
680
  /**
681
- * 获取 Model 实例
681
+ * 获取 Model 实例(缓存复用)
682
+ *
683
+ * 同一 collectionName 多次调用返回同一实例。
684
+ * Model.redefine() / Model.undefine() 后自动失效,close() 后全部清空。
682
685
  *
683
686
  * @param {string} collectionName - 集合名称
684
687
  * @returns {ModelInstance} Model 实例
@@ -710,8 +713,19 @@ module.exports = class {
710
713
  throw err;
711
714
  }
712
715
 
713
- // 检查 Model 是否已定义
714
716
  const Model = require("./model");
717
+
718
+ // 缓存命中 + redefine 失效检查
719
+ if (this._modelInstances && this._modelInstances.has(collectionName)) {
720
+ if (!Model._redefinedNames.has(collectionName)) {
721
+ return this._modelInstances.get(collectionName);
722
+ }
723
+ // redefine 后需要重建实例
724
+ this._modelInstances.delete(collectionName);
725
+ Model._redefinedNames.delete(collectionName);
726
+ }
727
+
728
+ // 检查 Model 是否已定义
715
729
  if (!Model.has(collectionName)) {
716
730
  const err = new Error(
717
731
  `Model '${collectionName}' is not defined. Call Model.define() first.`,
@@ -723,12 +737,68 @@ module.exports = class {
723
737
  // 获取 Model 定义
724
738
  const modelDef = Model.get(collectionName);
725
739
 
726
- // 获取 collection 实例
727
- const collection = this.dbInstance.collection(collectionName);
740
+ // 获取 collection 实例(v1.2.2+ 支持 connection 路由)
741
+ const connection = modelDef.definition.connection;
742
+ const collection = (connection && (connection.pool || connection.database))
743
+ ? this._resolveModelCollection(collectionName, connection)
744
+ : this.dbInstance.collection(collectionName);
745
+
746
+ // 创建 ModelInstance 并缓存
747
+ const instance = new Model.ModelInstance(collection, modelDef.definition, this);
748
+
749
+ if (!this._modelInstances) this._modelInstances = new Map();
750
+ this._modelInstances.set(collectionName, instance);
751
+
752
+ return instance;
753
+ }
754
+
755
+ /**
756
+ * 解析 Model 绑定的 collection(内部方法)
757
+ *
758
+ * 根据 Model 定义中的 connection 配置,将请求路由到正确的连接池和数据库。
759
+ *
760
+ * 四种组合:
761
+ * 1. pool + database → 指定池、指定数据库
762
+ * 2. pool 只配置 → 指定池、实例默认 databaseName
763
+ * 3. database 只配置 → 默认连接池、指定数据库
764
+ * 4. 均未配置 → 原逻辑(此方法不会被调用)
765
+ *
766
+ * @private
767
+ * @param {string} collectionName - 集合名称
768
+ * @param {{ pool?: string, database?: string }} connection - Model 的 connection 配置
769
+ * @returns {Object} monSQLize 包装 collection 对象
770
+ * @since v1.2.2
771
+ */
772
+ _resolveModelCollection(collectionName, connection) {
773
+ const poolName = connection.pool;
774
+ const dbName = connection.database || this.databaseName;
775
+
776
+ if (poolName) {
777
+ if (!this._poolManager) {
778
+ const err = new Error(
779
+ `Model '${collectionName}' requires pool '${poolName}' but no pools are configured. ` +
780
+ `Add 'pools' to MonSQLize constructor options.`
781
+ );
782
+ err.code = 'NO_POOL_MANAGER';
783
+ throw err;
784
+ }
785
+
786
+ const client = this._poolManager._getPool(poolName);
787
+ if (!client) {
788
+ const availablePools = this._poolManager.getPoolNames().join(', ');
789
+ const err = new Error(
790
+ `Pool '${poolName}' not found. Available pools: [${availablePools}]`
791
+ );
792
+ err.code = 'POOL_NOT_FOUND';
793
+ throw err;
794
+ }
728
795
 
729
- // 创建 ModelInstance
730
- const ModelInstanceClass = require("./model").ModelInstance;
731
- return new ModelInstanceClass(collection, modelDef.definition, this);
796
+ // 指定池 + 数据库:通过 adapter 的 collectionFromClient 复用包装逻辑
797
+ return this._adapter.collectionFromClient(client, dbName, collectionName);
798
+ }
799
+
800
+ // 只切换数据库,用默认连接
801
+ return this.dbInstance.db(dbName).collection(collectionName);
732
802
  }
733
803
 
734
804
  /**
@@ -1007,6 +1077,12 @@ module.exports = class {
1007
1077
  this.dbInstance = null;
1008
1078
  this._connecting = null;
1009
1079
 
1080
+ // 清理 ModelInstance 缓存
1081
+ if (this._modelInstances) {
1082
+ this._modelInstances.clear();
1083
+ this._modelInstances = null;
1084
+ }
1085
+
1010
1086
  return null;
1011
1087
  }
1012
1088
  };
@@ -58,6 +58,14 @@ class Model {
58
58
  */
59
59
  static _registry = new Map();
60
60
 
61
+ /**
62
+ * 已被 redefine 的 collectionName 集合(用于缓存失效通知)
63
+ * @private
64
+ * @type {Set<string>}
65
+ * @since 1.2.1
66
+ */
67
+ static _redefinedNames = new Set();
68
+
61
69
  /**
62
70
  * 定义并注册 Model
63
71
  *
@@ -152,6 +160,11 @@ class Model {
152
160
  this._validateOptions(definition.options);
153
161
  }
154
162
 
163
+ // ========== 验证 connection 配置(v1.2.2+)==========
164
+ if (definition.connection) {
165
+ this._validateConnection(definition.connection);
166
+ }
167
+
155
168
  // ========== 解析 timestamps 配置 ==========
156
169
  const timestampsConfig = this._parseTimestampsConfig(
157
170
  definition.options?.timestamps,
@@ -214,6 +227,37 @@ class Model {
214
227
  }
215
228
  }
216
229
 
230
+ /**
231
+ * 验证 connection 配置
232
+ *
233
+ * @private
234
+ * @param {Object} connection - connection 配置对象
235
+ * @throws {Error} 配置不合法时抛出错误
236
+ *
237
+ * @since v1.2.2
238
+ */
239
+ static _validateConnection(connection) {
240
+ if (!connection || typeof connection !== 'object') {
241
+ return;
242
+ }
243
+
244
+ if (connection.pool !== undefined) {
245
+ if (typeof connection.pool !== 'string' || connection.pool.trim() === '') {
246
+ const err = new Error('connection.pool must be a non-empty string');
247
+ err.code = 'INVALID_MODEL_DEFINITION';
248
+ throw err;
249
+ }
250
+ }
251
+
252
+ if (connection.database !== undefined) {
253
+ if (typeof connection.database !== 'string' || connection.database.trim() === '') {
254
+ const err = new Error('connection.database must be a non-empty string');
255
+ err.code = 'INVALID_MODEL_DEFINITION';
256
+ throw err;
257
+ }
258
+ }
259
+ }
260
+
217
261
  /**
218
262
  * 验证 options 配置
219
263
  * @private
@@ -396,6 +440,8 @@ class Model {
396
440
  * @since 1.1.7
397
441
  */
398
442
  static undefine(collectionName) {
443
+ // 标记需要缓存失效(MonSQLize.model() 检查此标记)
444
+ this._redefinedNames.add(collectionName);
399
445
  return this._registry.delete(collectionName);
400
446
  }
401
447
 
@@ -434,6 +480,8 @@ class Model {
434
480
  static redefine(collectionName, definition) {
435
481
  this.undefine(collectionName);
436
482
  this.define(collectionName, definition);
483
+ // 标记需要缓存失效(MonSQLize.model() 检查此标记)
484
+ this._redefinedNames.add(collectionName);
437
485
  }
438
486
 
439
487
  /**
@@ -442,7 +490,12 @@ class Model {
442
490
  * @private
443
491
  */
444
492
  static _clear() {
493
+ // 将所有已注册名标记为需要缓存失效,让 msq.model() 重建 ModelInstance
494
+ for (const name of this._registry.keys()) {
495
+ this._redefinedNames.add(name);
496
+ }
445
497
  this._registry.clear();
498
+ // 注意:不清除 _redefinedNames,确保 msq 实例缓存能感知本次 clear
446
499
  }
447
500
  }
448
501
 
@@ -662,38 +715,6 @@ class ModelInstance {
662
715
  }
663
716
  }
664
717
 
665
- /**
666
- * 创建索引
667
- *
668
- * @private
669
- * @returns {Promise<void>}
670
- */
671
- async _createIndexes() {
672
- if (!Array.isArray(this.indexes) || this.indexes.length === 0) {
673
- return;
674
- }
675
-
676
- try {
677
- // 使用 createIndexes 批量创建索引
678
- await this.collection.createIndexes(this.indexes);
679
-
680
- if (this.msq && this.msq.logger) {
681
- this.msq.logger.info(
682
- `[Model] Created ${this.indexes.length} index(es) for ${this.collection.collectionName}`,
683
- );
684
- }
685
- } catch (err) {
686
- // 索引创建失败仅记录警告
687
- if (this.msq && this.msq.logger) {
688
- this.msq.logger.warn(
689
- `[Model] Failed to create indexes for ${this.collection.collectionName}:`,
690
- err.message,
691
- );
692
- }
693
- throw err;
694
- }
695
- }
696
-
697
718
  /**
698
719
  * 将实例方法注入到文档对象
699
720
  *
@@ -336,6 +336,31 @@ module.exports = class {
336
336
  };
337
337
  }
338
338
 
339
+ /**
340
+ * 从指定 MongoClient 创建包装 collection(用于多连接池 Model 绑定场景)
341
+ *
342
+ * 当 Model 定义了 `connection.pool` 时,`msq.model()` 会调用此方法,
343
+ * 将指定连接池的 MongoClient 传入,复用现有 collection() 的完整包装逻辑
344
+ * (runner、缓存、慢查询等),避免重复实现。
345
+ *
346
+ * 线程安全说明:Node.js 单线程,临时替换 this.client 的同步操作无竞态风险。
347
+ *
348
+ * @param {import('mongodb').MongoClient} client - 指定连接池的 MongoClient 实例
349
+ * @param {string} databaseName - 数据库名称
350
+ * @param {string} collectionName - 集合名称
351
+ * @returns {Object} monSQLize 包装 collection 对象
352
+ * @since v1.2.2
353
+ */
354
+ collectionFromClient(client, databaseName, collectionName) {
355
+ const originalClient = this.client;
356
+ this.client = client;
357
+ try {
358
+ return this.collection(databaseName, collectionName);
359
+ } finally {
360
+ this.client = originalClient;
361
+ }
362
+ }
363
+
339
364
  /**
340
365
  * 关闭连接并释放资源
341
366
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monsqlize",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
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",
@@ -114,7 +114,7 @@
114
114
  },
115
115
  "dependencies": {
116
116
  "async-lock": "^1.4.1",
117
- "mongodb": "^6.17.0",
117
+ "mongodb": "^6.21.0",
118
118
  "schema-dsl": "^1.2.4",
119
119
  "ssh2": "^1.17.0"
120
120
  }
@@ -106,5 +106,47 @@ export interface ModelDefinition<T = any> {
106
106
  * }
107
107
  */
108
108
  statics?: Record<string, (...args: any[]) => any>;
109
+
110
+ /**
111
+ * 数据源绑定配置(v1.2.2+)
112
+ *
113
+ * 声明此 Model 使用哪个连接池和/或数据库。
114
+ * 不配置时行为与 v1.2.1 完全相同(向后兼容)。
115
+ *
116
+ * @example
117
+ * // 绑定到指定连接池 + 数据库
118
+ * connection: { pool: 'analytics', database: 'reports_db' }
119
+ *
120
+ * @example
121
+ * // 只切换数据库(使用默认连接池)
122
+ * connection: { database: 'logs_db' }
123
+ *
124
+ * @since v1.2.2
125
+ */
126
+ connection?: ModelConnection;
127
+ }
128
+
129
+ /**
130
+ * Model 数据源绑定配置
131
+ *
132
+ * 声明 Model 使用哪个连接池(pool)和/或数据库(database)。
133
+ * 两个字段均为可选:
134
+ * - 只指定 pool → 使用该池 + 实例初始化时的 databaseName
135
+ * - 只指定 database → 使用默认连接池 + 指定数据库
136
+ * - 两者都指定 → 使用指定池 + 指定数据库
137
+ * - 均不指定 → 与原来完全相同(向后兼容)
138
+ *
139
+ * @since v1.2.2
140
+ */
141
+ export interface ModelConnection {
142
+ /**
143
+ * 连接池名称,须与 MonSQLize 构造函数 `pools[].name` 对应
144
+ */
145
+ pool?: string;
146
+
147
+ /**
148
+ * 数据库名称,不指定时使用实例初始化时的 `databaseName`
149
+ */
150
+ database?: string;
109
151
  }
110
152
 
@@ -4,12 +4,13 @@
4
4
  */
5
5
 
6
6
  import type { TransactionOptions } from './options';
7
- import type { DbAccessor, HealthView } from './collection';
7
+ import type { DbAccessor, HealthView, Collection } from './collection';
8
8
  import type { CacheLike } from './cache';
9
9
  import type { Transaction } from './transaction';
10
10
  import type { Lock, LockOptions } from './lock';
11
11
  import type { ExpressionFunction } from './base';
12
12
  import type { MetaInfo } from './pagination';
13
+ import type { ModelInstance } from './model';
13
14
 
14
15
  /**
15
16
  * MonSQLize 主类
@@ -46,6 +47,29 @@ export interface MonSQLize {
46
47
  */
47
48
  health(): Promise<HealthView>;
48
49
 
50
+ /**
51
+ * 获取 Model 实例(缓存复用)
52
+ *
53
+ * 同一 collectionName 多次调用返回同一实例。
54
+ * Model.redefine() / Model.undefine() 后自动失效,close() 后全部清空。
55
+ *
56
+ * @param collectionName - 已注册的集合名称
57
+ * @returns ModelInstance 实例
58
+ * @throws 数据库未连接(NOT_CONNECTED)
59
+ * @throws Model 未定义(MODEL_NOT_DEFINED)
60
+ * @since 1.0.3
61
+ */
62
+ model(collectionName: string): ModelInstance;
63
+
64
+ /**
65
+ * 获取原始集合实例
66
+ *
67
+ * @param collectionName - 集合名称
68
+ * @returns Collection 实例
69
+ * @throws 数据库未连接(NOT_CONNECTED)
70
+ */
71
+ collection(collectionName: string): Collection;
72
+
49
73
 
50
74
  // ============================================================================
51
75
  // 事件系统