Skip to content

Commit e6ab702

Browse files
committed
refactor: 模块化重构 + 修复示例Bug + 新增bookmark APIs
主要变更: - 🏗️ 模块化重构:拆分为 queries/ 和 management/ 模块(843→235行,72%精简) - ✨ 新增功能:prewarmBookmarks/listBookmarks/clearBookmarks APIs - 🐛 修复Bug:multi-level-cache.examples.js 的 API 使用错误 - 🔧 Redis处理:添加连接检测和优雅降级 - ✅ 测试通过:12/12 测试套件(308个测试)全部通过 - ✅ 示例验证:9/9 示例文件全部可运行 - 📝 文档更新:CHANGELOG.md, STATUS.md 技术细节: - queries/: find, findOne, findPage, count, aggregate, distinct, explain - management/: bookmark-ops, cache-ops, collection-ops, namespace - 修复6处 insertMany/insertOne/deleteMany API 调用错误 - 添加 testRedisConnection() 和错误抑制 - 重构无破坏性变更,100%向后兼容
1 parent 86f82db commit e6ab702

21 files changed

Lines changed: 1569 additions & 682 deletions

‎CHANGELOG.md‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,42 @@
44

55
## [未发布]
66

7+
### 新增
8+
- **Bookmark 维护 API**(2025-11-06)
9+
- 新增 `prewarmBookmarks(keyDims, pages)` - 预热指定页面的 bookmark 缓存
10+
- 新增 `listBookmarks(keyDims?)` - 列出已缓存的 bookmark,支持按查询维度过滤
11+
- 新增 `clearBookmarks(keyDims?)` - 清除 bookmark 缓存,支持精确控制
12+
- 创建 `lib/mongodb/management/bookmark-ops.js` 模块(167 行)
13+
- 完整文档:`docs/bookmarks.md`
14+
- 示例文件:`examples/bookmarks.examples.js`
15+
- 测试覆盖:16/16 测试通过
16+
17+
### 改进
18+
- **完整模块化重构**(2025-11-06)
19+
- **主文件精简**:`lib/mongodb/index.js` 从 843 行精简至 235 行(减少 72%)
20+
- **模块化架构**:
21+
- 创建 7 个查询模块:find.js, find-one.js, count.js, aggregate.js, distinct.js, explain.js, find-page.js
22+
- 创建 4 个管理模块:namespace-ops.js, collection-ops.js, cache-ops.js, bookmark-ops.js
23+
- 统一导出:queries/index.js, management/index.js
24+
- **工厂函数模式**:所有模块使用 `createXXX()` 工厂函数,支持上下文注入和闭包封装
25+
- **循环依赖解决**:通过正确的创建顺序(findPageOps → accessor → bookmarkOps)解决循环依赖
26+
- **动态缓存获取**:使用 `getCache()` 回调支持测试时动态替换 cache
27+
- **代码质量**:所有测试通过(12/12 测试套件,308 个测试),无性能回归
28+
- **文档完整**:所有 API 有对应的文档和示例
29+
30+
- **代码质量优化**(2025-11-06)
31+
- 清理 `lib/mongodb/index.js` 中 7 个未使用的模块导入(减少 36.8% 的导入)
32+
- 删除:reverseSort, pickAnchor, buildPagePipelineA, decodeCursor, makePageResult, validateLimitAfterBefore, assertCursorSortCompatible
33+
- 原因:这些模块已被 `find-page.js` 内部使用,无需在主文件中导入
34+
- 添加区域注释:在 collection() 方法内添加 7 个功能区域标记(命名空间、集合管理、缓存管理、查询方法、聚合统计、查询分析、分页、Bookmark)
35+
- 优化代码导航:支持 VS Code "Go to Symbol" 快速跳转
36+
- 创建模块化基础设施:
37+
- 新建 `lib/mongodb/queries/` 和 `lib/mongodb/management/` 目录
38+
- 完成 2 个示例模块:`management/namespace.js`, `management/collection-ops.js`
39+
- 验证脚本:`verify-refactoring.js`(模块化方案可行性验证通过)
40+
- 完整重构分析:`guidelines/analysis-reports/2025-11-06-mongodb-adapter-refactoring-analysis.md`
41+
- 所有测试通过(308/308),无性能回归
42+
743
### 修复
844
- **缓存文档澄清**(2025-11-06)
945
- 修正 `docs/cache.md` 中关于"自动失效"的误导性描述
@@ -13,6 +49,17 @@
1349
- 更新"常见问题"章节,准确描述手动缓存清理流程
1450

