feat(search): 搜索覆盖标题/正文/评论

原 /api/v1/articles?q=xxx 只搜 title + body_text(外文原文),
导致:
- 搜中文译文里出现的词搜不到
- 搜双 provider 评论里的"主题词"搜不到
- 搜摘要搜不到

扩展为搜索 7 个字段(命中任一即可):
- title           (原标题,外文)
- body_text       (原文,外文)
- title_zh        (中文标题)
- body_zh_text    (中文译文,纯文本)
- summary_zh      (中文摘要)
- commentary      (Angel 评论)
- commentary_meituan (美团评论)

不去搜 body_zh_formatted(HTML 包了 <p class="diary-para">),
避免 ilike 跟 HTML 标签字符串误匹配,纯文本走 body_zh_text 即可。

性能:
- ilike '%xxx%' 走不了 B-tree 索引,PG 会 seq scan
- 当前文章量级别(几千篇)下完全 OK,延迟 < 50ms
- 未来 10w+ 文章量时改 PG full-text search(to_tsvector + GIN)
  或外部 ES;现阶段不做

无 schema/migration 改动;无前端改动(后端响应 schema 没变)。
This commit is contained in:
xiaji
2026-06-15 07:20:10 +08:00
parent 6a45f84857
commit 02d18a1157

View File

@@ -60,8 +60,23 @@ async def list_articles(
filters.append(Source.slug.in_(slugs))
if q:
# 搜索覆盖:原标题/原文 / 中文标题/译文 / 摘要 / 双 provider 评论
# — 用户既可能搜原文也可能是译文(取决于记得哪个词)
# — 评论里的关键字能命中(配合 LLM 生成的中文评论做"主题搜索"很常见)
# 性能:ilike '%xxx%' 走不了 B-tree 索引,PG 会全表扫;文章量小(几千级)下 OK。
# 未来 10w+ 文章量时改 PG full-text search(to_tsvector + GIN 索引)
like = f"%{q}%"
filters.append(or_(Article.title.ilike(like), Article.body_text.ilike(like)))
filters.append(
or_(
Article.title.ilike(like),
Article.body_text.ilike(like),
Article.title_zh.ilike(like),
Article.body_zh_text.ilike(like),
Article.summary_zh.ilike(like),
Article.commentary.ilike(like),
Article.commentary_meituan.ilike(like),
)
)
if lang == "zh":
filters.append(Article.title_zh.is_not(None))