fix: 第29轮搜索审计修复 — 缺失导入/中文精确短语ILIKE/子组日期标记/开区间范围/NULL安全等8项
This commit is contained in:
@@ -2155,3 +2155,85 @@ if _PARTIAL_DATE_RE.match(end_val):
|
||||
- ✅ **1007 tests passed, 0 failed**
|
||||
- 全部搜索测试通过(parser 36 + search engine 集成测试)
|
||||
- 无回归
|
||||
|
||||
## R29 (2026-07-29): 第四轮并行审计修复
|
||||
|
||||
### 审计范围
|
||||
|
||||
4 路并行 agent,分别覆盖:
|
||||
1. `pubmed_query_parser.py` 语法正确性(递归下降解析器)
|
||||
2. `search_engine.py` 条件构建(_pubmed_conditions 全字段路径)
|
||||
3. 纯文本降级路径(_is_flat_text 决策、缓存键、引号短语、facet 一致性)
|
||||
4. 前端搜索参数映射(前后端参数完整性、URL 同步、分页)
|
||||
|
||||
### R29-1 (P0): `_expand_partial_date` 未导入
|
||||
|
||||
**文件**:[search_engine.py:1696](backend/app/services/search_engine.py#L1696)
|
||||
|
||||
**根因**:`_single_term_condition()` 的 YYYY-MM 部分日期分支调用 `_expand_partial_date()` 但该函数未从 `pubmed_query_parser` 导入。导入语句只导入了 `_PARTIAL_DATE_RE` 和 `_validate_date_str`。
|
||||
|
||||
**影响**:组内 YYYY-MM 日期(如 `(2024-06[DP] OR cancer)`)触发 `NameError`,被 `except Exception` 吞没后降级到纯文本。
|
||||
|
||||
**修复**:添加 `_expand_partial_date` 到 inline import。
|
||||
|
||||
### R29-2 (P0): 非日期范围 field 标签混入文本
|
||||
|
||||
**文件**:[pubmed_query_parser.py:1111-1115](backend/app/services/pubmed_query_parser.py#L1111)
|
||||
|
||||
**根因**:`_parse_range()` 对非日期范围(如 `term1:term2[MH]`)的 fallthrough 路径将 `[MH]` 追加入 term 文本,导致 `text="term1:term2[MH]"` 而 `Term.field="MH"` 已正确设置。引擎按 `field` 路由(正确到 MeSH 路径),但 `text` 中的 `[MH]` 被作为字面搜索词。
|
||||
|
||||
**修复**:去除 `[{field}]` 文本拼接,只保留 `start_val:end_val`。
|
||||
|
||||
### R29-3 (P0): 中文精确短语缺少 title/abstract ILIKE
|
||||
|
||||
**文件**:[search_engine.py:1789-1798](backend/app/services/search_engine.py#L1789)
|
||||
|
||||
**根因**:`_field_condition("all", exact=True)` 分支使用 `phraseto_tsquery('english', ...)` 无法匹配非英文文本(如中文)。缺少 title/abstract ILIKE 回退。非精确分支(line 1832)正确包含这些 ILIKE。
|
||||
|
||||
**影响**:`"肺癌"[TIAB]` 等中文精确短语返回零结果。
|
||||
|
||||
**修复**:在 exact 分支中添加 `title.ilike(pat)` 和 `abstract.ilike(pat)`。
|
||||
|
||||
### R29-4 (P1): 子组日期范围标记被丢弃
|
||||
|
||||
**文件**:[search_engine.py:1313-1314](backend/app/services/search_engine.py#L1313)
|
||||
|
||||
**根因**:子组(sub-group,由 `_parse_or_expr` AND 集群 OR 拆分产生)的日期范围标记在 child processing 循环中被 `continue` 跳过。
|
||||
|
||||
**影响**:`(A OR B AND 2020:2024[DP])` 等查询中日期范围丢失。
|
||||
|
||||
**修复**:子组循环中添加 `_build_marker_condition` + NULL 安全 NOT,逻辑与父组一致。
|
||||
|
||||
### R29-5 (P1): 开区间日期范围不支持
|
||||
|
||||
**文件**:[pubmed_query_parser.py:930-938](backend/app/services/pubmed_query_parser.py#L930)
|
||||
|
||||
**根因**:`:2024[DP]`(截止到 2024)和 `2024:[DP]`(从 2024 起)的 COLON 在首个位置或无第二个 NUMBER 的模式不被范围检测匹配。
|
||||
|
||||
**修复**:在 `_parse_atom` 中增加 `:NUMBER[FIELD]` 和 `NUMBER:[FIELD]` 检测,路由到新方法 `_handle_date_range_edge()`,正确设置全局属性、标记和 `_neg_single_dates`。
|
||||
|
||||
### R29-6 (P2): field_map NOT 条件缺少 NULL 安全
|
||||
|
||||
**文件**:[search_engine.py:874-881](backend/app/services/search_engine.py#L874)
|
||||
|
||||
**根因**:`[TI]`/`[AB]`/`[AU]` 等字段标签的否定词使用裸 `not_(cond)`,导致字段为 NULL 的行被错误排除。
|
||||
|
||||
**修复**:添加 `_NULL_SAFE_COL_MAP`,对每个字段的否定条件使用 `or_(not_(cond), col.is_(None))`。
|
||||
|
||||
### R29-7 (LOW): PRISMA 导出正则不匹配含空格的字段标签
|
||||
|
||||
**文件**:[pubmed_query_parser.py:1301](backend/app/services/pubmed_query_parser.py#L1301)
|
||||
|
||||
**修复**:`[\w/:]+` → `[\w/: -]+` 以匹配 `[MeSH Terms]`、`[Date - Publication]` 等。
|
||||
|
||||
### R29-8 (LOW): `is_free_full_text` 在 `is_oa` 已设时被跳过
|
||||
|
||||
**文件**:[search_engine.py:563](backend/app/services/search_engine.py#L563)
|
||||
|
||||
**修复**:移除 `is_oa is None` 守卫,`is_free_full_text` 始终独立生效。
|
||||
|
||||
## 验证
|
||||
|
||||
- ✅ **1007 tests passed, 0 failed**
|
||||
- 全部搜索测试通过
|
||||
- 无回归
|
||||
|
||||
Reference in New Issue
Block a user