PostgreSQL pg_trgm 模糊搜索完全指南
作者:中年如酒
什么是 pg_trgm?
pg_trgm 是 PostgreSQL 的一个扩展模块,用于实现基于三元组(trigram)的模糊文本搜索。它可以帮你找到拼写错误、部分匹配的文本,非常适合搜索功能。
三元组(Trigram)原理
三元组是将文本分解为连续的三个字符的组合:
SELECT show_trgm('iPhone');
-- 结果: {" i"," ip","hon","iph","ne ","one","pho"}
通过比较两个字符串的三元组重叠度,可以计算相似度。
一、环境准备
1. 创建扩展
CREATE EXTENSION IF NOT EXISTS pg_trgm;
2. 创建测试表
CREATE TABLE products ( tenant_id uuid, id integer, name text, description text, PRIMARY KEY (tenant_id, id) );
3. 创建索引(性能关键)
有两种索引类型可选:
-- GiST 索引:平衡型,更新快,占用空间小 CREATE INDEX trgm_idx_products_name ON products USING gist (name gist_trgm_ops); -- GIN 索引:查询更快,但更新慢,占用更多空间 CREATE INDEX trgm_gin_idx_products_name ON products USING gin (name gin_trgm_ops);
选择建议:
- 读多写少 → 用 GIN
- 频繁更新 → 用 GiST
二、插入测试数据
-- 插入产品(注意第二条有拼写错误 "iPhne")
INSERT INTO products (tenant_id, id, name, description) VALUES
('d1c06023-3421-4fbb-9dd1-c96e42d2fd02', 1, 'iPhone 13 Pro', 'Latest Apple smartphone'),
('d1c06023-3421-4fbb-9dd1-c96e42d2fd02', 2, 'iPhne 13', 'Budget Apple smartphone'),
('d1c06023-3421-4fbb-9dd1-c96e42d2fd02', 3, 'Samsung Galaxy S21', 'Android flagship phone');
三、核心查询方法
1. 计算相似度
-- 返回 0 到 1 之间的数字(1 = 完全相同)
SELECT similarity('iPhone', 'iPhne');
-- 结果: 0.5454545
2. 整体相似度匹配(% 操作符)
-- 查找与 'iPhone' 相似的产品名 SELECT name, similarity(name, 'iPhone') AS sim FROM products WHERE name % 'iPhone' -- 相似度超过阈值 ORDER BY sim DESC;
输出结果:
name | sim
---------------+------------
iPhone 13 Pro | 0.5
iPhne 13 | 0.33333334
(2 rows)
3. 子串相似度匹配(%> 操作符)
适合搜索包含某个词的文本:
-- 查找包含 'phone' 的产品 SELECT name, similarity(name, 'phone') AS sim FROM products WHERE name %> 'phone' ORDER BY sim DESC;
输出:
name | sim
---------------+------------
iPhone 13 Pro | 0.33333334
(1 row)
四、高级功能
1. 调整相似度阈值
默认阈值是 0.3,可以调整
SET pg_trgm.similarity_threshold = 0.5;
再次查询,只返回相似度 ≥ 0.5 的结果
SELECT name FROM products WHERE name % 'iPhone';
2. 单词相似度(Word Similarity)
更适合匹配完整单词:
- word_similarity: 从左到右查找最相似的词
SELECT name, word_similarity('iPhone', name) as sim
FROM products
ORDER BY sim DESC;- strict_word_similarity: 更严格的单词边界匹配
SELECT name, strict_word_similarity('iPhone', name) as sim
FROM products
ORDER BY sim DESC;
五、实战场景
场景 1:容错搜索(处理拼写错误)
方法 1:降低相似度阈值
- 先查看相似度
SELECT similarity('iPhone 13 Pro', 'ipone'); -- 结果约 0.25
- 临时降低阈值(针对单次查询)
BEGIN; SET LOCAL pg_trgm.similarity_threshold = 0.2; SELECT name, similarity(name, 'ipone') AS score FROM products WHERE tenant_id = 'd1c06023-3421-4fbb-9dd1-c96e42d2fd02' AND name % 'ipone' ORDER BY score DESC LIMIT 5; COMMIT; -- 或 ROLLBACK,阈值会自动恢复
输出:
name | score
---------------+-------
iPhone 13 Pro | 0.25
iPhne 13 | 0.25
(2 rows)
方法 2:不用 % 操作符(推荐)
直接用 similarity() 函数,手动过滤:
SELECT name, similarity(name, 'ipone') AS score FROM products WHERE tenant_id = 'd1c06023-3421-4fbb-9dd1-c96e42d2fd02' AND similarity(name, 'ipone') > 0.15 -- 自定义阈值 ORDER BY score DESC LIMIT 5;
注意:方法 2 性能较差(无法用索引),适合小数据集。对于大表,使用方法 1 配合索引。
场景 2:自动补全(更好的解决方案)
用户输入 “iPh”,显示候选项。使用 word_similarity 更适合前缀匹配:
SELECT name, word_similarity('iPh', name) AS score
FROM products
WHERE tenant_id = 'd1c06023-3421-4fbb-9dd1-c96e42d2fd02'
AND 'iPh' <% name -- word_similarity 操作符
ORDER BY score DESC
LIMIT 10;
或使用 LIKE(性能更好):
SELECT name FROM products WHERE tenant_id = 'd1c06023-3421-4fbb-9dd1-c96e42d2fd02' AND name ILIKE 'iPh%' -- 大小写不敏感 ORDER BY name LIMIT 10;
场景 3:去重(找到相似的重复数据)
SELECT p1.name, p2.name, similarity(p1.name, p2.name) AS sim FROM products p1 JOIN products p2 ON p1.id < p2.id WHERE p1.tenant_id = p2.tenant_id AND p1.name % p2.name AND similarity(p1.name, p2.name) > 0.7 ORDER BY sim DESC;
六、函数与操作符
主要函数
- similarity (text, text):返回两个字符串的相似度(0 至 1 之间)
- show_trgm (text):显示字符串中的三元组
- word_similarity (text, text):返回基于单词的相似度
- strict_word_similarity (text, text):返回严格基于单词的相似度
- show_limit ():显示当前的相似度阈值
七、操作符速查表
| 操作符 | 示例 | 说明 |
|---|---|---|
| % | 相似度匹配 | name % ‘iPhone’ |
| %> | 子串相似度(左边在右边中) | ‘phone’ %> name |
| < % | 子串相似度(右边在左边中) | name <% ‘phone’ |
| similarity() | 计算相似度分数 | similarity(name, ‘iPhone’) |
| word_similarity() | 单词相似度 | word_similarity(‘iPhone’, name) |
大小写敏感性:默认区分大小写,可以用 LOWER() 转换
WHERE LOWER(name) % LOWER('iphone')
八、索引类型
pg_trgm 支持两种索引类型:
GiST:
CREATE INDEX trgm_gist_idx ON table_name USING gist (column_name gist_trgm_ops);
- 搜索与更新的性能平衡
- 更小的索引体积
- 适用于动态数据
- GIN索引:
CREATE INDEX trgm_gin_idx ON table_name USING gin (column_name gin_trgm_ops);
- 搜索速度更快
- 更新速度较慢
- 索引体积更大
- 更适用于静态数据
九、最佳实践
1.索引选择
- 以读取操作为主的数据,使用 GIN 索引
- 频繁更新的数据,使用 GiST 索引
- 仅为频繁搜索的列创建索引
2.阈值调整
- 较低阈值(如 0.2)可获得更多匹配结果
- 较高阈值(如 0.5)可实现更严格的匹配
- 结合自身数据测试,找到最优值
3.性能优化
- 全词匹配场景使用 word_similarity () 函数
- 仅对特定列创建索引,而非所有文本列
- 监控索引体积,必要时重建索引
十、性能注意事项
- GIN 索引搜索速度更快,但更新速度较慢
- 包含大量唯一值的文本列,索引体积可能较大
- 大型表可考虑使用部分索引
- 根据误报 / 漏报率,监控并调整相似度阈值
十一、局限性
- 不适用于极短字符串(少于 3 个字符)
- 可能产生误报结果
- 大型文本列的索引体积可能较大
- 不适合精确匹配(建议使用标准索引)
十二、总结
pg_trgm 是实现模糊搜索的强大工具,适用于:
- 模糊搜索功能
- 拼写检查建议
- 自动补全功能
- 查找相似产品名称
- 匹配含拼写错误的地址
- 容忍拼写错误的搜索
关键是创建合适的索引和调整相似度阈值,就能在保证性能的前提下提供出色的用户体验。
到此这篇关于PostgreSQL pg_trgm 模糊搜索完全指南的文章就介绍到这了,更多相关PostgreSQL pg_trgm 模糊搜索内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
