纲要
- 信息检索的演进阶段
- 关键词匹配
- 全文检索
- 语义检索(向量检索)
- 全文检索(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]。例如:
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:4][reference:5]。
to_tsvector :将文本转换为 tsvector 类型[reference:6]。
to_tsquery :将查询字符串转换为 tsquery 类型[reference:7]。
@@ 运算符:判断 tsvector 是否匹配 tsquery[reference:8]。
基础示例
— 创建测试表
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]:
CREATE INDEX idx_documents_tsv ON documents USING GIN (tsv);
GIN 索引是全文检索的首选索引类型,它存储 tsvector 中每个词素的索引条目[reference:10]。需要注意的是,表达式中必须显式指定文本搜索配置(如 'english'),否则索引无法被使用[reference:11]。
相关性排序
ts_rank 函数用于计算文档与查询的相关性分数,分数越高表示匹配程度越高[reference:12]:
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
— 创建扩展
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);
向量相似度检索
— 余弦相似度检索(值越接近 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)实践
在实际生产环境中,将全文检索与向量检索结合使用,可以实现“高效、精准、智能”的搜索体验。
数据表设计
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 文档,再通过全文检索进行精确过滤:
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)
先用全文检索过滤候选文档,再通过向量相似度进行排序:
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 分数与向量相似度分数,进行加权综合排序:
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 | 将文档文本转换为 tsvector[reference:13] |
| to_tsquery | to_tsquery([config regconfig, ] query text) → tsquery | 将查询字符串转换为 tsquery[reference:14] |
| plainto_tsquery | plainto_tsquery([config regconfig, ] query text) → tsquery | 将纯文本转换为 tsquery,自动添加 & 运算符[reference:15] |
| phraseto_tsquery | phraseto_tsquery([config regconfig, ] query text) → tsquery | 将纯文本转换为短语查询 tsquery[reference: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 是否匹配 tsquery[reference:18] |
| ` | ` |
pgvector 核心 API
| vector | vector(n) | 创建 n 维向量类型 |
| <=> | vector <=> vector → float | 余弦距离(值越小越相似) |
| <-> | vector <-> vector → float | 欧几里得距离 |
| <#> | vector <#> vector → float | 负内积 |
Demo 完整示例
以下是一个完整的 Node.js + PostgreSQL 混合检索示例,使用 pg 驱动连接数据库。
运行说明
初始化 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 代码
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 示例
运行说明
安装依赖:
go 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 脚本。
运行:
go 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 类型
- 支持 <=> 余弦距离、<-> 欧几里得距离、<#> 负内积运算符
完整代码
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 示例
运行说明
安装依赖:
pip install psycopg2-binary pgvector
数据库准备:确保 PostgreSQL 已安装 pgvector 扩展,并执行 Node.js 示例中的初始化 SQL 脚本。
运行:
python hybrid_search_demo.py
代码说明
Python 示例使用 psycopg2 作为 PostgreSQL 驱动,pgvector.psycopg2 提供 vector 类型的支持[reference:5]。核心流程如下:
- 使用 psycopg2.connect 建立数据库连接
- 调用 register_vector 注册 vector 类型[reference:6][reference:7]
- 执行混合检索 SQL,使用列表作为向量参数
- 使用余弦距离运算符 <=> 计算相似度
技术点总结
- 使用 psycopg2-binary + pgvector Python 包
- 通过 register_vector 注册自定义类型
- 向量参数以 Python list 形式传递,自动转换为 PostgreSQL vector
- 支持 <=> 余弦距离、<-> 欧几里得距离、<#> 负内积运算符
完整代码
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):
<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 脚本。
编译与运行:
mvn 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 + pgvector Java 库
- 通过 PGvector.registerTypes 注册自定义类型
- 使用 setObject 传递 PGvector 参数
- 支持 <=> 余弦距离、<-> 欧几里得距离、<#> 负内积运算符
完整代码
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();
}
}
}
多语言对比
| 驱动/库 | 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 | SQLAlchemy[reference: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 同时原生支持全文检索与向量检索,是构建现代检索系统的理想数据库选型。





