当前位置: 首页 > news >正文

用postgresql实现es搜索功能 - 孙龙

用postgresql实现es搜索功能

如果搜索型号字段 用户输入 3458 怎么把这些搜索出来 MAX3485ESA (UMW) , yyX34677785ESA (UMW), ddX3d4677785ESA (UMW) ,像 es 就可以实现

 

PostgreSQL 实现 ES 式 Fuzzy 搜索:用户输 3458 怎么匹配到 MAX3485ESA

一、先搞清楚 ES 是怎么做到的

ES 对这种搜索用了两层机制:

 

ES 机制

原理对你的例子
ngram 分词器 MAX3485ESA 拆成 ma,ax,x3,34,48,85,5e... 建倒排索引 用户输 3458 拆成 34,45,58,倒排命中包含 34 的文档
fuzzy query 基于 Levenshtein 编辑距离,允许字符替换 / 插入 / 删除 34583485 编辑距离 = 2(5 和 8 换位),在容错范围内
两者结合:ngram 负责快速召回候选集,fuzzy 负责容错排序。

二、PostgreSQL 的 4 种实现方案

方案 1:pg_trgm + word_similarity(最简单,先试这个)

pg_trgm 原生有一个被低估的函数 word_similarity,它不是比较整个字符串,而是在目标字符串中找与查询词最相似的子串—— 这正是型号搜索需要的。
SET pg_trgm.word_similarity_threshold = 0.1;  -- 调低阈值,短词才够灵敏SELECT model, word_similarity('3458', model) AS sml
FROM components
WHERE model %> '3458'        -- word_similarity 操作符,走 GIN 索引
ORDER BY sml DESC
LIMIT 20;

效果分析:

型号最相似子串word_similarity
MAX3485ESA(UMW) 3485 1 - edit_dist(3458,3485)/4 = 0.5
yyX34677785ESA 34677785 ~0.25
ddX3d4677785ESA 3d467785 ~0.25
 
MAX3485ESA 能排到前面,但 3458 只有 4 个字符,trigram 只有 34,45,58 三个,短查询词是 pg_trgm 的软肋。

方案 2:自定义 ngram 数组 + GIN(最接近 ES ngram,推荐)

自己实现 ES 的 ngram 分词逻辑,完全可控:
-- 1. ngram 拆分函数(不可变函数,才能用于生成列)
CREATE OR REPLACE FUNCTION ngram(text, int) RETURNS text[] AS $$SELECT ARRAY(SELECT substring(lower($1) FROM i FOR $2)FROM generate_series(1, greatest(0, length($1) - $2 + 1)) AS i);
$$ LANGUAGE sql IMMUTABLE;-- 2. 生成列:同时存 2-gram 和 3-gram
ALTER TABLE components ADD COLUMN model_ngram text[]GENERATED ALWAYS AS (COALESCE(ngram(model, 2), '{}') || COALESCE(ngram(model, 3), '{}')) STORED;-- 3. GIN 索引
CREATE INDEX idx_model_ngram ON components USING GIN (model_ngram);

查询:用户输入也拆 ngram,按交集数量排序(交集越多越相关):

SELECT model,array_length(model_ngram && ngram('3458', 2), 1) AS hit2,array_length(model_ngram && ngram('3458', 3), 1) AS hit3
FROM components
WHERE model_ngram && ngram('3458', 2)    -- 至少有一个 2-gram 相交(走GIN)
ORDER BY hit3 DESC, hit2 DESC
LIMIT 20;

效果:

型号2-gram 交集3-gram 交集排名
MAX3485ESA 34(1 个) 靠前
yyX34677785ESA 34,78,85(3 个) 更靠前
ddX3d4677785ESA 34,78,85(3 个) 并列
这个方案对 34677785 这种长数字串匹配更好,但对 34583485 这种换位错误召回率一般(因为 ngram 不重叠)。
 
 
 

方案 3:ngram 粗筛 + Levenshtein 精排(兼顾召回和精度)

组合方案,最接近 ES 的 ngram + fuzzy 双层架构:
SET pg_trgm.word_similarity_threshold = 0.05;SELECT c.model,levenshtein(substring(c.model from '\d+'), '3458') AS num_dist,word_similarity('3458', c.model) AS wsml
FROM components c
WHERE c.model %> '3458'                          -- 第一层:trigram 粗筛(走索引)AND levenshtein(substring(c.model from '\d+'), '3458') <= 3  -- 第二层:数字部分编辑距离精排
ORDER BY num_dist ASC, wsml DESC
LIMIT 20;

关键技巧:substring(model from '\d+') 提取型号中的纯数字部分(348534677785),只对数字部分算编辑距离 —— 因为用户输的 3458 是数字,型号的核心区分也是数字段。

 
型号提取数字3458编辑距离
MAX3485ESA 3485 2(5↔8 换位)
yyX34677785ESA 34677785 5
ddX3d4677785ESA 34677785 5
 
MAX3485ESA 编辑距离最小,排第一。这正是你要的效果。

方案 4:pgroonga 扩展(性能最接近 ES,终极方案)

pgroonga 是基于 Groonga 的 PG 全文检索扩展,原生支持 ngram 分词和模糊搜索,性能接近 ES,且不用维护独立集群:
 
CREATE EXTENSION pgroonga;CREATE INDEX idx_model_pgroonga ON components USING pgroonga (model pgroonga.text_full_text_search_ops);-- 模糊搜索(类似 ES fuzzy)
SELECT * FROM components WHERE model &@~ '3458';-- 编辑距离容错
SELECT * FROM components WHERE model &@* '3458';

