PostgreSQL과 VectorChord를 활용한 하이브리드 검색: 인프라 설정
벡터 검색과 전체 텍스트 검색을 결합한 하이브리드 검색은 현대 검색 시스템의 표준으로 자리 잡고 있습니다. 이 글에서는 PostgreSQL과 VectorChord를 기반으로 인프라를 설정하는 방법을 자세히 살펴보겠습니다. 이는 수동 확장 설치나 복잡한 설정 없이 하이브리드 검색 구현을 간편하게 만들어주는 솔루션입니다.
환경 설정: Docker와 PostgreSQL
시작하기 위해 컨테이너화된 환경이 필요합니다. Docker Compose를 사용해 VectorChord 확장이 미리 설치된 PostgreSQL을 배포하겠습니다. 주의: 설정에서 확장을 명시적으로 지정하지 않습니다. 첫 연결 시 VechordRegistry를 통해 자동으로 추가됩니다.
docker-compose-dev.yml 파일 예시:
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은 레코드 변경 시 자동 업데이트되지 않으므로 업데이트 메서드에서 수동으로 처리해야 합니다.
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는 자동으로 캐스케이드 삭제를 구현합니다.
중요: uid를 기본 클래스에 이동하려 하면 오류가 발생합니다. ForeignKey가 BaseTable에서 필드를 찾기 때문입니다. 따라서 각 테이블마다 기본 키를 중복 정의해야 합니다.
VechordRegistry: 중앙화된 데이터베이스 관리
VechordRegistry 클래스는 데이터베이스 작업을 위한 단일 인터페이스를 제공합니다. 이를 통해 자동화되는 작업은 다음과 같습니다:
- 확장 설치(
vchord,vchord_bm25,pg_tokenizer) - 테이블 및 토크나이저 생성
- 데이터 생성/조회/수정/삭제 작업 및 검색
초기화:
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
아직 댓글이 없습니다.