fix: D2 中文搜索 + D3 混合布尔语义 — PubMed 搜索路径修复
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s

D2: PubMed 路径搜索中文(不带字段标签)时,plainto_tsquery("english")
    对中文返回空,导致零结果。新增 ILIKE fallback 检测中文字符。
D3: A OR B AND C 被解析器拉平为 A OR B OR C。修改 _parse_or_expr
    保留 AND cluster 分组,引擎正确产生产出 A OR (B AND C)。
This commit is contained in:
34047007@qq.com
2026-07-27 14:23:10 +08:00
parent c1e38386f8
commit 8a4826220a
2 changed files with 32 additions and 5 deletions
+26 -5
View File
@@ -473,15 +473,36 @@ class PubmedQueryParser:
result.plain_terms.append(term)
def _parse_or_expr(self, result: ParsedPubmedQuery) -> list[Term]:
"""or_expr → and_expr (OR and_expr)*"""
left = self._parse_and_expr(result)
"""or_expr → and_expr (OR and_expr)*
P7-D3: Preserve AND clusters when OR is mixed (e.g. ``A OR B AND C``).
Collect each ``_parse_and_expr`` result as a separate cluster. When OR
is found AND a cluster has >1 term (implicit AND), wrap it in a group
so the engine produces ``A OR (B AND C)`` instead of ``A OR B OR C``.
"""
clusters = [self._parse_and_expr(result)]
had_or = False
while self.peek().type == TokenType.OR:
had_or = True
self.advance()
if self.peek().type == TokenType.EOF:
break # trailing OR, ignore silently
right = self._parse_and_expr(result)
left.extend(right)
return left
clusters.append(self._parse_and_expr(result))
if not had_or:
return clusters[0] if clusters else []
# OR present: group any AND-cluster with >1 term
all_terms: list[Term] = []
for cluster in clusters:
if len(cluster) > 1 and not any(t.group_id >= 0 for t in cluster):
gid = len(result.groups)
for t in cluster:
t.group_id = gid
result.groups.append(cluster)
result.group_operators.append("and")
all_terms.extend(cluster)
return all_terms
def _is_primary_start(self, token: Token) -> bool:
"""Check if token could start a primary expression."""
+6
View File
@@ -1195,6 +1195,12 @@ class AdvancedSearchEngine:
cast(GlobalLiterature.pmid, String).ilike(like_val),
GlobalLiterature.doi.ilike(like_val),
)
# P7-D2: Chinese → ILIKE fallback (tsvector is English-only)
if re.search(r'[一-鿿㐀-䶿豈-﫿]', term):
return or_(
GlobalLiterature.title.ilike(like_val),
GlobalLiterature.abstract.ilike(like_val),
)
return GlobalLiterature.search_tsv.op("@@")(func.plainto_tsquery("english", term))
@staticmethod