Cloud SQL for PostgreSQL 中的 pg_textsearch 扩展程序使用行业标准的 BM25(最佳匹配 25)得分算法提供全文搜索功能,可实现高度准确的相关性得分。该扩展程序是一个开源项目,可在 GitHub 上找到。
pg_textsearch 扩展程序需要 PostgreSQL 17 或更高版本。
由于 pg_textsearch 支持逆文档频率、词频饱和度和文档长度归一化,因此与 PostgreSQL 的内置 ts_rank 函数相比,它可以生成更相关的结果。此外,由于它直接在标准 PostgreSQL 存储页面上运行,因此您无需安装 Elasticsearch 等外部引擎,也不必担心双集群维护和数据同步问题。借助 pg_textsearch,您可以在不离开 PostgreSQL 生态系统的情况下,打造强大、可伸缩且高度相关的搜索体验。
多种搜索方式
Cloud SQL for PostgreSQL 中有多种文本搜索选项:
完全匹配:使用标准 SQL 搜索时,您可以使用
LIKE和ILIKE语句来扫描完全匹配的字符序列。例如,查询ILIKE '%smart fitness watches%'会找到以下实例:- “探索我们正在促销的全新智能健身手表。”
- “智能健身手表是绝佳的礼物。”
不过,它无法找到:
- 跑者想要一款智能防水健身手表。
- “这款手表在健身方面尤其智能。”
全文搜索:使用 BM25 算法和
tsvector,全文搜索会将文本拆分为词根,忽略过滤字词,并对相关性进行评分。它了解语言规则(包括复数和语法),并会考虑字词频率。对smart AND fitness AND watches进行全文搜索会找到示例完全匹配查询会错过的实例,以及以下更复杂的实例:- “智能手表非常适合日常健身。”
- “在健身方面,并非所有手表都如此智能。”
不过,它无法找到:
- “智能健康感知手表可以帮助你制定锻炼计划。”
- “这款智能运动追踪手环非常划算。”
语义搜索:语义搜索使用 AI 模型将文本转换为向量并衡量相似度距离。它能理解含义、意图、上下文、同义词和相关概念。如果使用语义搜索来搜索
smart fitness watches,则会找到其他搜索方法会遗漏的示例实例。语义搜索会理解“智能健康感知手表”与“智能健身手表”是相同的,并且“智能运动追踪手环”也可能相关。如果未充分优先考虑“手表”,则可能会错误地匹配计步器手环。
安装 pg_textsearch 扩展程序
请按以下步骤安装并启用 pg_textsearch:
按照配置数据库标志中的说明,将
cloudsql.enable_pg_textsearch标志设置为on。这会将pg_textsearch添加到shared_preload_libraries。安装
pg_textsearch扩展程序CREATE EXTENSION pg_textsearch;验证安装:
SELECT extversion FROM pg_extension WHERE extname = 'pg_textsearch'
使用 pg_textsearch 进行搜索
假设以下示例表已填充数据:
CREATE TABLE documents (
doc_id TEXT PRIMARY KEY,
content TEXT,
text_embedding vector(3072)
GENERATED ALWAYS AS (embedding('gemini-embedding-001', content)) STORED
);
在使用全文搜索之前,请先创建 BM25 索引:
CREATE INDEX idx_docs_bm25
ON documents
USING bm25 (content)
WITH (text_config = 'english');
然后,您可以发出如下所示的全文搜索查询:
SELECT doc_id, content, content <@> 'database system'
AS score FROM documents
ORDER BY content <@> 'database system'
ASC LIMIT 5;
使用 pg_textsearch 和 vector 执行混合搜索
您可以将 pg_textsearch 与 vector 语义搜索扩展程序搭配使用,以执行混合搜索。按如下方式安装 vector 语义搜索扩展程序:
CREATE EXTENSION IF NOT EXISTS vector CASCADE;
发出如下混合语义搜索和全文搜索查询:
WITH
-- Semantic search results
vector_search AS (
SELECT doc_id,
RANK () OVER (ORDER BY text_embedding <=>
google_ml.embedding('gemini-embedding-001',
'database')::VECTOR ) AS rank
FROM documents
ORDER BY text_embedding <=> google_ml.embedding('gemini-embedding-001',
'database')::VECTOR
LIMIT 10
),
-- Full text search results
text_search AS (
SELECT doc_id,
RANK () OVER (ORDER BY content <@> 'database' ASC) AS rank
FROM documents
ORDER BY content <@> 'database' ASC
LIMIT 10
)
-- RRF combining both semantic and full text search results
SELECT
COALESCE(vector_search.doc_id, text_search.doc_id) AS id,
COALESCE(1.0 / (60 + vector_search.rank), 0.0) +
COALESCE(1.0 / (60 + text_search.rank), 0.0) AS rrf_score
FROM vector_search
FULL OUTER JOIN text_search ON vector_search.doc_id = text_search.doc_id
ORDER BY rrf_score DESC
LIMIT 5;
用于配置 pg_textsearch 扩展程序的标志
使用以下标志配置 pg_textsearch 全文搜索:
pg_textsearch.bulk_load_threshold:自动溢出之前每个事务的字词数。设置为 0 可停用。 默认值:100,000。pg_textsearch.compress_segments:压缩新段中的发布块。默认值:on。pg_textsearch.default_limit:在没有LIMIT子句的情况下,评分的文档数量上限。 默认值:1,000。pg_textsearch.memtable_pages_threshold:在自动溢出之前要链接的页面数。设置为 0 可停用。默认值:64。pg_textsearch.segments_per_level:在发生自动压缩之前,每个级别的段数。范围:2-64。默认值:8。