fix: 第22轮搜索审计修复 — De Morgan语义/部分日期/has_not/custom URL等8项
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s

Bug-R22-1 (MEDIUM): OR模式特殊字段NOT产生错误De Morgan语义 — 17处修正
Bug-R22-2 (MEDIUM): has_not不反映日期范围NOT — 添加_date_range_markers检查
Bug-R22-3 (MEDIUM): YYYY-MM[DP]未引号部分日期无法解析 — 预处理器标准化
Bug-R22-4 (MEDIUM): date_preset=custom URL恢复丢失年份范围 — 读取URL参数
Bug-R22-5 (MEDIUM): 错误状态下分页总数残留 — 搜索开始时重置total
Bug-R22-6 (LOW): 搜索错误状态未在开始时清除 — 搜索开始时重置searchError
Bug-R22-7 (LOW): DATE token不在未消耗标记恢复处理器中 — 添加DATE类型
Bug-R22-8 (LOW): 外部NOT组内is_not被忽略 — 保留t.is_not
This commit is contained in:
34047007@qq.com
2026-07-28 17:05:27 +08:00
parent c68aa060f0
commit 2444a4be2c
4 changed files with 105 additions and 23 deletions
+69 -1
View File
@@ -2,7 +2,7 @@
> 本文档按修复轮次详细记录所有搜索功能合规性修复的背景、根因分析和修改内容。
>
> **累计**21 轮,235 项修复,80+ 字段标签注册,1007+ 项测试覆盖,7 项已知限制
> **累计**22 轮,248 项修复,80+ 字段标签注册,1007+ 项测试覆盖,7 项已知限制
> **时间跨度**2026-07-24 ~ 2026-07-29
> **核心文件**`pubmed_query_parser.py`~850 行)→ `search_engine.py`~1360 行)
@@ -1588,6 +1588,74 @@
---
## 第二十二轮:第 22 轮审计修复(8 项)
**日期**2026-07-29
**提交**`c68aa06..`(第 21 轮后追加)
**数量**8 项(5 MEDIUM + 3 LOW
**触发**:用户第 17 次要求全面检查(第 22 轮,4 并行审计 agent)
**测试**1007+ 全部通过 + 前端 build 通过
### Bug-R22-1 (MEDIUM): OR 模式下特殊字段 NOT 产生错误 De Morgan 语义
- **文件**`search_engine.py:944-1167`
- **根因**`term_conditions.append(not_(or_(*neg_conds)))` 在 OR 模式下产生 `NOT(A OR B)` = `NOT A AND NOT B`。正确应为 `or_(not_(A), not_(B))` = `NOT A OR NOT B`。
- **影响**`NOT "Review"[PT] OR NOT "Clinical Trial"[PT]` 搜索结果过窄。
- **修复**:将全部 17 处 `term_conditions.append(not_(or_(*neg_conds)))` 改为 `term_conditions.extend(not_(c) for c in neg_conds)`。AND 模式下语义等价,OR/mixed 模式语义修正。
### Bug-R22-2 (MEDIUM): `has_not` 不反映日期范围 NOT
- **文件**`pubmed_query_parser.py:446`
- **根因**`_ungrouped` 过滤掉 `_is_range_end=True` 的标记,`has_not` 不检查 `_date_range_markers`。
- **修复**`has_not` 额外检查 `result._date_range_markers`。
### Bug-R22-3 (MEDIUM): `YYYY-MM[DP]` 未引号部分日期无法解析
- **文件**`pubmed_query_parser.py:1010-1015`
- **根因**`2024-01[DP]` 分词为 `NUMBER(2024) WORD(-01) FIELD([DP])`。`-01` 不匹配 `_PARTIAL_DATE_RE`。
- **修复**:预处理器将 `YYYY-MM[date_field]` 标准化为 `YYYY-MM-01[date_field]`。
### Bug-R22-4 (MEDIUM): `date_preset=custom` URL 恢复丢失年份范围
- **文件**`SearchView.vue:393-396`
- **根因**`restoreFromQuery` 在 custom 分支无条件清空 yearFromStr/yearToStr。
- **修复**custom 分支读取 URL 中的 `year_from/year_to`。
### Bug-R22-5 (MEDIUM): 错误状态下分页总数残留
- **文件**`SearchView.vue:274-276`
- **根因**catch 块清空 results 但不清除 total。
- **修复**:搜索开始时重置 `total.value = 0`。
### Bug-R22-6 (LOW): 搜索错误状态未在开始时清除
- **文件**`SearchView.vue:274-276`
- **根因**searchError 仅成功后清空。
- **修复**:搜索开始时设置 `searchError.value = ''`。
### Bug-R22-7 (LOW): DATE token 不在未消耗标记恢复处理器中
- **文件**`pubmed_query_parser.py:456`
- **根因**:未消耗标记处理器仅捕获 WORD/QUOTED/NUMBER。
- **修复**:添加 `TokenType.DATE`。
### Bug-R22-8 (LOW): 外部 NOT 组内 `is_not` 被忽略
- **文件**`search_engine.py:1203`
- **根因**`NOT (A OR NOT B)` → `not_(or_(A, B))`,内部 NOT B 被吞掉。
- **修复**:组内保留 `t.is_not`。
### 审计结果汇总
| 审计维度 | 结果 |
|---------|------|
| R21 回归 | ✅ 无回归 |
| 搜索引擎代码 | ✅ De Morgan 语义 17 处修正、negated 组内 is_not |
| 解析器/分词器 | ✅ YYYY-MM 预处理器、has_not 日期范围、DATE 恢复 |
| 前端集成 | ✅ custom URL 恢复、total/error 清理 |
---
## 附录:测试覆盖统计
| 测试文件 | 用例数 | 范围 |