fix: 第20轮搜索审计修复 — AND子组层级/OR模式NOT/长格式标签等6项
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:
+64
-1
@@ -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 20,4 并行 agent:R19 回归/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 项)
|
||||
|
||||
Reference in New Issue
Block a user