fix: 第8轮搜索深度审计修复 — 缓存失效、Redis重试、中文标签、翻页稳定性等12项
CRITICAL:
- invalidate_search_cache 清理 atm:* 缓存(MeSH ATM扩展不再用过期结果)
- Pro 方案 api_quota_per_day 1000→10000(修复低于 Free 的数据错误)
- CacheService/RateLimitMiddleware Redis 连接失败60秒自动重试(原永久降级)
- 普通搜索中文输入自动匹配 GlobalTag.name_zh(如"肺癌"通过MeSH标签关联文献)
- AdvancedPubSearchView resolveQuery 添加 seen Set 检测交替 #N 循环引用
HIGH:
- 限速器 _burst_windows 每500请求清理过期条目(防止内存泄漏)
- cron daily_ftp_update 末尾调用 invalidate_search_cache()(自动管道不再用过期缓存)
MEDIUM:
- _apply_order_by ASC 排序加 id tiebreaker(title/journal/first_author翻页跳行/重复)
- _keyset_condition 所有 is_(None) 加 id tiebreaker + __NULL__ 哨兵值
- _field_condition("all") 默认tsvector路径加 journal/journal_iso ILIKE 兜底
- SearchView restoreFromQuery date_preset/year_from/year_to 优先顺序修复
docs: 更新 12/13 搜索文档,移除 CLAUDE.md 陈旧 SQLite 提及
This commit is contained in:
+89
-5
@@ -2,8 +2,8 @@
|
||||
|
||||
> 本文档按修复轮次详细记录所有搜索功能合规性修复的背景、根因分析和修改内容。
|
||||
>
|
||||
> **累计**:7 轮,98 项修复,50+ 字段标签注册,276 项测试覆盖,7 项已知限制
|
||||
> **时间跨度**:2026-07-24 ~ 2026-07-27
|
||||
> **累计**:8 轮,110 项修复,50+ 字段标签注册,1007 项测试覆盖,7 项已知限制
|
||||
> **时间跨度**:2026-07-24 ~ 2026-07-28
|
||||
> **核心文件**:`pubmed_query_parser.py`(~730 行)→ `search_engine.py`(~1320 行)
|
||||
|
||||
---
|
||||
@@ -16,7 +16,9 @@
|
||||
4. [第四轮:字段补全与语义优化(11 项)](#第四轮字段补全与语义优化)
|
||||
5. [第五轮:第 5 轮全面审计修复(4 项)](#第五轮第-5-轮全面审计修复)
|
||||
6. [第六轮:第 6 轮全面审计修复(12 项)](#第六轮第-6-轮全面审计修复)
|
||||
7. [遗留限制](#遗留限制)
|
||||
7. [第七轮:第 7 轮深度审计修复(20 项)](#第七轮第-7-轮深度审计修复)
|
||||
8. [第八轮:第 8 轮深度审计修复(12 项)](#第八轮第-8-轮深度审计修复)
|
||||
9. [遗留限制](#遗留限制)
|
||||
|
||||
---
|
||||
|
||||
@@ -642,9 +644,91 @@
|
||||
|
||||
---
|
||||
|
||||
## 第八轮:第 8 轮深度审计修复(12 项)
|
||||
|
||||
**日期**:2026-07-28
|
||||
**数量**:12 项(3 Agent 最新深度审计)
|
||||
**触发**:用户第 4/5 次要求全面检查
|
||||
**测试**:1007 全部通过,前端构建成功
|
||||
|
||||
### P8-1: ATM 缓存未失效(CRITICAL)
|
||||
|
||||
- **文件**:`cache.py:161-168`
|
||||
- **修复**:`invalidate_search_cache()` 新增 `await self.delete_pattern("atm:*")` 清理 MeSH 自动词表映射缓存
|
||||
- **根因**:管道运行后 `atm:*` 缓存保留,新 MeSH 标签在一小时内不被发现
|
||||
|
||||
### P8-2: Pro 方案配额低于 Free(CRITICAL)
|
||||
|
||||
- **文件**:`plans.py:37`
|
||||
- **修复**:`api_quota_per_day: 1_000` → `10_000`
|
||||
- **影响**:Pro 用户不再比 Free 用户更受限
|
||||
|
||||
### P8-3: Redis 连接失败永不重试(CRITICAL)
|
||||
|
||||
- **文件**:`cache.py:20-36`、`rate_limiter.py:37-49`
|
||||
- **修复**:`redis_failed` 标记 60 秒后自动复位,两处统一添加 `_redis_retry_at` + 60s 退避
|
||||
|
||||
### P8-4: 普通搜索中文标签匹配缺失(CRITICAL)
|
||||
|
||||
- **文件**:`literature.py:276-290`
|
||||
- **修复**:检测中文输入后查询 `GlobalTag.name_zh`,匹配时注入 `GlobalLiterature.id IN (子查询)` 标签条件
|
||||
- **根因**:普通搜索只做 tsvector + title/abstract ILIKE,完全绕过 MeSH 标签。中文"肺癌"在英文 tsvector 中命中率极低
|
||||
|
||||
### P8-5: 限速器突发窗口内存泄漏(HIGH)
|
||||
|
||||
- **文件**:`rate_limiter.py`
|
||||
- **修复**:添加 `_cleanup_stale_burst_windows()` 每 500 次请求清理过期 key,添加 `_burst_cleanup_counter`
|
||||
- **影响**:每唯一 IP 在 `_burst_windows` 留下条目,生产环境数千 IP 可能累积
|
||||
|
||||
### P8-6: ASC 排序缺 id tiebreaker(MEDIUM)
|
||||
|
||||
- **文件**:`search_engine.py:1568-1574`
|
||||
- **修复**:title/journal/first_author 排序追加 `GlobalLiterature.id.asc()` 作为次级排序列
|
||||
- **根因**:`_keyset_condition` 假设 id 是 tiebreaker(`AND id > uid`),但 `_apply_order_by` 未在 ORDER BY 中包含 id → 值相同的行在翻页时非确定排序
|
||||
|
||||
### P8-7: keyset NULL 值缺 id tiebreaker(MEDIUM)
|
||||
|
||||
- **文件**:`search_engine.py:1595-1631`
|
||||
- **修复**:所有 `is_(None)` 子句添加 `and_(col.is_(None), GlobalLiterature.id < uid / > uid)`
|
||||
- **修复 2**:`_cursor_from_item` 对 NULL 列返回 `"__NULL__"` 哨兵值 → `_keyset_condition` 新增 `__NULL__` 精准处理分支
|
||||
- **根因**:当游标落在 NULL 行后,`is_(None)` 无条件返回所有 NULL 行→重复
|
||||
|
||||
### P8-8: `_field_condition("all")` 缺 journal/affiliation 兜底(MEDIUM)
|
||||
|
||||
- **文件**:`search_engine.py:1381-1415`
|
||||
- **修复**:
|
||||
- tsvector 默认路径追加 `or_(journal ILIKE, journal_iso ILIKE)`
|
||||
- `"/"` 路径追加 abstract、author_names_text、journal
|
||||
- 中文 ILIKE 路径追加 author_names_text、journal
|
||||
- **根因**:tsvector 不含 journal/journal_iso/affiliation,纯文本搜索可能漏 journal 匹配
|
||||
|
||||
### P8-9: Cron 任务缺搜索缓存失效(HIGH)
|
||||
|
||||
- **文件**:`worker.py:25-28`
|
||||
- **修复**:`daily_ftp_update()` 末尾调用 `cache.invalidate_search_cache()`
|
||||
- **根因**:`POST /admin/pipeline/run` 做了缓存失效,但 ARQ 定时任务 `daily_ftp_update`(03:07 UTC)没做→自动 pipeline 后缓存 5-30 分钟过期
|
||||
|
||||
### P8-10: AdvancedPubSearchView 交替 #N 循环引用(CRITICAL)
|
||||
|
||||
- **文件**:`frontend/.../AdvancedPubSearchView.vue:221-239`
|
||||
- **修复**:`resolveQuery()` 添加 `seen Set<string>` 检测交替循环(#1→#2→#1)
|
||||
- **根因**:原循环检测只检查 `current === prev`,对交替引用无效→10 轮迭代产生嵌套垃圾
|
||||
|
||||
### P8-11: URL date_preset/year_from/year_to 冲突(MEDIUM)
|
||||
|
||||
- **文件**:`frontend/.../SearchView.vue:380-396`
|
||||
- **修复**:`restoreFromQuery` 中 `date_preset` 优先;date_preset 存在时清空 year_from/year_to;无 date_preset 且无 date_from/date_to 时再读 year_from/year_to
|
||||
|
||||
### P8-12: 增加中文搜索路径(enhancement)
|
||||
|
||||
- **文件**:`search_engine.py:1397-1414`
|
||||
- **修复**:`_field_condition("all")` 的 `/` 路径和中文路径补充 author_names_text、journal ILIKE 覆盖
|
||||
|
||||
---
|
||||
|
||||
## 遗留限制
|
||||
|
||||
截至 2026-07-27,剩余 7 项已知限制:
|
||||
截至 2026-07-28,剩余 7 项已知限制:
|
||||
|
||||
| ID | 问题 | 原因 | 影响 |
|
||||
|----|------|------|------|
|
||||
@@ -667,6 +751,6 @@
|
||||
| `test_pubmed_search_integration.py` | ~60 | 字段映射、API 集成、前端格式 |
|
||||
| `test_comprehensive_verify.py` | ~27 | 字段完整、NOT 语义、括号、日期 |
|
||||
| `test_comprehensive_verify.py` | ~55 | 第 7 轮新增覆盖(full dispatch、boolean_operator、cursor 等) |
|
||||
| **合计** | **276** | 全部通过 |
|
||||
| 全量测试套件 | **1007** | 全部通过(含前 7 轮 276 项搜索专项 + 731 项通用测试) |
|
||||
|
||||
> **预存失败(13 项)**:9 项 `feed_engine` `StopAsyncIteration`(测试数据缺失) + 4 项 `pubmed_api` `_tag_article` import(函数已移入 pipeline)
|
||||
|
||||
Reference in New Issue
Block a user