返回首页

PostgreSQL 中的混合搜索 | VectorChord 设置

本文描述了使用 PostgreSQL 和 VectorChord 进行混合搜索的基础设施设置。它涵盖了表设计、自动扩展安装以及数据处理管道的组织。该材料面向创建高级搜索系统的开发者。

如何使用 VectorChord 在 PostgreSQL 中设置混合搜索
Advertisement 728x90

PostgreSQL 和 VectorChord 上的混合搜索:基础设施搭建

混合搜索结合了向量搜索和全文搜索方法,正成为现代搜索系统的标准配置。本文将深入探讨基于 PostgreSQL 和 VectorChord 的基础设施搭建方案——这一解决方案简化了混合搜索的实现,无需手动安装扩展或进行复杂配置。

环境搭建:Docker 和 PostgreSQL

开始之前,我们需要一个容器化环境。我们将使用 Docker Compose 部署带有预装 VectorChord 扩展的 PostgreSQL。请注意:在配置中,我们无需显式指定扩展——它们会在首次连接时通过 VechordRegistry 自动添加。

示例 docker-compose-dev.yml 文件:

Google AdInline article slot
services:
  postgres:
    image: tensorchord/vchord-suite:pg18-latest
    environment:
      POSTGRES_DB: ${DB__NAME}
      POSTGRES_USER: ${DB__USER}
      POSTGRES_PASSWORD: ${DB__PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB__USER} -d ${DB__NAME}"]
      interval: 5s
      timeout: 5s
      retries: 5
volumes:
  pgdata:

关键点:tensorchord/vchord-suite 镜像已包含所有必要组件,但扩展仅在首次通过 VechordRegistry 访问时激活。这避免了手动设置,并确保版本兼容性。

混合搜索的表设计

数据结构基于三个表。基础表 BaseTable 包含通用字段:

from datetime import datetime, timezone
from functools import partial
import msgspec
from psycopg.types.json import Jsonb
from vechord import Table

class BaseTable(Table, kw_only=True):
    metadata: Jsonb
    created_at: datetime = msgspec.field(
        default_factory=partial(datetime.now, timezone.utc))
    updated_at: datetime = msgspec.field(
        default_factory=partial(datetime.now, timezone.utc))

metadata 字段以 JSONB 格式存储任意数据,时间戳会在记录创建时自动更新。请注意:updated_at 不会在记录更新时自动刷新——需要在更新方法中手动处理。

Google AdInline article slot

Document 表:

import msgspec
from vechord.spec import PrimaryKeyUUID
from .base import BaseTable

class Document(BaseTable, kw_only=True):
    uid: PrimaryKeyUUID = msgspec.field(default_factory=PrimaryKeyUUID.factory)
    title: str
    text: str

主键使用 UUID。Chunk 表通过外键关联文档:

from vechord.spec import ForeignKey, Keyword, PrimaryKeyUUID, Vector

class Chunk(BaseTable, kw_only=True):
    uid: PrimaryKeyUUID = msgspec.field(default_factory=PrimaryKeyUUID.factory)
    doc_id: Annotated[UUID, ForeignKey[Document.uid]]
    content: str
    content_tsv: Keyword
    embedding: DenseVector
    chunk_index: int

这里,content_tsv 用于全文搜索(BM25),embedding 存储块的向量表示。外键 doc_id 会自动实现级联删除。

Google AdInline article slot

重要提示:尝试将 uid 移到基类会导致错误,因为 ForeignKey 会在 BaseTable 而非具体表中查找字段。因此,每个表都需要重复定义主键。

VechordRegistry:集中式数据库管理

VechordRegistry 类作为与数据库交互的统一接口,自动处理:

  • 扩展安装(vchordvchord_bm25pg_tokenizer
  • 表和分词器创建
  • CRUD 操作和搜索

初始化:

from vechord import VechordRegistry
from core import settings
from .document import Document
from .doc_chunk import Chunk

vr = VechordRegistry(
    namespace=settings.db.namespace,
    url=settings.db.db_url,
    tables=[Document, Chunk]
)

首次使用(在 async with vr: 上下文中),会运行 init_extension 方法,安装扩展并配置 search_path。这消除了手动设置的需求,并减少配置错误。

插入文档的示例用法:

async with vr:
    doc = Document(title='Note', text='Some text', metadata=Jsonb({}))
    await vr.insert(doc)

运行此代码后,数据库中会出现所有必要扩展、表和记录。

数据处理管道

对于创建文档后进行分块等复杂操作,使用 VechordPipeline。它允许将多个步骤组合成管道,每阶段结果自动保存到数据库。

步骤 1:文档创建。@vr.inject(output=Document) 装饰器确保对象持久化:

@vr.inject(output=Document)
async def _create_document(doc_data: DocumentCreate) -> Document:
    doc = Document(
        title=doc_data.title,
        text=doc_data.text,
        metadata=Jsonb(doc_data.metadata)
    )
    return doc

步骤 2:分块生成。从 Document 表获取数据,返回块列表:

@vr.inject(input=Document, output=Chunk)
async def create_chunks(uid: UUID, text: str) -> list[Chunk]:
    chunks = await _chunker.segment(text)
    return [
        Chunk(
            doc_id=uid,
            content=chunk,
            content_tsv=Keyword(chunk),
            embedding=DenseVector(await _embedder.vectorize_chunk(chunk)),
            metadata=Jsonb({}),
            chunk_index=i
        )
        for i, chunk in enumerate(chunks, start=1)
    ]

管道组装:

from db_models import vr
from .utils import create_chunks, create_document

class DocumentService:
    async def create_document(self, doc_data: DocumentCreate):
        pipeline = vr.pipeline(
            _create_document,
            create_chunks
        )
        await pipeline(doc_data)

这种方法确保操作的原子性,并最小化数据库访问。

关键要点:核心实现注意事项

基于 VectorChord 实现混合搜索时,需要注意:

  • 自动扩展初始化:VechordRegistry 在首次连接时安装所有必要组件,简化部署。
  • 表结构:将文档和块分离对高效搜索至关重要;外键确保数据完整性。
  • 处理管道:使用 VechordPipeline 封装复杂操作,确保数据一致性。

这些要素构成了可扩展搜索系统的基石,结合了向量搜索的精确性和全文搜索的灵活性。

— Editorial Team

Advertisement 728x90

继续阅读