纲要
- 信息检索的演进阶段
- 关键词匹配
- 全文检索
- 语义检索(向量检索)
- 全文检索(Full-Text Search)核心机制
tsvector数据类型tsquery数据类型to_tsvector与to_tsquery函数- GIN 索引
ts_rank排序函数
- 向量检索(Vector Search)核心机制
- Embedding 模型
- 相似度计算
- pgvector 插件
- HNSW / IVFFlat 索引
- 全文检索与向量检索的对比
- 混合检索(Hybrid Search)实践
- Pre-filter 与 Post-filter 策略
- 统一 SQL 查询实现
- API 速览
- Demo 完整示例
- 官方文档与参考链接
信息检索的演进:从关键词到语义
信息检索技术的发展大致经历了三个阶段。
第一阶段:关键词匹配(Keyword Matching) 。最初的搜索方式本质上是字符串匹配------用户输入什么,系统就在文档中查找完全相同的字符串。这种方式简单直接,但无法处理同义词、拼写变体等问题。
第二阶段:全文检索(Full-Text Search) 。随着倒排索引(Inverted Index)、分词(Tokenization)和词干提取(Stemming)等技术的成熟,系统能够在海量文本中高效检索关键词。Elasticsearch 是这一领域的代表性产品,PostgreSQL 则通过 tsvector / tsquery 类型与 GIN 索引提供了原生的全文检索能力reference:0。
第三阶段:语义检索(Semantic Search / Vector Search) 。进入 AI 与大模型时代,用户的输入越来越自然------可能是一句话、一段文字,甚至是一张图片或一段音频。系统需要理解语义而非仅仅匹配字符。向量检索通过 Embedding 模型将文本转换为向量,再通过相似度计算完成检索。
全文检索"快而准,但死板"------它只关心字符是否匹配。向量检索"懂语义,但模糊"------它能理解"轿车"与"汽车"的语义相似性,但召回率可能不稳定,索引维护成本也更高reference:1。
全文检索核心机制
PostgreSQL 自 8.3 版本开始支持全文检索功能(8.3 引入核心支持,后续版本持续增强)。其核心是倒排索引------将每个词(Lexeme)映射到包含它的文档列表,如同书籍的附录索引。
核心数据类型与函数
tsvector :全文检索的文档表示类型。它将文本分词、去除停用词(Stop Words)、词干归一化后,存储为词素(Lexeme)及其位置的排序列表reference:2。例如:
sql
SELECT to_tsvector('english', 'a fat cat sat on a mat - it ate a fat rats');
返回结果中不包含 a、on、it 等停用词,rats 被归一化为 rat,标点符号被忽略reference:3。
tsquery :全文检索的查询表示类型。它存储待搜索的词素,并支持布尔运算符 &(AND)、|(OR)、!(NOT)以及短语搜索运算符 <->reference:4reference:5。
to_tsvector :将文本转换为 tsvector 类型reference:6。
to_tsquery :将查询字符串转换为 tsquery 类型reference:7。
@@ 运算符 :判断 tsvector 是否匹配 tsqueryreference:8。
基础示例
sql
-- 创建测试表
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
title TEXT,
body TEXT,
tsv TSVECTOR
);
-- 插入数据并生成 tsvector(使用 English 配置)
INSERT INTO documents (title, body, tsv)
VALUES (
'PostgreSQL Full-Text Search',
'PostgreSQL provides powerful full-text search capabilities.',
to_tsvector('english', 'PostgreSQL provides powerful full-text search capabilities.')
);
-- 全文检索查询:查找包含 "postgresql" 和 "search" 的文档
SELECT title, body
FROM documents
WHERE tsv @@ to_tsquery('english', 'postgresql & search');
GIN 索引加速
对于生产环境,必须创建 GIN(Generalized Inverted Index)索引以加速全文检索reference:9:
sql
CREATE INDEX idx_documents_tsv ON documents USING GIN (tsv);
GIN 索引是全文检索的首选索引类型,它存储 tsvector 中每个词素的索引条目reference:10。需要注意的是,表达式中必须显式指定文本搜索配置(如 'english'),否则索引无法被使用reference:11。
相关性排序
ts_rank 函数用于计算文档与查询的相关性分数,分数越高表示匹配程度越高reference:12:
sql
SELECT
title,
ts_rank(tsv, to_tsquery('english', 'postgresql & search')) AS rank
FROM documents
WHERE tsv @@ to_tsquery('english', 'postgresql & search')
ORDER BY rank DESC;
向量检索核心机制
向量检索(Vector Search)通过 Embedding 模型将文本、图像、音频等数据转换为高维向量,然后通过计算向量间的相似度(如余弦相似度、欧几里得距离)来检索语义相近的内容。
PostgreSQL 通过 pgvector 扩展支持向量检索。pgvector 提供:
vector数据类型- 余弦相似度运算符
<=> - 欧几里得距离运算符
<-> - 内积运算符
<#> - HNSW 和 IVFFlat 索引
启用 pgvector
sql
-- 创建扩展
CREATE EXTENSION vector;
-- 创建包含向量列的表
CREATE TABLE embeddings (
id SERIAL PRIMARY KEY,
content TEXT,
embedding VECTOR(768) -- 维度取决于所使用的 Embedding 模型
);
-- 创建 HNSW 索引(适用于高维向量)
CREATE INDEX idx_embeddings_hnsw ON embeddings
USING hnsw (embedding vector_cosine_ops);
-- 或使用 IVFFlat 索引(适用于数据量较大的场景)
CREATE INDEX idx_embeddings_ivfflat ON embeddings
USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
向量相似度检索
sql
-- 余弦相似度检索(值越接近 1 越相似)
SELECT content, 1 - (embedding <=> '[0.1, 0.2, ...]') AS similarity
FROM embeddings
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 10;
全文检索与向量检索对比
| 维度 | 全文检索 | 向量检索 |
|---|---|---|
| 核心原理 | 倒排索引 + 分词 | Embedding + 相似度计算 |
| 匹配方式 | 精确关键词匹配 | 语义相似度匹配 |
| 优势 | 速度快、精确、成熟稳定 | 理解语义、容错性强、跨模态 |
| 局限 | 不理解语义、灵活性差 | 召回率不稳定、索引维护成本高 |
| 适用场景 | 日志检索、明确关键词的结构化文本 | 自然语言提问、模糊语义搜索 |
全文检索与向量检索并非互斥关系,而是互补关系。全文检索在精确定位场景下表现出色,向量检索在自然语言处理与模糊语义场景中更加灵活。
混合检索(Hybrid Search)实践
在实际生产环境中,将全文检索与向量检索结合使用,可以实现"高效、精准、智能"的搜索体验。
数据表设计
sql
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE hybrid_docs (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT,
tsv TSVECTOR,
embedding VECTOR(768)
);
-- 创建 GIN 索引(全文检索)
CREATE INDEX idx_hybrid_docs_tsv ON hybrid_docs USING GIN (tsv);
-- 创建 HNSW 索引(向量检索)
CREATE INDEX idx_hybrid_docs_embedding ON hybrid_docs
USING hnsw (embedding vector_cosine_ops);
策略一:先向量召回,再全文过滤(Post-filter)
先用向量检索召回语义相关的 Top-N 文档,再通过全文检索进行精确过滤:
sql
WITH vector_results AS (
SELECT id, title, content, 1 - (embedding <=> '[0.1, 0.2, ...]') AS similarity
FROM hybrid_docs
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 100
)
SELECT *
FROM vector_results
WHERE tsv @@ to_tsquery('english', 'postgresql & search')
ORDER BY similarity DESC;
策略二:先全文过滤,再向量排序(Pre-filter)
先用全文检索过滤候选文档,再通过向量相似度进行排序:
sql
WITH fulltext_results AS (
SELECT id, title, content, tsv
FROM hybrid_docs
WHERE tsv @@ to_tsquery('english', 'postgresql & search')
)
SELECT
id,
title,
content,
1 - (embedding <=> '[0.1, 0.2, ...]') AS similarity
FROM fulltext_results
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 20;
策略三:加权混合排序
结合全文检索的 ts_rank 分数与向量相似度分数,进行加权综合排序:
sql
SELECT
id,
title,
content,
ts_rank(tsv, to_tsquery('english', 'postgresql & search')) AS text_score,
1 - (embedding <=> '[0.1, 0.2, ...]') AS vector_score,
(0.5 * ts_rank(tsv, to_tsquery('english', 'postgresql & search')) +
0.5 * (1 - (embedding <=> '[0.1, 0.2, ...]'))) AS hybrid_score
FROM hybrid_docs
WHERE tsv @@ to_tsquery('english', 'postgresql & search')
ORDER BY hybrid_score DESC
LIMIT 20;
API 速览
全文检索核心 API
| 函数 / 运算符 | 签名 | 说明 |
|---|---|---|
to_tsvector |
to_tsvector([config regconfig, ] document text) → tsvector |
将文档文本转换为 tsvectorreference:13 |
to_tsquery |
to_tsquery([config regconfig, ] query text) → tsquery |
将查询字符串转换为 tsqueryreference:14 |
plainto_tsquery |
plainto_tsquery([config regconfig, ] query text) → tsquery |
将纯文本转换为 tsquery,自动添加 & 运算符reference:15 |
phraseto_tsquery |
phraseto_tsquery([config regconfig, ] query text) → tsquery |
将纯文本转换为短语查询 tsqueryreference:16 |
ts_rank |
ts_rank([weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) → float4 |
计算文档与查询的相关性分数 |
setweight |
setweight(vector tsvector, weight "char") → tsvector |
为 tsvector 中的词素标记权重(A/B/C/D)reference:17 |
@@ |
tsvector @@ tsquery → boolean |
判断 tsvector 是否匹配 tsqueryreference:18 |
| ` | ` |
pgvector 核心 API
| 函数 / 运算符 | 签名 | 说明 |
|---|---|---|
vector |
vector(n) |
创建 n 维向量类型 |
<=> |
vector <=> vector → float |
余弦距离(值越小越相似) |
<-> |
vector <-> vector → float |
欧几里得距离 |
<#> |
vector <#> vector → float |
负内积 |
Demo 完整示例
以下是一个完整的 Node.js + PostgreSQL 混合检索示例,使用 pg 驱动连接数据库。
运行说明
- 确保 PostgreSQL 已安装 pgvector 扩展
- 执行下方 SQL 初始化脚本
- 安装依赖:
npm install pg - 运行脚本:
node hybrid-search-demo.js
初始化 SQL
sql
-- 启用 pgvector 扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 创建混合检索表
DROP TABLE IF EXISTS hybrid_docs;
CREATE TABLE hybrid_docs (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
tsv TSVECTOR,
embedding VECTOR(3) -- 使用 3 维向量便于演示
);
-- 插入示例数据
INSERT INTO hybrid_docs (title, content, tsv, embedding) VALUES
(
'PostgreSQL Full-Text Search Guide',
'PostgreSQL provides built-in full-text search using tsvector and tsquery.',
to_tsvector('english', 'PostgreSQL provides built-in full-text search using tsvector and tsquery.'),
'[0.9, 0.1, 0.2]'::VECTOR(3)
),
(
'Vector Search with pgvector',
'pgvector is a PostgreSQL extension that enables vector similarity search.',
to_tsvector('english', 'pgvector is a PostgreSQL extension that enables vector similarity search.'),
'[0.1, 0.8, 0.3]'::VECTOR(3)
),
(
'Hybrid Search Combining Full-Text and Vectors',
'Combining full-text search and vector search delivers better search results.',
to_tsvector('english', 'Combining full-text search and vector search delivers better search results.'),
'[0.6, 0.6, 0.4]'::VECTOR(3)
),
(
'Database Indexing Strategies',
'GIN indexes accelerate full-text search, while HNSW indexes accelerate vector search.',
to_tsvector('english', 'GIN indexes accelerate full-text search, while HNSW indexes accelerate vector search.'),
'[0.2, 0.3, 0.9]'::VECTOR(3)
),
(
'Introduction to Machine Learning',
'Machine learning models can generate embeddings for text, images, and audio.',
to_tsvector('english', 'Machine learning models can generate embeddings for text, images, and audio.'),
'[0.3, 0.2, 0.7]'::VECTOR(3)
);
-- 创建 GIN 索引(全文检索)
CREATE INDEX idx_hybrid_docs_tsv ON hybrid_docs USING GIN (tsv);
-- 创建 HNSW 索引(向量检索)
CREATE INDEX idx_hybrid_docs_embedding ON hybrid_docs
USING hnsw (embedding vector_cosine_ops);
Node.js 代码
js
const { Pool } = require('pg');
const pool = new Pool({
host: 'localhost',
port: 5432,
database: 'postgres',
user: 'postgres',
password: 'your_password',
});
// 查询向量(模拟用户输入 "PostgreSQL search" 的 Embedding)
const QUERY_VECTOR = [0.8, 0.2, 0.3];
async function hybridSearch(queryText, queryVector, limit = 10) {
const client = await pool.connect();
try {
// 混合检索:先全文过滤,再按向量相似度排序
const result = await client.query(
`
WITH filtered AS (
SELECT
id,
title,
content,
tsv,
embedding,
ts_rank(tsv, to_tsquery('english', $1)) AS text_score
FROM hybrid_docs
WHERE tsv @@ to_tsquery('english', $1)
)
SELECT
id,
title,
content,
text_score,
1 - (embedding <=> $2::VECTOR(3)) AS vector_score,
(0.5 * ts_rank(tsv, to_tsquery('english', $1)) +
0.5 * (1 - (embedding <=> $2::VECTOR(3)))) AS hybrid_score
FROM filtered
ORDER BY hybrid_score DESC
LIMIT $3
`,
[queryText, `[${QUERY_VECTOR.join(',')}]`, limit]
);
return result.rows;
} finally {
client.release();
}
}
async function main() {
console.log('=== PostgreSQL 混合检索 Demo ===\n');
const results = await hybridSearch('postgresql & search', QUERY_VECTOR);
console.log('查询条件: "postgresql & search"');
console.log('查询向量:', QUERY_VECTOR);
console.log('\n检索结果:');
console.log('-' .repeat(80));
results.forEach((row, idx) => {
console.log(`\n[${idx + 1}] ${row.title}`);
console.log(` Text Score: ${row.text_score.toFixed(4)}`);
console.log(` Vector Score: ${row.vector_score.toFixed(4)}`);
console.log(` Hybrid Score: ${row.hybrid_score.toFixed(4)}`);
console.log(` Content: ${row.content}`);
});
await pool.end();
}
main().catch(console.error);
技术点总结
- 全文检索 :使用
tsvector、tsquery、to_tsvector、to_tsquery和@@运算符 - 向量检索 :使用 pgvector 的
vector类型和余弦距离运算符<=> - 混合检索:在同一 SQL 查询中结合全文过滤与向量排序
- 加权排序 :使用
ts_rank和向量相似度计算综合分数 - 索引加速:GIN 索引加速全文检索,HNSW 索引加速向量检索
多语言示例
本部分提供 Go、Python 和 Java 三种语言的完整可运行示例,功能与前述 Node.js 版本等价:连接 PostgreSQL 数据库,执行混合检索(全文过滤 + 向量相似度排序),输出综合排序结果。
Go 示例
运行说明
-
安装依赖:
bashgo mod init hybrid-search-demo go get github.com/jackc/pgx/v5 go get github.com/pgvector/pgvector-go go get github.com/pgvector/pgvector-go/pgx -
数据库准备:确保 PostgreSQL 已安装 pgvector 扩展,并执行 Node.js 示例中的初始化 SQL 脚本。
-
运行:
bashgo run main.go
代码说明
Go 示例使用 pgx 作为 PostgreSQL 驱动,pgvector-go 提供 vector 类型的支持reference:0。核心流程如下:
- 通过
pgx.Connect建立数据库连接 - 使用
pgxvec.RegisterTypes注册 vector 类型 - 执行混合检索 SQL,使用
pgvector.NewVector构造查询向量 - 使用余弦距离运算符
<=>计算相似度
技术点总结
- 使用
pgx驱动 +pgvector-go扩展 - 通过
pgxvec.RegisterTypes注册自定义类型 - 使用
pgvector.NewVector将 Go 切片转换为数据库 vector 类型 - 支持
<=>余弦距离、<->欧几里得距离、<#>负内积运算符
完整代码
go
package main
import (
"context"
"fmt"
"log"
"github.com/jackc/pgx/v5"
"github.com/pgvector/pgvector-go"
pgxvec "github.com/pgvector/pgvector-go/pgx"
)
type HybridDoc struct {
ID int
Title string
Content string
TextScore float64
VectorScore float64
HybridScore float64
}
func main() {
ctx := context.Background()
// 连接数据库
conn, err := pgx.Connect(ctx, "postgres://postgres:your_password@localhost:5432/postgres")
if err != nil {
log.Fatal("无法连接数据库:", err)
}
defer conn.Close(ctx)
// 注册 pgvector 类型
if err := pgxvec.RegisterTypes(ctx, conn); err != nil {
log.Fatal("注册 vector 类型失败:", err)
}
// 启用扩展(如未启用)
_, err = conn.Exec(ctx, "CREATE EXTENSION IF NOT EXISTS vector")
if err != nil {
log.Fatal("启用 vector 扩展失败:", err)
}
// 查询向量(模拟用户输入 "PostgreSQL search" 的 Embedding)
queryVector := pgvector.NewVector([]float32{0.8, 0.2, 0.3})
queryText := "postgresql & search"
limit := 10
// 混合检索 SQL
sql := `
WITH filtered AS (
SELECT
id,
title,
content,
tsv,
embedding,
ts_rank(tsv, to_tsquery('english', $1)) AS text_score
FROM hybrid_docs
WHERE tsv @@ to_tsquery('english', $1)
)
SELECT
id,
title,
content,
text_score,
1 - (embedding <=> $2) AS vector_score,
(0.5 * ts_rank(tsv, to_tsquery('english', $1)) +
0.5 * (1 - (embedding <=> $2))) AS hybrid_score
FROM filtered
ORDER BY hybrid_score DESC
LIMIT $3
`
rows, err := conn.Query(ctx, sql, queryText, queryVector, limit)
if err != nil {
log.Fatal("查询失败:", err)
}
defer rows.Close()
fmt.Println("=== Go 混合检索 Demo ===")
fmt.Printf("查询条件: %q\n", queryText)
fmt.Printf("查询向量: [0.8, 0.2, 0.3]\n\n")
fmt.Println("检索结果:")
fmt.Println("--------------------------------------------------------------------------------")
var results []HybridDoc
for rows.Next() {
var doc HybridDoc
err := rows.Scan(
&doc.ID,
&doc.Title,
&doc.Content,
&doc.TextScore,
&doc.VectorScore,
&doc.HybridScore,
)
if err != nil {
log.Fatal("扫描行失败:", err)
}
results = append(results, doc)
}
for i, doc := range results {
fmt.Printf("\n[%d] %s\n", i+1, doc.Title)
fmt.Printf(" Text Score: %.4f\n", doc.TextScore)
fmt.Printf(" Vector Score: %.4f\n", doc.VectorScore)
fmt.Printf(" Hybrid Score: %.4f\n", doc.HybridScore)
fmt.Printf(" Content: %s\n", doc.Content)
}
}
Python 示例
运行说明
-
安装依赖:
bashpip install psycopg2-binary pgvector -
数据库准备:确保 PostgreSQL 已安装 pgvector 扩展,并执行 Node.js 示例中的初始化 SQL 脚本。
-
运行:
bashpython hybrid_search_demo.py
代码说明
Python 示例使用 psycopg2 作为 PostgreSQL 驱动,pgvector.psycopg2 提供 vector 类型的支持reference:5。核心流程如下:
- 使用
psycopg2.connect建立数据库连接 - 调用
register_vector注册 vector 类型reference:6reference:7 - 执行混合检索 SQL,使用列表作为向量参数
- 使用余弦距离运算符
<=>计算相似度
技术点总结
- 使用
psycopg2-binary+pgvectorPython 包 - 通过
register_vector注册自定义类型 - 向量参数以 Python
list形式传递,自动转换为 PostgreSQLvector - 支持
<=>余弦距离、<->欧几里得距离、<#>负内积运算符
完整代码
python
import psycopg2
from pgvector.psycopg2 import register_vector
def main():
# 连接数据库
conn = psycopg2.connect(
host="localhost",
port=5432,
database="postgres",
user="postgres",
password="your_password"
)
cur = conn.cursor()
# 注册 pgvector 类型
register_vector(conn)
# 启用扩展(如未启用)
cur.execute("CREATE EXTENSION IF NOT EXISTS vector")
conn.commit()
# 查询向量(模拟用户输入 "PostgreSQL search" 的 Embedding)
query_vector = [0.8, 0.2, 0.3]
query_text = "postgresql & search"
limit = 10
# 混合检索 SQL
sql = """
WITH filtered AS (
SELECT
id,
title,
content,
tsv,
embedding,
ts_rank(tsv, to_tsquery('english', %s)) AS text_score
FROM hybrid_docs
WHERE tsv @@ to_tsquery('english', %s)
)
SELECT
id,
title,
content,
text_score,
1 - (embedding <=> %s) AS vector_score,
(0.5 * ts_rank(tsv, to_tsquery('english', %s)) +
0.5 * (1 - (embedding <=> %s))) AS hybrid_score
FROM filtered
ORDER BY hybrid_score DESC
LIMIT %s
"""
cur.execute(sql, (query_text, query_text, query_vector, query_text, query_vector, limit))
results = cur.fetchall()
print("=== Python 混合检索 Demo ===")
print(f'查询条件: "{query_text}"')
print(f"查询向量: {query_vector}\n")
print("检索结果:")
print("-" * 80)
for i, row in enumerate(results):
doc_id, title, content, text_score, vector_score, hybrid_score = row
print(f"\n[{i+1}] {title}")
print(f" Text Score: {text_score:.4f}")
print(f" Vector Score: {vector_score:.4f}")
print(f" Hybrid Score: {hybrid_score:.4f}")
print(f" Content: {content}")
cur.close()
conn.close()
if __name__ == "__main__":
main()
Java 示例
运行说明
-
Maven 依赖 (
pom.xml):xml<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.7.3</version> </dependency> <dependency> <groupId>com.pgvector</groupId> <artifactId>pgvector</artifactId> <version>0.1.6</version> </dependency> -
数据库准备:确保 PostgreSQL 已安装 pgvector 扩展,并执行 Node.js 示例中的初始化 SQL 脚本。
-
编译与运行:
bashmvn compile mvn exec:java -Dexec.mainClass="com.example.HybridSearchDemo"
代码说明
Java 示例使用 JDBC 作为 PostgreSQL 驱动,pgvector 库提供 PGvector 类型支持reference:9。核心流程如下:
- 使用
DriverManager.getConnection建立数据库连接 - 调用
PGvector.registerTypes注册 vector 类型 - 执行混合检索 SQL,使用
PGvector对象构造查询向量 - 使用余弦距离运算符
<=>计算相似度reference:12
技术点总结
- 使用 JDBC +
pgvectorJava 库 - 通过
PGvector.registerTypes注册自定义类型 - 使用
setObject传递PGvector参数 - 支持
<=>余弦距离、<->欧几里得距离、<#>负内积运算符
完整代码
java
package com.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.pgvector.PGvector;
public class HybridSearchDemo {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/postgres";
String user = "postgres";
String password = "your_password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 注册 pgvector 类型
PGvector.registerTypes(conn);
// 启用扩展(如未启用)
try (PreparedStatement stmt = conn.prepareStatement(
"CREATE EXTENSION IF NOT EXISTS vector")) {
stmt.executeUpdate();
}
// 查询向量(模拟用户输入 "PostgreSQL search" 的 Embedding)
float[] vectorData = {0.8f, 0.2f, 0.3f};
PGvector queryVector = new PGvector(vectorData);
String queryText = "postgresql & search";
int limit = 10;
// 混合检索 SQL
String sql = """
WITH filtered AS (
SELECT
id,
title,
content,
tsv,
embedding,
ts_rank(tsv, to_tsquery('english', ?)) AS text_score
FROM hybrid_docs
WHERE tsv @@ to_tsquery('english', ?)
)
SELECT
id,
title,
content,
text_score,
1 - (embedding <=> ?) AS vector_score,
(0.5 * ts_rank(tsv, to_tsquery('english', ?)) +
0.5 * (1 - (embedding <=> ?))) AS hybrid_score
FROM filtered
ORDER BY hybrid_score DESC
LIMIT ?
""";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, queryText);
pstmt.setString(2, queryText);
pstmt.setObject(3, queryVector);
pstmt.setString(4, queryText);
pstmt.setObject(5, queryVector);
pstmt.setInt(6, limit);
ResultSet rs = pstmt.executeQuery();
System.out.println("=== Java 混合检索 Demo ===");
System.out.printf("查询条件: \"%s\"\n", queryText);
System.out.printf("查询向量: [0.8, 0.2, 0.3]\n\n");
System.out.println("检索结果:");
System.out.println("--------------------------------------------------------------------------------");
int index = 0;
while (rs.next()) {
index++;
int id = rs.getInt("id");
String title = rs.getString("title");
String content = rs.getString("content");
double textScore = rs.getDouble("text_score");
double vectorScore = rs.getDouble("vector_score");
double hybridScore = rs.getDouble("hybrid_score");
System.out.printf("\n[%d] %s\n", index, title);
System.out.printf(" Text Score: %.4f\n", textScore);
System.out.printf(" Vector Score: %.4f\n", vectorScore);
System.out.printf(" Hybrid Score: %.4f\n", hybridScore);
System.out.printf(" Content: %s\n", content);
}
}
} catch (SQLException e) {
System.err.println("数据库错误: " + e.getMessage());
e.printStackTrace();
}
}
}
多语言对比
| 维度 | Node.js | Go | Python | Java |
|---|---|---|---|---|
| 驱动/库 | pg |
pgx + pgvector-go |
psycopg2 + pgvector |
JDBC + pgvector |
| 类型注册 | 自动(pg 驱动原生支持) |
pgxvec.RegisterTypes |
register_vector |
PGvector.registerTypes |
| 向量构造 | [0.8, 0.2, 0.3](数组字面量) |
pgvector.NewVector([]float32{...}) |
[0.8, 0.2, 0.3](列表) |
new PGvector(new float[]{...}) |
| 参数传递 | $1, $2 占位符 |
$1, $2 占位符 |
%s 占位符(psycopg2) |
? 占位符(JDBC) |
| 异步支持 | 原生 async/await |
原生 context + goroutine |
需 asyncpg 或 asyncio |
需 CompletableFuture / R2DBC |
| ORM 可选 | TypeORM / Prisma | GORM / Bun / Ent | SQLAlchemyreference:14 | Hibernate / Spring JDBC |
| 依赖管理 | npm |
go mod |
pip |
Maven / Gradle |
| 类型安全 | 弱(TypeScript 可选) | 强 | 弱 | 强 |
官方文档
- PostgreSQL Full Text Search (Chapter 12)
- PostgreSQL Text Search Functions (Section 9.13)
- PostgreSQL Text Search Types (Section 8.11)
- PostgreSQL GIN Indexes
- pgvector GitHub Repository
- pgvector Documentation
参考链接
- PostgreSQL Full Text Search Tutorial
- pgvector - Open Source Vector Similarity Search for PostgreSQL
- Elasticsearch vs PostgreSQL Full-Text Search
总结
本文系统梳理了 PostgreSQL 全文检索与向量检索的核心机制、适用场景及实现方式。全文检索以倒排索引为核心,通过 tsvector / tsquery 类型和 GIN 索引实现高效的关键词匹配;向量检索通过 pgvector 扩展和 Embedding 模型实现语义级别的相似度检索。二者并非互斥,而是互补关系------全文检索擅长精确定位,向量检索擅长语义理解。
通过混合检索策略(Pre-filter / Post-filter / 加权排序),可以在同一 PostgreSQL 实例中实现兼具效率与智能的搜索体验。PostgreSQL 同时原生支持全文检索与向量检索,是构建现代检索系统的理想数据库选型。