Skip to content

Commit df44f74

Browse files
committed
更新文档
1 parent aee223a commit df44f74

4 files changed

Lines changed: 136 additions & 13 deletions

File tree

‎README.md‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [快速开始](#quick-start)
99
- [深度分页(聚合版,Mongo)](#deep-pagination-agg)
1010
- [统一 findPage:游标 + 跳页 + offset + totals](#findpage-unified)
11+
- [返回耗时(meta)](#返回耗时meta)
1112
- [缓存与失效](#cache)
1213
- [缓存配置](#缓存配置)
1314
- [缓存行为与细节](#缓存行为与细节)
@@ -16,6 +17,8 @@
1617
- [invalidate(op) 用法](#invalidate)
1718
- [跨库访问注意事项](#cross-db)
1819
- [说明](#notes)
20+
- [事件(Mongo)](#事件mongo)
21+
- [健康检查与事件(Mongo)](#健康检查与事件mongo)
1922

2023
<a id='status'></a>
2124
## 状态(速览)
@@ -566,6 +569,53 @@ const msq = await new MonSQLize({
566569
提示:也可在上层自行构建 MultiLevelCache 并作为 `cache` 直接注入(需 `require('monsqlize/lib/multi-level-cache')`)。
567570

568571

572+
## 返回耗时(meta)
573+
- 支持在所有读 API 上按次返回耗时与元信息(opt-in,不改默认返回类型)。
574+
- 使用方法:在 options 中传入 `meta: true` 或 `meta: { level: 'sub', includeCache: true }`。
575+
- findOne/find/count/find:当 `meta` 为真时返回 `{ data, meta }`;不传则维持原返回(对象/数组/数字)。
576+
- findPage:当 `meta` 为真时在返回对象上附加 `meta` 字段;`level:'sub'` 时返回每个 hop/offset 的子步骤耗时。
577+
578+
示例:
579+
```js
580+
// 单条查询:返回耗时
581+
const { data, meta } = await coll.findOne({ query:{ name: 'Alice' }, cache: 2000, maxTimeMS: 1500, meta: true });
582+
console.log(meta.durationMs);
583+
584+
// 分页:总耗时
585+
const page = await coll.findPage({ query:{ status:'paid' }, sort:{ createdAt:-1,_id:1 }, limit:20, page:37, meta:true });
586+
console.log(page.meta.durationMs);
587+
588+
// 分页:子步骤耗时(跳页时可见每个 hop 的耗时)
589+
const page2 = await coll.findPage({ query:{ status:'paid' }, sort:{ createdAt:-1,_id:1 }, limit:20, page:128, jump:{ step:20 }, meta:{ level:'sub', includeCache:true } });
590+
console.table(page2.meta.steps);
591+
```
592+
593+
> 说明:
594+
> - 默认不返回 meta,需显式开启;开销很小,仅一次时间戳与对象组装。
595+
> - includeCache 仅包含去敏维度(如 cacheTtl 等,具体依实现)。
596+
597+
## 事件(Mongo)
598+
- 事件基于 Node.js EventEmitter,进程内有效:
599+
- `connected`: `{ type, db, scope, iid? }`
600+
- `closed`: `{ type, db, iid? }`
601+
- `error`: `{ type, db, error, iid? }`
602+
- `slow-query`: `{ op, ns, durationMs, startTs, endTs, maxTimeMS, ... }`(去敏)
603+
- `query`(可选):每次读操作完成后触发;需在构造 defaults 中开启 `metrics.emitQueryEvent=true`。
604+
- 实例还暴露:`on/off/once/emit`。
605+
606+
用法示例:
607+
```js
608+
const msq = new MonSQLize({ type:'mongodb', databaseName:'example', config:{ uri:'mongodb://localhost:27017' }, defaults:{ metrics:{ emitQueryEvent:false } } });
609+
msq.on('connected', info => console.log('[connected]', info));
610+
msq.on('closed', info => console.log('[closed]', info));
611+
msq.on('error', info => console.error('[error]', info));
612+
msq.on('slow-query', meta => console.warn('[slow-query]', meta));
613+
// 可选:开启 query 事件
614+
// const msq = new MonSQLize({ ..., defaults:{ metrics:{ emitQueryEvent:true } } });
615+
msq.on('query', meta => console.log('[query]', meta));
616+
await msq.connect();
617+
```
618+
569619
## 健康检查与事件(Mongo)
570620
- 健康检查:`await msq.health()` 返回 `{ status: 'up'|'down', connected, defaults, cache?, driver }` 摘要视图。
571621
- 事件钩子:

‎example/mongodb.js‎

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,15 @@ const MonSQLize = require('../lib/index.js');
8888
// });
8989
//
9090
// // 1) 基础查询:findOne
91-
// const one = await coll.findOne({
92-
// query: { status: 'paid' },
93-
// projection: ['_id', 'status', 'createdAt'],
94-
// sort: { createdAt: -1 }, // -1 降序,1 升序
95-
// cache: 2000, // 启用 2s 缓存(TTL 毫秒)
96-
// maxTimeMS: 1500,
97-
// });
98-
// console.log('[findOne] =>', one);
91+
const { data, meta } = await coll.findOne({
92+
query: { status: 'paid' },
93+
projection: ['_id', 'status', 'createdAt'],
94+
sort: { createdAt: -1 }, // -1 降序,1 升序
95+
cache: 2000, // 启用 2s 缓存(TTL 毫秒)
96+
maxTimeMS: 1500, // 超时
97+
meta: true // 返回详细耗时信息
98+
});
99+
console.log('[findOne] =>', data, meta);
99100

100101
// // 2) 基础查询:find(列表)
101102
// const list = await coll.find({
@@ -210,6 +211,41 @@ const MonSQLize = require('../lib/index.js');
210211
// 警告:dropCollection 会删除数据,请谨慎使用
211212
// const dropped = await coll.dropCollection();
212213
// console.log('dropCollection =>', dropped);
214+
215+
// 9.1) 演示跳页功能 - 模拟跳转到特定页面
216+
// console.log('\n--- 跳页演示 ---');
217+
//
218+
// // 获取总数(用于计算总页数)
219+
// const totalCount = await coll.count({
220+
// query: { status: 'paid' },
221+
// cache: 2000
222+
// });
223+
// const pageSize = 2;
224+
// const totalPages = Math.ceil(totalCount / pageSize);
225+
//
226+
// console.log(`总记录数: ${totalCount}, 每页: ${pageSize}, 总页数: ${totalPages}`);
227+
//
228+
// // 跳转到最后一页的逻辑(实际应用中可能需要更复杂的游标计算)
229+
// if (totalPages > 2) {
230+
// console.log('\n--- 尝试获取最后一页 ---');
231+
// // 注意:真实的跳页可能需要维护页码与游标的映射关系
232+
// // 这里只是演示概念,实际应用中建议:
233+
// // 1. 缓存关键页面的游标
234+
// // 2. 或使用传统的 skip/limit 方式作为跳页的补充
235+
//
236+
// const lastPageQuery = await coll.find({
237+
// query: { status: 'paid' },
238+
// sort: { createdAt: -1, _id: 1 },
239+
// limit: pageSize,
240+
// skip: (totalPages - 1) * pageSize, // 使用 skip 跳到最后一页
241+
// cache: 1000,
242+
// });
243+
//
244+
// console.log(`最后一页找到 ${lastPageQuery.length} 条记录`);
245+
// lastPageQuery.forEach((item, index) => {
246+
// console.log(` ${index + 1}. 订单金额: ${item.amount}, 创建时间: ${item.createdAt}`);
247+
// });
248+
// }
213249
}
214250
catch (e) {
215251
// 统一错误处理:

‎lib/common/runner.js‎

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,54 @@ const CacheFactory = require('../cache');
22
const { withSlowQueryLog } = require('./log');
33

44
/**
5-
* 统一执行器:包装缓存与慢日志
5+
* 统一执行器:包装缓存与慢日志,并可选返回 meta(耗时等)与发出 query 事件
66
* @param {import('../..').CacheLike|any} cache
77
* @param {{iid:string, type:string, db:string, collection:string}} nsAll
88
* @param {import('../..').LoggerLike} logger
99
* @param {object} defaults
10-
* @param {{ keyBuilder?: (op:string, options:any)=>any, slowLogShaper?: (options:any)=>object, onSlowQueryEmit?: (meta:any)=>void }} hooks
10+
* @param {{ keyBuilder?: (op:string, options:any)=>any, slowLogShaper?: (options:any)=>object, onSlowQueryEmit?: (meta:any)=>void, onQueryEmit?: (meta:any)=>void }} hooks
1111
*/
1212
function createCachedRunner(cache, nsAll, logger, defaults, hooks = {}) {
1313
const cached = CacheFactory.createCachedReader(cache, nsAll);
14-
return (op, options, exec) => {
14+
return async (op, options = {}, exec) => {
1515
const optsForKey = typeof hooks.keyBuilder === 'function' ? hooks.keyBuilder(op, options || {}) : (options || {});
1616
const runExec = () => cached(op, optsForKey, exec);
17-
return withSlowQueryLog(logger, defaults, op, { db: nsAll.db, coll: nsAll.collection, iid: nsAll.iid, type: nsAll.type }, options, runExec, hooks.slowLogShaper, hooks.onSlowQueryEmit);
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;
1853
};
1954
}
2055

‎lib/mongodb/index.js‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module.exports = class {
3636
// 事件:connected/closed/error/slow-query
3737
this._emitter = new EventEmitter();
3838
this.on = this._emitter.on.bind(this._emitter);
39+
this.once = this._emitter.once.bind(this._emitter);
3940
this.off = (this._emitter.off ? this._emitter.off.bind(this._emitter) : this._emitter.removeListener.bind(this._emitter));
4041
this.emit = this._emitter.emit.bind(this._emitter);
4142
}
@@ -133,7 +134,8 @@ module.exports = class {
133134
}, this.logger, this.defaults, {
134135
keyBuilder: mongoKeyBuilder,
135136
slowLogShaper: mongoSlowLogShaper,
136-
onSlowQueryEmit: (meta) => { try { this.emit && this.emit('slow-query', meta); } catch(_) {} }
137+
onSlowQueryEmit: (meta) => { try { this.emit && this.emit('slow-query', meta); } catch(_) {} },
138+
onQueryEmit: (meta) => { try { this.emit && this.emit('query', meta); } catch(_) {} }
137139
});
138140
return {
139141
/** 返回当前访问器的命名空间信息 */

0 commit comments

Comments
 (0)