1551
### 改进
52+
- **高级查询选项评估完成**(2025-11-06)
53+
- 完成对 11 个 MongoDB 高级选项的全面评估(noCursorTimeout/tailable/max/min/returnKey/allowPartialResults/readPreference/readPreferenceTags/readConcern/comment/let)
54+
- 评估维度:必要性、实施难度、跨数据库兼容性、风险
55+
- 确认当前已支持:hint, collation, batchSize, comment (aggregate)
56+
- 确定实施优先级:
57+
- P1(推荐实施): comment 扩展到 find/findOne/count
58+
- P2(可选实施): readPreference (全局配置), max/min, readConcern, let
59+
- P3(不实施): noCursorTimeout, tailable, returnKey, allowPartialResults, readPreferenceTags
60+
- 详细分析报告:`guidelines/analysis-reports/2025-11-06-advanced-query-options-evaluation.md`
61+
- 更新 STATUS.md 反映评估结论
62+
1663
- **项目规范文档优化**(2025-11-06)
1764
- 在 `guidelines/profiles/monSQLize.md` 新增"MongoDB 连接模式"章节
1865
- 详细说明测试环境的推荐连接方式:`config: { useMemoryServer: true }`

‎STATUS.md‎

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,12 @@
145145
- 失败检测:prewarmBookmarks 自动检测超出范围页面
146146
- 缓存可用性检查:缓存不可用时抛出 CACHE_UNAVAILABLE 错误
147147
- 使用场景:系统启动预热、运维监控、数据变更后清除缓存、内存管理
148-
- ❌ 高级查询/游标选项统一抽象
149-
- batchSize/hint/collation/noCursorTimeout/tailable/max/min/returnKey/allowPartialResults/
150-
readPreference/readConcern。
151-
- ❌ comment / let
152-
- let 多见于聚合;待评估透传策略。
153-
- ❌ readPreferenceTags
154-
- 读偏好标签,仅 Mongo 适配器相关。
148+
- ☑️ 高级查询/游标选项(已评估,分阶段实施)
149+
- ✅ 已支持: hint, collation, batchSize, comment (aggregate)
150+
- 🗺️ P1 推荐实施: comment (find/findOne/count)
151+
- 🗺️ P2 可选实施: readPreference (全局配置), max/min, readConcern, let
152+
- ❌ 不实施: noCursorTimeout, tailable, returnKey, allowPartialResults, readPreferenceTags
153+
- 详细评估: [analysis-reports/2025-11-06-advanced-query-options-evaluation.md](../../guidelines/analysis-reports/2025-11-06-advanced-query-options-evaluation.md)
155154

156155
### MongoDB 方法(Writes)
157156
- ❌ insertOne / insertMany
@@ -189,11 +188,6 @@
189188
- ❌ validator / validationLevel / validationAction
190189
- ❌ time-series / clustered / capped 支持态度
191190

192-
### MongoDB 方法(Cursors & Pagination)
193-
- ❌ 深分页(游标/主键锚点)
194-
- ❌ 流式消费(cursor/async iterator)
195-
- ❌ 其他光标细节统一抽象(batchSize/noCursorTimeout/tailable/maxAwaitTimeMS/exhaust/hint)
196-
197191
### MongoDB 方法(Operators/Projection/Sort)
198192
- 🗺️ 跨库运算符映射层(operators registry)
199193
- 复杂阶段如 $lookup/$facet/$graphLookup 暂不覆盖。

‎examples/multi-level-cache.examples.js‎

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,36 @@
1010

1111
const MonSQLize = require('../lib/index');
1212

