资讯详情

用 Postgres 构建第三代搜索引擎:从全文检索到 PostgresML 多级重排实战

📅 2026/10/8 1:38:12 | 华诺云谱 👁 阅读
用 Postgres 构建第三代搜索引擎:从全文检索到 PostgresML 多级重排实战
后端人工智能机器学习RAG向量数据库【免费下载链接】postgresmlPostgres with GPUs for ML/AI apps.项目地址https://gitcode.com/gh_mirrors/po/postgresml点击查看免费下载如果你想让搜索结果的相关性真正上一个台阶不要继续在昂贵的词频统计如 TF-IDF、BM25上堆算力而是应该引入新的数据源。本文以 PostgresML 官方博客 postgres-full-text-search-is-awesome.md 为骨架完整讲解 PostgreSQL 全文检索的内置能力、为何 ElasticSearch 式的分布式中途 join 是死胡同以及如何在单一 SQL 查询里用 PostgresML 的向量相似度与梯度提升树模型完成召回—重排—个性化的三级搜索流水线。读完本文你将掌握Postgres 词频排名的基本原理、把特征与语料放在同一数据库中的架构收益以及一段可直接运行的第三代搜索引擎 SQL 示例。核心论点想真正提升搜索结果不要依赖昂贵的 O(n×m) 词频统计去获取新的数据源。正是相关性的关系本质决定了关系型数据库才是理想的搜索引擎。Postgres 全文检索够用的基础能力在深入第三代搜索架构之前先明确 PostgreSQL 原生全文检索Full Text Search已经提供的五类能力这也是postgres-full-text-search-is-awesome.md开篇强调的good enough清单词干还原Stemming把复数、过去式等形态变化归并为词根例如second、secondly都能匹配同一个词位排名与加权Ranking / Boost用ts_rank计算词频排名并可对标题、正文分别加权多语言支持Multiple languages内置多种语言配置例如english、simple等词典配置模糊搜索Fuzzy search可容忍用户拼写错误重音支持Accent support处理带重音字符的语言。这些能力由 PostgreSQL 的两个核心函数提供详见官方指南 improve-search-results-with-machine-learning.mdto_tsvector(config, text)把纯文本转成可索引的tsvector加速召回to_tsquery(config, text)把纯文本查询转成布尔规则and、or、not、phrasetsquery通过操作符与tsvector匹配。一个最小的关键词搜索示例SELECT * FROM documents WHERE to_tsvector(english, body) to_tsquery(english, second);用生成列 GIN 索引为生产级召回做准备默认情况下上面的查询会对全表做顺序扫描sequential scan每条记录的body都要现场转换成tsvector再比较。对小表没问题但生产环境必须用索引加速。推荐的标准化做法是先把tsvector落成生成列GENERATED column再建 GINGeneralized Inverted Index索引ALTER TABLE documents ADD COLUMN title_and_body_text tsvector GENERATED ALWAYS AS (to_tsvector(english, title || || body )) STORED; CREATE INDEX documents_title_and_body_text_index ON documents USING GIN (title_and_body_text);生成列会自动回填历史数据、随写入自动更新GIN 索引则预计算了每个关键词包含哪些文档的倒排表从而跳过顺序扫描。观察生成列的输出可以发现词位已经过词干还原并带位置信息位置信息支持短语匹配同时the、is、of等停用词stopwords被剔除——这是词频检索的常规优化。ts_rank 的词频排名机制Postgres 提供的ts_rank计算每个关键词在文档中的词频Term FrequencyTF。官方指南给出直观的算术模型文档 5 个词中命中 2 个关键词时ts_rank为2 / 5 0.4命中越多、总词数越少分数越高多个查询词用 OR|组合时分子分母相加例如第一个词命中2/5、第二个词命中1/5则ts_rank (2 1) / (5 5) 0.3。一个带排名的查询SELECT ts_rank(title_and_body_text, to_tsquery(english, second | title)), * FROM documents ORDER BY ts_rank DESC;注意ts_rank本身不会过滤不相关的文档纯ORDER BY会把ts_rank为 0 的记录也排进来。因此生产查询要同时保留匹配过滤可走索引并加LIMIT控制分页规模。简单加权Boosting一个直观的优化是区分标题与正文的相关性权重——标题命中通常比正文命中更相关SELECT ts_rank(title, to_tsquery(english, second | title)) AS title_rank, ts_rank(body, to_tsquery(english, second | title)) AS body_rank, * FROM documents ORDER BY (2 * title_rank) body_rank DESC;但这立刻引出问题标题该乘 2 倍还是 10 倍正文更长导致ts_rank被长度稀释是不是反而该加权正文手工调公式很难覆盖所有查询——这正是把调权重交给机器学习去学的理由也就是后文Learning to Rank的动机。为什么 ElasticSearch 式的分布式 join 是死胡同postgres-full-text-search-is-awesome.md给出了两个关键论断理解它们才能理解 PostgresML 搜索架构的选择用 TF-IDF / BM25 这类词频统计去提升相关性方向本身就是错的。学术界的算法往往消耗数量级更多的算力却只换来边际收益。BM25 是 1970 年代提出的检索模型ElasticSearch 号称state of the art的其实是它的改良版为 BM25 计算 IDFInverse Document Frequency会严重拖累索引性能。IDF 需要全局文档统计在分布式系统里为这一收益存疑的统计量引入跨节点协调会招致分布式计算的各类经典故障网络延迟、分区、节点故障、时钟偏差等。更关键的是分布式系统在数据 join 上的结构性缺陷ElasticSearch 官方也承认在朴素的分布式系统里读时read timejoin 数据成本高得离谱于是只能退而在索引时indexing time急切地eagerlyjoin 并反规范化这进一步抬高成本、放大更新放大效应——任何一个数据源变化都要重建整篇甚至大范围语料的索引。Postgres 的选择恰恰相反它不计算 IDF换来可负担的索引与查询开销却提供了功能最完整的关系型数据平台。正确做法是把数据保持规范化留在 Postgres 里在查询时随时 join 额外的相关数据索引和检索一个规范化语料的算力比反规范化版本低多个数量级这意味着在需要分布式之前你可以撑得更久甚至永远不需要不必再维护在系统间搬运更新的管道省下的时间可以投入到真正提升相关性的新数据源上分布式的需求真到来时也可以更有智慧地规划而不是被索引性能逼着走朴素分片。把特征库与语料放进同一个数据库PostgresML 把上述思路推到极致你可以把特征库feature store直接加载进与搜索语料相同的数据库。每种数据源都可以是独立的表、拥有独立的更新节奏而不必在某个特征变化时把整篇文档反规范化回 ElasticSearch更不必重索引大范围语料。这一架构的直接收益是用一条 SQL 查询就能完成多轮重排re-ranking、剪枝pruning与个性化personalization逐级叠加更昂贵也更精准的评分器。postgres-full-text-search-is-awesome.md列举的三级评分器基础词频相关性basic term relevancets_rank嵌入相似度embedding similaritiespgml.cosine_similarityXGBoost / LightGBM 推理pgml.predict。这些查询借助 Postgres 的多种索引策略在大型生产规模语料上也能在毫秒级完成而且不需要给你的技术栈新增任何基础设施。第三代搜索引擎单条 SQL 完成三级重排下面这段完整示例来自原文档演示了一个第三代搜索引擎3rd generation search engine的三级流水线先用词频召回候选集再用向量相似度重排最后用梯度提升树模型做个性化精排。WITH子句的每一层都把上一步的结果进一步剪枝让最昂贵的模型只作用于最少数量的文档。WITH query AS ( -- 构造查询上下文参数通常来自应用层 SELECT -- 关键词查询my OR search OR terms tsquery(my | search | terms) AS keywords, -- 用于后续个性化的用户 ID 123456 AS user_id ), first_pass AS ( SELECT *, -- 计算关键词在文档中的词频 ts_rank(documents.full_text, keywords) AS term_frequency -- 基础语料存放在 documents 表 FROM documents -- 只保留匹配关键词的文档 WHERE documents.full_text query.keywords -- 按词频排序 ORDER BY term_frequency DESC -- 剪枝到合理大小的候选集 LIMIT 10000 ), second_pass AS ( SELECT *, -- 第二遍评分跨嵌入的余弦相似度 pgml.cosine_similarity(document_embeddings.vector, user_embeddings.vector) AS similarity_score FROM first_pass -- 从 documents 之外取更多数据 JOIN document_embeddings ON document_embeddings.document_id documents.id JOIN user_embeddings ON user_embeddings.user_id query.user_id -- 当然我们正在重排 ORDER BY similarity_score DESC -- 进一步剪枝为更昂贵的排序保留表现最好的结果 LIMIT 1000 ), third_pass AS ( SELECT *, -- 用 xgboost 生成最终评分 pgml.predict(search relevance model, ARRAY[session_level_features.*]) AS final_score FROM second_pass JOIN session_level_features ON session_level_features.user_id query.user_id ) SELECT * FROM third_pass ORDER BY final_score DESC LIMIT 100;三个层级的分工与数据来源可以这样理解层级评分函数数据来源候选规模first_passts_rank词频documents全文10000second_passpgml.cosine_similarity嵌入相似度document_embeddings、user_embeddings1000third_passpgml.predictXGBoost 等学习模型session_level_features100每个数据源独立成表、独立更新文档、文档嵌入、用户嵌入、会话级特征互不干扰任何单一信号变化都只需要更新自己的表再跑一遍查询即可无需重索引。第二级pgml.cosine_similarity的底层实现second_pass使用的pgml.cosine_similarity是 PostgresML 扩展内置的向量函数定义于 pgml-extension/src/vectors.rs。它同时提供float4与float8两个重载版本底层直接调用 BLAS 线性代数库完成点积与范数计算#[pg_extern(immutable, parallel_safe, strict, name cosine_similarity)] fn cosine_similarity_s(vector: Arrayf32, other: Arrayf32) - f32 { unsafe { let vector: [f32] RawArray::from_array(vector).unwrap().data().as_ref(); let other: [f32] RawArray::from_array(other).unwrap().data().as_ref(); let len vector.len() as i32; let dot blas::sdot(len, vector, 1, other, 1); let a_norm blas::snrm2(len, vector, 1); let b_norm blas::snrm2(len, other, 1); dot / (a_norm * b_norm) } }即dot / (a_norm * b_norm)cosine 相似度把两个向量归一化后比较方向而非长度天然适合文档嵌入与用户偏好嵌入之间的语义相似度计算。该扩展还提供dot_product、distance_l1、distance_l2、norm_l2、normalize_l2等一系列向量操作且均标注immutable, parallel_safe意味着它们可以被 Postgres 并行执行并进入索引与缓存优化路径。单元测试vectors.rs直接用 SQL 验证了相似度数值的确定性SELECT pgml.cosine_similarity(ARRAY[1,2,3]::float4[], ARRAY[1.0, 2.0, 3.0]::float4[]);关于文档嵌入的生成PostgresML 还支持用pgml.embed配合GENERATED ALWAYS AS生成列让嵌入随文本写入自动维护见 dimensionality-reduction.mdCREATE TABLE documents_with_embeddings ( id serial PRIMARY KEY, body text, embedding float[] GENERATED ALWAYS AS (pgml.normalize_l2(pgml.embed(intfloat/e5-small-v2, body))) STORED );第三级pgml.predict的模型部署与调用third_pass里的pgml.predict(search relevance model, ARRAY[session_level_features.*])是 PostgresML 的核心价值主张——用项目名project_name调用自动部署的最优模型做在线推理。从 pgml-extension/src/api.rs 的实现看predict有一组针对f32/f64/i16/i32/i64/bool的特征类型重载全部通过Project::get_deployed_model_id(project_name)解析出当前部署的模型再交给Model::predict执行#[pg_extern(immutable, parallel_safe, strict, name predict)] fn predict_f32(project_name: str, features: Vecf32) - f32 { predict_model(Project::get_deployed_model_id(project_name), features) }训练侧官方指南 improve-search-results-with-machine-learning.md 展示了Learning to Rank的完整闭环把用户点击行为记录成(title_rank, body_rank, clicked)的训练表然后用pgml.train训练回归模型把相关性重新定义为用户点击结果的概率SELECT * FROM pgml.train( project_name Search Ranking, task regression, relation_name search_result_clicks, y_column_name clicked );pgml.train会返回项目名、任务类型、算法与是否部署deployed四列结果。训练完成后pgml.predict(Search Ranking, array[title_rank, body_rank])即可用于线上重排。针对 XGBoost、LightGBM 等梯度提升树模型PostgresML 还分别提供了 Rust 绑定实现见 xgboost.rs 与 lightgbm.rs保证了这些现代 SOTA 级排序模型能在数据库进程内直接推理。若想对比不同算法的效果也可以给pgml.predict传具体的model_id而非项目名配合pgml.deploy手动控制部署。从三级评分到第四级学习模型原文档最后给读者留了一个进阶练习把上述三个分数词频、嵌入相似度、XGBoost 输出组合成单个代数函数用于排序然后把这套组合函数本身再交给第四个学习模型去学习权重。这与Learning to Rank的核心理念一脉相承——当你有足够多的点击、会话与个性化数据时让模型决定每个信号该占多大比重通常比手工调2 * title_rank body_rank这类启发式公式更可靠。PostgresML 的路线是让每个信号独立成表、独立更新再用一条 SQL 把它们 join 在一起做多级重排因此向第四级模型演进时你只需新增一个特征表与一次pgml.train而不必重写整个搜索架构。小结postgres-full-text-search-is-awesome.md的核心主张可以浓缩为一句话别在词频统计上烧算力去获取新的数据源。Postgres 用功能最全的关系型平台换掉了 IDF 的索引负担ElasticSearch 则在读时 join 太贵与索引时 join 更贵之间陷入死胡同。PostgresML 则顺势把特征库与语料放进了同一个数据库让三级重排词频 → 嵌入相似度 → 梯度提升树在一条 SQL、毫秒级、零新增基础设施的条件下得以实现。如果你想亲手验证可以在 PostgresML Gym 中通过交互式笔记本interactive notebook在真实 Postgres 数据库里训练搜索相关性模型、跑通上面的完整示例并尝试把三个分数组合成单一排序函数、再演进为第四级学习模型。相关的进阶材料可直接阅读仓库内文档improve-search-results-with-machine-learning.md、embeddings 指南以及扩展的向量与推理实现源码 vectors.rs 与 api.rs。赞分享后端人工智能机器学习RAG向量数据库【免费下载链接】postgresmlPostgres with GPUs for ML/AI apps.项目地址https://gitcode.com/gh_mirrors/po/postgresml点击查看免费下载相关推荐基于 PostgresML 构建多层级 ML 搜索排序系统从关键词检索到学习排序与向量重排基于 PostgresML 构建多层级 ML 搜索排序系统从关键词检索到学习排序与向量重排 摘要 本文以 pgml cms/blog/how to impro后端人工智能机器学习RAG向量数据库CANN ops-transformer 算子详解MoeInitRoutingQuant 的 MoE 路由量化实现与 aclnn 调用实践CANN ops transformer 算子详解MoeInitRoutingQuant 的 MoE 路由量化实现与 aclnn 调用实践 导读 MoeIni后端人工智能机器学习RAG向量数据库fairseq 潜层深度 TransformerLatent Depth基于层选择后验分布的多语言机器翻译训练与推理实战fairseq 潜层深度 TransformerLatent Depth基于层选择后验分布的多语言机器翻译训练与推理实战 导读 本文基于 fairseq后端人工智能机器学习RAG向量数据库上一篇MPC视频渲染器完整安装配置终极指南下一篇swift-corelibs-libdispatch 架构解析BlocksRuntime、event 模块与 shims 层的设计思想创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