fix: 第20轮搜索审计修复 — AND子组层级/OR模式NOT/长格式标签等6项
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s

Bug-R20-1 (HIGH): AND子组在括号内被提升为顶层AND条件 — 新增sub_group_refs层级追踪
Bug-R20-2 (HIGH): OR模式NOT条件未独立AND — 分离UnaryExpression独立AND
Bug-R20-3 (MEDIUM): is_pubmed_syntax()不识别小写布尔运算符 — 添加re.IGNORECASE
Bug-R20-4 (MEDIUM): 长格式字段标签不被识别 — _ALL_FIELD_TAGS新增40+条目
Bug-R20-5 (MEDIUM): 400错误URL持久化导致刷新循环 — syncSearchToUrl移入try块
Bug-R20-6 (LOW): 空括号[]产生空Term — stripped为空时跳过
This commit is contained in:
34047007@qq.com
2026-07-28 16:30:03 +08:00
parent 2b6ffd6de4
commit 5dcf639b0b
4 changed files with 189 additions and 16 deletions
+64 -1
View File
@@ -2,7 +2,7 @@
> 本文档按修复轮次详细记录所有搜索功能合规性修复的背景、根因分析和修改内容。
>
> **累计**19 轮,218 项修复,50+ 字段标签注册,1007 项测试覆盖,7 项已知限制
> **累计**20 轮,224 项修复,80+ 字段标签注册,1007 项测试覆盖,7 项已知限制
> **时间跨度**2026-07-24 ~ 2026-07-29
> **核心文件**`pubmed_query_parser.py`~850 行)→ `search_engine.py`~1360 行)
@@ -1390,6 +1390,69 @@
---
## 第二十轮:第 20 轮审计修复(6 项)
**日期**2026-07-29
**提交**`2b6ffd6`(与第 19 轮同一提交基础上追加)
**数量**6 项(2 HIGH + 3 MEDIUM + 1 LOW
**触发**:用户第 15 次要求全面检查(Round 204 并行 agentR19 回归/OR 模式、分词器/边界、集成/租户、前端集成)
**测试**1007 全部通过 + 前端 build 通过
### Bug-R20-1 (HIGH): AND 子组在括号内被提升为顶层 AND 条件
- **文件**`pubmed_query_parser.py``_parse_primary` + `_parse_or_expr`)、`search_engine.py`(组处理循环)
- **根因**:第 19 轮移除了 `_depth > 0` 守卫,`_parse_or_expr` 在括号内创建 AND 子组(如 `(A OR B AND C)` → sub-group `[B,C]`parent group `[A]`)。但引擎将两个组独立处理后全部 AND 在一起 → `A AND B AND C`。正确语义应为 `A OR (B AND C)`。
- **影响**:任何括号内混用 AND/OR 的查询(如 `(lung OR breast cancer)`)结果被严重过滤。
- **修复**
- 解析器:新增 `ParsedPubmedQuery.sub_group_refs: list[list[int]]` 记录 parent→child 关系
- `_parse_primary` 在创建父组时记录子组 GID
- 引擎跳过子组(由父组处理),父组处理时包含自身 term + 子组条件,用父组操作符组合
- **验证**`(A OR B AND C)` 正确生成 `or_(A, and_(B, C))`
### Bug-R20-2 (HIGH): OR 模式 NOT 条件未独立 AND
- **文件**`search_engine.py:1234-1236`
- **根因**`boolean_operator == "or"` 时所有条件(含 `not_(cond)`)被 OR 在一起:`or_(cond_A, not_(cond_B))` → 匹配 A OR 非 B(几乎全库)。PubMed 语义:`A OR B NOT C` = `(A OR B) AND NOT C`。
- **影响**:任何 OR+NOT 混合查询(如 `cancer OR NOT review`)返回结果极大膨胀。
- **修复**OR 模式复用 `mixed` 模式的 NOT 分离逻辑:将 `UnaryExpression` NOT 条件分离出来独立 AND。
### Bug-R20-3 (MEDIUM): `is_pubmed_syntax()` 不识别小写布尔运算符
- **文件**`pubmed_query_parser.py:917`
- **根因**:布尔运算符正则 `r'\b(AND|OR|NOT)\b'` 缺少 `re.IGNORECASE`。`is_pubmed_syntax("cancer and tumor")` 返回 `False` → 查询不触发 PubMed 路径。
- **影响**:使用小写布尔运算符的 PubMed 查询丢失所有字段语义。
- **修复**`re.search(..., re.IGNORECASE)`。
### Bug-R20-4 (MEDIUM): 长格式字段标签不被识别
- **文件**`pubmed_query_parser.py``_ALL_FIELD_TAGS` + `_FIELD_TAG_MAP`
- **根因**`[Title]`、`[All Fields]`、`[MeSH Terms]` 等长格式标签不在 `_ALL_FIELD_TAGS` 中,被降级为普通 WORD。示例:`cancer[Title]` → 三个普通词。
- **修复**`_ALL_FIELD_TAGS` 新增 40+ 长格式标签;`_FIELD_TAG_MAP` 新增到规范内部名的映射。
- **验证**`cancer[Title]` → field="title"、`breast[MeSH Major Topic]` → field="MAJR"
### Bug-R20-5 (MEDIUM): 400 错误 URL 持久化导致刷新循环
- **文件**`SearchView.vue:379`
- **根因**`syncSearchToUrl()` 在 `finally` 中无条件运行,400 参数被持久化到 URL。刷新后恢复相同参数 → 再 400 → 死循环。
- **修复**`syncSearchToUrl()` 移到 `try` 块末尾(仅成功时同步)。
### Bug-R20-6 (LOW): 空括号 `[]` 产生空 Term
- **文件**`pubmed_query_parser.py:221-224`
- **根因**UNKNOWN_FIELD 匹配 `[]``strip('[]')` 产生空字符串 → 创建空 Term。
- **修复**`stripped` 为空时跳过。
### 审计结果汇总
| 审计维度 | 结果 |
|---------|------|
| R19 回归(AND 子组) | ✅ `sub_group_refs` 层级追踪 + 引擎层级处理 |
| OR 模式 NOT 语义 | ✅ 分离 NOT 条件独立 AND |
| 分词器/边界 | ✅ `re.IGNORECASE`、长格式标签、空括号跳过 |
| 前端集成 | ✅ 400 URL 持久化循环修复 |
---
## 第十七轮:第 17 轮审计修复(3 项)