Drizzle | pgvector 확장을 활용한 벡터 유사성 검색
This guide assumes familiarity with:
npm
yarn
pnpm
bun
npm i openai
  • drizzle-orm@0.31.0drizzle-kit@0.22.0 이상 버전이 필요합니다.

Drizzle ORM과 함께 PostgreSQL에서 벡터 유사성 검색을 구현하려면 pgvector 확장을 사용할 수 있습니다. 이 확장은 벡터 작업과 유사성 검색을 수행하기 위한 함수를 제공합니다.

현재 Drizzle은 확장을 자동으로 생성하지 않으므로 수동으로 생성해야 합니다. 빈 마이그레이션 파일을 만들고 SQL 쿼리를 추가합니다.

npx drizzle-kit generate --custom
CREATE EXTENSION vector;

유사성 검색을 수행하려면 벡터 컬럼과 이 컬럼에 대한 HNSW 또는 IVFFlat 인덱스를 생성해야 합니다. 이는 성능을 향상시키기 위함입니다.

schema.ts
migration.sql
import { index, pgTable, serial, text, vector } from 'drizzle-orm/pg-core';

export const guides = pgTable(
  'guides',
  {
    id: serial('id').primaryKey(),
    title: text('title').notNull(),
    description: text('description').notNull(),
    url: text('url').notNull(),
    embedding: vector('embedding', { dimensions: 1536 }),
  },
  (table) => ({
    embeddingIndex: index('embeddingIndex').using('hnsw', table.embedding.op('vector_cosine_ops')),
  }),
);

embedding 컬럼은 가이드 설명의 벡터 임베딩을 저장하는 데 사용됩니다. 벡터 임베딩은 데이터를 벡터로 변환하여 언어 모델이 처리할 수 있는 공통 형식으로 표현하는 방법입니다. 이를 통해 두 벡터 간의 거리를 측정하는 등 수학적 연산을 수행하여 두 데이터 항목의 유사성이나 차이를 확인할 수 있습니다.

이 예제에서는 OpenAI 모델을 사용하여 설명에 대한 임베딩을 생성합니다.

import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env['OPENAI_API_KEY'],
});

export const generateEmbedding = async (value: string): Promise => {
  const input = value.replaceAll('\n', ' ');

  const { data } = await openai.embeddings.create({
    model: 'text-embedding-ada-002',
    input,
  });

  return data[0].embedding;
};

임베딩을 기반으로 유사한 가이드를 검색하려면 gtsql 연산자를 사용하여 embedding 컬럼과 생성된 임베딩 간의 유사성을 계산할 수 있습니다.

import { cosineDistance, desc, gt, sql } from 'drizzle-orm';
import { generateEmbedding } from './embedding';
import { guides } from './schema';

const db = drizzle(...);

const findSimilarGuides = async (description: string) => {
  const embedding = await generateEmbedding(description);

  const similarity = sql`1 - (${cosineDistance(guides.embedding, embedding)})`;

  const similarGuides = await db
    .select({ name: guides.title, url: guides.url, similarity })
    .from(guides)
    .where(gt(similarity, 0.5))
    .orderBy((t) => desc(t.similarity))
    .limit(4);

  return similarGuides;
};
const description = '다양한 플랫폼에서 Drizzle ORM 사용 가이드';

const similarGuides = await findSimilarGuides(description);
[
  {
    name: 'Turso와 함께하는 Drizzle',
    url: '/docs/tutorials/drizzle-with-turso',
    similarity: 0.8642314333984994
  },
  {
    name: 'Supabase 데이터베이스와 함께하는 Drizzle',
    url: '/docs/tutorials/drizzle-with-supabase',
    similarity: 0.8593631126014918
  },
  {
    name: 'Neon Postgres와 함께하는 Drizzle',
    url: '/docs/tutorials/drizzle-with-neon',
    similarity: 0.8541051184461372
  },
  {
    name: 'Vercel Edge Functions와 함께하는 Drizzle',
    url: '/docs/tutorials/drizzle-with-vercel-edge-functions',
    similarity: 0.8481551084241092
  }
]