PostgreSQL笔记48: 全文检索与向量检索的对比与混合检索实践

纲要

  • 信息检索的演进阶段
    • 关键词匹配
    • 全文检索
    • 语义检索(向量检索)
  • 全文检索(Full-Text Search)核心机制
    • tsvector 数据类型
    • tsquery 数据类型
    • to_tsvectorto_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');

返回结果中不包含 aonit 等停用词,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 驱动连接数据库。

运行说明

  1. 确保 PostgreSQL 已安装 pgvector 扩展
  2. 执行下方 SQL 初始化脚本
  3. 安装依赖:npm install pg
  4. 运行脚本: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);

技术点总结

  • 全文检索 :使用 tsvectortsqueryto_tsvectorto_tsquery@@ 运算符
  • 向量检索 :使用 pgvector 的 vector 类型和余弦距离运算符 <=>
  • 混合检索:在同一 SQL 查询中结合全文过滤与向量排序
  • 加权排序 :使用 ts_rank 和向量相似度计算综合分数
  • 索引加速:GIN 索引加速全文检索,HNSW 索引加速向量检索

多语言示例

本部分提供 Go、Python 和 Java 三种语言的完整可运行示例,功能与前述 Node.js 版本等价:连接 PostgreSQL 数据库,执行混合检索(全文过滤 + 向量相似度排序),输出综合排序结果。

Go 示例

运行说明
  1. 安装依赖

    bash 复制代码
    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
  2. 数据库准备:确保 PostgreSQL 已安装 pgvector 扩展,并执行 Node.js 示例中的初始化 SQL 脚本。

  3. 运行

    bash 复制代码
    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 类型
  • 支持 <=> 余弦距离、<-> 欧几里得距离、<#> 负内积运算符
完整代码
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 示例

运行说明
  1. 安装依赖

    bash 复制代码
    pip install psycopg2-binary pgvector
  2. 数据库准备:确保 PostgreSQL 已安装 pgvector 扩展,并执行 Node.js 示例中的初始化 SQL 脚本。

  3. 运行

    bash 复制代码
    python hybrid_search_demo.py
代码说明

Python 示例使用 psycopg2 作为 PostgreSQL 驱动,pgvector.psycopg2 提供 vector 类型的支持reference:5。核心流程如下:

  • 使用 psycopg2.connect 建立数据库连接
  • 调用 register_vector 注册 vector 类型reference:6reference:7
  • 执行混合检索 SQL,使用列表作为向量参数
  • 使用余弦距离运算符 <=> 计算相似度
技术点总结
  • 使用 psycopg2-binary + pgvector Python 包
  • 通过 register_vector 注册自定义类型
  • 向量参数以 Python list 形式传递,自动转换为 PostgreSQL vector
  • 支持 <=> 余弦距离、<-> 欧几里得距离、<#> 负内积运算符
完整代码
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 示例

运行说明
  1. 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>
  2. 数据库准备:确保 PostgreSQL 已安装 pgvector 扩展,并执行 Node.js 示例中的初始化 SQL 脚本。

  3. 编译与运行

    bash 复制代码
    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 参数
  • 支持 <=> 余弦距离、<-> 欧几里得距离、<#> 负内积运算符
完整代码
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 asyncpgasyncio CompletableFuture / R2DBC
ORM 可选 TypeORM / Prisma GORM / Bun / Ent SQLAlchemyreference:14 Hibernate / Spring JDBC
依赖管理 npm go mod pip Maven / Gradle
类型安全 弱(TypeScript 可选)

官方文档

参考链接

总结

本文系统梳理了 PostgreSQL 全文检索与向量检索的核心机制、适用场景及实现方式。全文检索以倒排索引为核心,通过 tsvector / tsquery 类型和 GIN 索引实现高效的关键词匹配;向量检索通过 pgvector 扩展和 Embedding 模型实现语义级别的相似度检索。二者并非互斥,而是互补关系------全文检索擅长精确定位,向量检索擅长语义理解。

通过混合检索策略(Pre-filter / Post-filter / 加权排序),可以在同一 PostgreSQL 实例中实现兼具效率与智能的搜索体验。PostgreSQL 同时原生支持全文检索与向量检索,是构建现代检索系统的理想数据库选型。

相关推荐
liuyicenysabel2 小时前
Flask 个人站 iProfile 上线笔记(k3s + Nginx + Let‘s Encrypt)
笔记·nginx·flask
深蓝海拓2 小时前
基于QtPy (PySide6) 的PLC-HMI工程项目(十五)后续完善和改进:PLC端数据解析的防粘包、残包、废包、拆包
笔记·学习·plc
小黄蚁2 小时前
LVGL学习笔记(六)
笔记·学习
Wang's Blog3 小时前
PostgreSQL笔记49:向量检索核心算法、索引调优与过滤策略深度解析
笔记·算法·postgresql
疯狂打码的少年3 小时前
【数据结构】交换类排序:冒泡与快速排序
数据结构·笔记·算法·排序算法
chnyi6_ya3 小时前
VideoHallu 论文阅读笔记
论文阅读·笔记
Wang's Blog4 小时前
PostgreSQL笔记51: 基于 pgvector 构建企业级智能问答系统
数据库·笔记·postgresql
自小吃多4 小时前
Capture软件原理图添加差分属性笔记
笔记·嵌入式硬件
日常筹谋记5 小时前
ATS选不对运维两行泪:数据中心市电与柴发切换场景的双电源自动转换开关选型笔记
运维·数据库·笔记