13+
// ============================================
14+
// Redis 连接测试辅助函数
15+
// ============================================
16+
17+
async function testRedisConnection() {
18+
try {
19+
const Redis = require('ioredis');
20+
const redis = new Redis({
21+
host: process.env.REDIS_HOST || 'localhost',
22+
port: parseInt(process.env.REDIS_PORT || '6379'),
23+
db: 0,
24+
retryStrategy: () => null, // 不重试
25+
lazyConnect: true,
26+
connectTimeout: 2000,
27+
enableOfflineQueue: false,
28+
maxRetriesPerRequest: 0
29+
});
30+
31+
// 抑制错误事件,避免未处理错误警告
32+
redis.on('error', () => { });
33+
34+
await redis.connect();
35+
await redis.ping();
36+
await redis.quit();
37+
return true;
38+
} catch (error) {
39+
return false;
40+
}
41+
}
42+
1343
// ============================================
1444
// 示例 1:使用内置 Redis 适配器(推荐)
1545
// ============================================
@@ -47,9 +77,10 @@ async function example1_builtinAdapter() {
4777

4878
try {
4979
const { collection } = await msq.connect();
80+
const db = msq._adapter.db; // 获取原生 MongoDB db 对象
5081

5182
// 插入测试数据
52-
await collection('products').insertMany([
83+
await db.collection('products').insertMany([
5384
{ name: 'Product A', price: 100, category: 'electronics' },
5485
{ name: 'Product B', price: 200, category: 'electronics' },
5586
{ name: 'Product C', price: 300, category: 'books' }
@@ -77,7 +108,7 @@ async function example1_builtinAdapter() {
77108
console.log(` - 加速: ${((Date.now() - start1) / (Date.now() - start2)).toFixed(1)}x`);
78109

79110
// 清理测试数据
80-
await collection('products').deleteMany({ query: {} });
111+
await db.collection('products').deleteMany({});
81112

82113
console.log('\n✅ 示例 1 完成\n');
83114
} catch (error) {
@@ -132,9 +163,10 @@ async function example2_existingRedisInstance() {
132163
});
133164

134165
const { collection } = await msq.connect();
166+
const db = msq._adapter.db; // 获取原生 MongoDB db 对象
135167

136168
// 插入测试数据
137-
await collection('users').insertMany([
169+
await db.collection('users').insertMany([
138170
{ name: 'Alice', age: 25, city: 'Beijing' },
139171
{ name: 'Bob', age: 30, city: 'Shanghai' },
140172
{ name: 'Charlie', age: 35, city: 'Beijing' }
@@ -165,7 +197,7 @@ async function example2_existingRedisInstance() {
165197
console.log(` - 未命中次数: ${stats.misses}`);
166198

167199
// 清理测试数据
168-
await collection('users').deleteMany({ query: {} });
200+
await db.collection('users').deleteMany({});
169201

170202
console.log('\n✅ 示例 2 完成\n');
171203

@@ -206,13 +238,14 @@ async function example3_policyComparison() {
206238
});
207239

208240
const { collection: col1 } = await msq1.connect();
209-
await col1('test').insertOne({ value: 1 });
241+
const db1 = msq1._adapter.db; // 获取原生 MongoDB db 对象
242+
await db1.collection('test').insertOne({ value: 1 });
210243

211244
const start1 = Date.now();
212245
await col1('test').find({ query: {}, cache: 5000, maxTimeMS: 3000 });
213246
console.log(` - 写入耗时: ${Date.now() - start1}ms(同步写入本地 + 远端)\n`);
214247

215-
await col1('test').deleteMany({ query: {} });
248+
await db1.collection('test').deleteMany({});
216249
await msq1.close();
217250

218251
// 策略 2: 本地优先(local-first-async-remote)
@@ -230,13 +263,14 @@ async function example3_policyComparison() {
230263
});
231264

232265
const { collection: col2 } = await msq2.connect();
233-
await col2('test').insertOne({ value: 2 });
266+
const db2 = msq2._adapter.db; // 获取原生 MongoDB db 对象
267+
await db2.collection('test').insertOne({ value: 2 });
234268

235269
const start2 = Date.now();
236270
await col2('test').find({ query: {}, cache: 5000, maxTimeMS: 3000 });
237271
console.log(` - 写入耗时: ${Date.now() - start2}ms(同步写入本地,异步写入远端)\n`);
238272

239-
await col2('test').deleteMany({ query: {} });
273+
await db2.collection('test').deleteMany({});
240274
await msq2.close();
241275

242276
console.log('✅ 示例 3 完成\n');
@@ -254,6 +288,23 @@ async function example3_policyComparison() {
254288
console.log(' 多层缓存示例(本地 + Redis)');
255289
console.log('=======================================');
256290

291+
// 检查 Redis 是否可用
292+
console.log('\n🔍 检查 Redis 连接...');
293+
const redisAvailable = await testRedisConnection();
294+
295+
if (!redisAvailable) {
296+
console.log('⚠️ Redis 不可用,跳过需要 Redis 的示例');
297+
console.log('💡 提示:请启动 Redis 服务以运行完整示例');
298+
console.log(' Windows: redis-server.exe');
299+
console.log(' Linux/Mac: redis-server\n');
300+
console.log('=======================================');
301+
console.log(' 示例已跳过(需要 Redis)');
302+
console.log('=======================================\n');
303+
process.exit(0);
304+
}
305+
306+
console.log('✅ Redis 连接正常\n');
307+
257308
await example1_builtinAdapter();
258309
await example2_existingRedisInstance();
259310
await example3_policyComparison();

0 commit comments

Comments
 (0)