From 8a4826220aaf56f781cb34166cd4d6fe774b8978 Mon Sep 17 00:00:00 2001 From: "34047007@qq.com" <34047007@qq.com> Date: Mon, 27 Jul 2026 14:23:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20D2=20=E4=B8=AD=E6=96=87=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=20+=20D3=20=E6=B7=B7=E5=90=88=E5=B8=83=E5=B0=94?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=20=E2=80=94=20PubMed=20=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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)。 --- backend/app/services/pubmed_query_parser.py | 31 +++++++++++++++++---- backend/app/services/search_engine.py | 6 ++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/backend/app/services/pubmed_query_parser.py b/backend/app/services/pubmed_query_parser.py index ddbef95..96deea1 100644 --- a/backend/app/services/pubmed_query_parser.py +++ b/backend/app/services/pubmed_query_parser.py @@ -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.""" diff --git a/backend/app/services/search_engine.py b/backend/app/services/search_engine.py index cac5768..50775e8 100644 --- a/backend/app/services/search_engine.py +++ b/backend/app/services/search_engine.py @@ -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