← 中文资源

简体中文版本

基于 PostgreSQL 构建全文检索 API

一个基于 PostgreSQL tsvector 的完整全文检索 API:GIN 索引、加权排序、摘要高亮与查询解析测试。

阅读英文原文

项目概述

本项目在关系型数据库之上实现全文检索 API:以加权向量列让标题权重高于正文,用索引加速匹配,返回带高亮的片段,并为输入提示提供前缀匹配。要点是把检索交给索引与数据库内建排序,而不是在应用里重写一遍。

架构与关键决策

生成的向量列由源列自动合成,因此不会与内容失配,更新文档即更新向量;覆盖该列的索引使包含查询走索引而非全表扫描。查询路径把用户输入解析为词项查询,按内建排序函数排名,并返回高亮片段,高亮留在数据库可避免用第二个解析器重述查询。语言配置一旦选定即同时进入列与查询,改动即 schema 变更。

实施与测试

以固定语料断言排序,且语料中标题命中应高于正文命中;仅含停用词的查询断言无结果;断言高亮包裹命中词、前缀路径可命中部分词;断言词项匹配的查询计划确实使用索引。

限制与后续

  • 单语言配置,真正的多语言语料需按语言分列。
  • 除静态权重外无相关性调优。
  • 无模糊匹配与同义词,拼写纠错需额外索引。

代码示例

pnpm init
pnpm add pg express
pnpm add -D typescript tsx vitest
full-text-search-api/
├── src/search.ts
├── src/index-doc.ts
├── src/ranking.ts
├── db/schema.sql
├── tests/search.test.ts
└── package.json
ALTER TABLE docs ADD COLUMN search tsvector
  GENERATED ALWAYS AS (
    setweight(to_tsvector('english', coalesce(title,'')), 'A') ||
    setweight(to_tsvector('english', coalesce(body,'')), 'B')) STORED;
CREATE INDEX docs_search_idx ON docs USING GIN (search);
SELECT id, title, ts_rank_cd(search, query) AS rank
FROM docs, websearch_to_tsquery('english', $1) query
WHERE search @@ query ORDER BY rank DESC LIMIT 20;
ts_headline('english', body, query, 'MaxFragments=2, StartSel=<mark>, StopSel=</mark>') AS snippet
const q = tokens.slice(0, -1).join(' & ') + ' & ' + last + ':*';
pnpm vitest run