&@* 操作符就是模糊匹配,内部用 Groonga 的倒排索引 + 编辑距离,100 万数据毫秒级。

 
三、方案对比与推荐
方案召回率精度性能 (100 万)复杂度适合场景
pg_trgm % 50~150ms 极低 通用模糊,短词弱
pg_trgm %> 中高 50~150ms 极低 型号子串匹配首选
自定义 ngram 数组 30~100ms 需要可控分词
ngram+levenshtein 80~200ms 中高 型号搜索最佳实践
pgroonga 极高 10~50ms 追求 ES 级体验
fuzzystrmatch 直接算 极高 2~5 秒 ❌ 不能用,无索引
 
 

四、针对你的型号搜索,我的建议

元器件型号有个特点:字母前缀 + 数字主体 + 字母后缀,用户搜索时主要输数字部分。所以最优架构是:
-- 1. 提取数字部分作为生成列
ALTER TABLE components ADD COLUMN model_num textGENERATED ALWAYS AS (COALESCE(substring(model from '\d+'), '')) STORED;-- 2. 数字部分建 trigram 索引
CREATE INDEX idx_model_num_trgm ON components USING GIN (model_num gin_trgm_ops);-- 3. 完整型号也建 trigram 索引(兜底)
CREATE INDEX idx_model_trgm ON components USING GIN (model gin_trgm_ops);-- 4. 查询:数字部分 word_similarity 粗筛 + 编辑距离精排
SET pg_trgm.word_similarity_threshold = 0.1;SELECT model, brand,word_similarity('3458', model_num) AS num_sml,levenshtein(model_num, '3458') AS dist
FROM components
WHERE model_num %> '3458'            -- 数字子串相似度粗筛(走索引)OR model %> '3458'                -- 完整型号兜底
ORDER BY CASE WHEN levenshtein(model_num, '3458') <= 2 THEN 0 ELSE 1 END,  -- 精确匹配优先num_sml DESC
LIMIT 30;
这个方案的效果:
  • 用户输 3458MAX3485ESA 排第一(数字部分3485编辑距离 = 2)
  • 用户输 3485 → 精确匹配所有含3485的型号
  • 用户输 MAX → 匹配所有 MAX 前缀
  • 用户输 ESA → 匹配所有 ESA 后缀
如果这个方案还不够(比如需要支持中文同义词、拼写自动纠错、分面聚合),再上 pgroonga 或直接迁 ES。100 万数据量,pg_trgm + 生成列的组合完全够用。
 
 
 
 
 
 
 
 
 
http://www.jsqmd.com/news/1400524/

相关文章:

  • 文件被 rm 之后还能救回来吗?一文掌握 trash-cli 命令行回收站的完整用法
  • 如何用 Navicat Keygen Tools 完成离线激活:从换公钥到生成激活码的完整实战
  • 掌心藏着一只“电子海豚“:Flipper Zero BadUSB脚本集锦上手指南
  • 你的144Hz显示器,在Roblox里可能一直在空转:Roblox FPS解锁器完整上手指南
  • 2026年8月北京五年内曾酒驾又醉驾从重处罚律所辩护参考:5家律所应对从重处罚辩护实务 - 品牌深度评测
  • boolinq完全指南:从安装到精通,一站式掌握C++ LINQ编程
  • 有哪些方法可以快速提升淘宝新店流量?新手零成本起流全攻略
  • 2026甄选:粤泓(深圳)建筑咨询有限公司——电力工程监理资质代办的全程护航实力之选 - 卓企推荐
  • 广州电商财税机构选型指南:2026金税四期下的合规筛选标准与口碑机构参考 - 互联网科技品牌测评
  • 智谱AI ZCode智能编程助手实战指南:从概念到工程集成
  • 道脉相续——古圣路标与当代拓扑新解043
  • 微信逆向入门:解密 ipa 之前,先搞懂这 3 个关键问题
  • 开源技术与Web3.0融合:从开发工具到去中心化治理
  • 【实践案例】文档工程师帮助业务避坑案例
  • 2026论文降重与去 AI 痕迹工具深度测评:逢君学术为核心的合规写作方案 - 互联网科技品牌测评
  • 记一次服务器被入侵(木马,挖矿)的排查过程
  • Claude Desktop中文补丁怎么装?3分钟汉化AI助手界面的完整指南
  • 龙泉驿封闭单招集训|成都融创单招2027届备考指南 - 成都单招培训
  • 2026 年雨山可靠的保温板推广公司联系方式,你家房屋每年多耗30%电费?难怪邻居都悄悄用上这玩意儿了-抖策盈AI豆包短视频推广 - 行业严选官
  • SOFARegistry Admin API详解:轻松管理服务注册中心
  • 将WIN10的wifi上网分享给以太网接口/远程桌面问题
  • 运筹学在供应链管理中的核心应用与实践
  • Android Emulator hypervisor driver 安装避坑全攻略:从零到一给安卓模拟器加速的完整实操手册
  • 光照与阴影优化:游戏里最“烧钱“的视觉大户
  • 2026年广州电商财税服务机构实测报告:监管趋严下的合规选型参考 - 互联网科技品牌测评
  • 界面控件DevExpress Blazor UI v22.2 - 支持.NET 7
  • 把背单词藏进 Windows 通知栏,ToastFish 帮你每天白赚 10 分钟
  • 薪资结构解析:从底薪绩效到劳动权益的职场必修课
  • 3分钟搞定Figma中文界面:FigmaCN中文插件完整使用指南
  • 新兴多智能体系统的模式与问题