Drizzle 스키마 내에서 확장을 생성하는 특정 코드는 없습니다. vector 타입, 인덱스, 쿼리를 사용하는 경우 PostgreSQL 데이터베이스에 pg_vector 확장이 설치되어 있다고 가정합니다.
PostgreSQL 확장
pg_vector
pg_vector는 Postgres를 위한 오픈소스 벡터 유사도 검색입니다.
벡터를 나머지 데이터와 함께 저장합니다. 지원하는 기능:
- 정확 및 근사 최근접 이웃 검색
- 단정밀도, 반정밀도, 이진, 희소 벡터
- L2 거리, 내적, 코사인 거리, L1 거리, 해밍 거리, 자카드 거리
컬럼 타입
vector
벡터를 나머지 데이터와 함께 저장합니다
자세한 내용은 공식 pg_vector **문서**를 참조하세요.
const table = pgTable('table', {
embedding: vector({ dimensions: 3 })
})CREATE TABLE IF NOT EXISTS "table" (
"embedding" vector(3)
);인덱스
이제 pg_vector를 위한 인덱스를 지정하고 쿼리, 정렬 등을 위해 pg_vector 함수를 활용할 수 있습니다.
pg_vector 문서에서 몇 가지 pg_vector 인덱스 예제를 가져와 Drizzle로 변환해 보겠습니다.
L2 거리, 내적, 코사인 거리
// CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
// CREATE INDEX ON items USING hnsw (embedding vector_ip_ops);
// CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
const table = pgTable('items', {
embedding: vector({ dimensions: 3 })
}, (table) => [
index('l2_index').using('hnsw', table.embedding.op('vector_l2_ops'))
index('ip_index').using('hnsw', table.embedding.op('vector_ip_ops'))
index('cosine_index').using('hnsw', table.embedding.op('vector_cosine_ops'))
])L1 거리, 해밍 거리, 자카드 거리 - pg_vector 0.7.0 버전에서 추가됨
// CREATE INDEX ON items USING hnsw (embedding vector_l1_ops);
// CREATE INDEX ON items USING hnsw (embedding bit_hamming_ops);
// CREATE INDEX ON items USING hnsw (embedding bit_jaccard_ops);
const table = pgTable('table', {
embedding: vector({ dimensions: 3 })
}, (table) => [
index('l1_index').using('hnsw', table.embedding.op('vector_l1_ops'))
index('hamming_index').using('hnsw', table.embedding.op('bit_hamming_ops'))
index('bit_jaccard_index').using('hnsw', table.embedding.op('bit_jaccard_ops'))
])헬퍼 함수
쿼리를 위해 벡터를 위한 사전 정의된 함수를 사용하거나 SQL 템플릿 연산자를 사용하여 커스텀 함수를 생성할 수 있습니다.
다음 헬퍼들도 사용할 수 있습니다:
import { l2Distance, l1Distance, innerProduct,
cosineDistance, hammingDistance, jaccardDistance } from 'drizzle-orm'
l2Distance(table.column, [3, 1, 2]) // table.column <-> '[3, 1, 2]'
l1Distance(table.column, [3, 1, 2]) // table.column <+> '[3, 1, 2]'
innerProduct(table.column, [3, 1, 2]) // table.column <#> '[3, 1, 2]'
cosineDistance(table.column, [3, 1, 2]) // table.column <=> '[3, 1, 2]'
hammingDistance(table.column, '101') // table.column <~> '101'
jaccardDistance(table.column, '101') // table.column <%> '101'pg_vector에 사용할 다른 함수가 있다면 기존에 우리가 가지고 있는 것에서 구현을 복제할 수 있습니다. 다음과 같이 할 수 있습니다:
export function l2Distance(
column: SQLWrapper | AnyColumn,
value: number[] | string[] | TypedQueryBuilder<any> | string,
): SQL {
if (is(value, TypedQueryBuilder<any>) || typeof value === 'string') {
return sql`${column} <-> ${value}`;
}
return sql`${column} <-> ${JSON.stringify(value)}`;
}원하는 대로 이름을 짓고 연산자를 변경하세요. 이 예제는 숫자 배열, 문자열 배열, 문자열 또는 심지어 select 쿼리도 허용합니다. 원하는 다른 타입을 자유롭게 만들거나 기여하여 PR을 제출하세요.
예제
pg_vector 문서에서 몇 가지 pg_vector 쿼리 예제를 가져와 Drizzle로 변환해 보겠습니다.
import { l2Distance } from 'drizzle-orm';
// SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
db.select().from(items).orderBy(l2Distance(items.embedding, [3,1,2]))
// SELECT embedding <-> '[3,1,2]' AS distance FROM items;
db.select({ distance: l2Distance(items.embedding, [3,1,2]) })
// SELECT * FROM items ORDER BY embedding <-> (SELECT embedding FROM items WHERE id = 1) LIMIT 5;
const subquery = db.select({ embedding: items.embedding }).from(items).where(eq(items.id, 1));
db.select().from(items).orderBy(l2Distance(items.embedding, subquery)).limit(5)
// SELECT (embedding <#> '[3,1,2]') * -1 AS inner_product FROM items;
db.select({ innerProduct: sql`(${maxInnerProduct(items.embedding, [3,1,2])}) * -1` }).from(items)
// and more!postgis
Drizzle 스키마 내에서 확장을 생성하는 특정 코드는 없습니다. postgis 타입, 인덱스, 쿼리를 사용하는 경우 PostgreSQL 데이터베이스에 postgis 확장이 설치되어 있다고 가정합니다.
PostGIS 웹사이트에서 언급하듯이:
PostGIS는 지리공간 데이터의 저장, 인덱싱, 쿼리 지원을 추가하여 PostgreSQL 관계형 데이터베이스의 기능을 확장합니다.
PostGIS 확장과 함께 introspect 또는 push 명령어를 사용하고 PostGIS 테이블을 포함하고 싶지 않다면, extensionsFilters를 사용하여 모든 PostGIS 테이블을 무시할 수 있습니다.
컬럼 타입
geometry
지오메트리 데이터를 나머지 데이터와 함께 저장합니다
자세한 내용은 공식 PostGIS **문서**를 참조하세요.
const items = pgTable('items', {
geo: geometry('geo', { type: 'point' }),
geoObj: geometry('geo_obj', { type: 'point', mode: 'xy' }),
geoSrid: geometry('geo_options', { type: 'point', mode: 'xy', srid: 4000 }),
});mode
geometry 타입은 데이터베이스에서의 매핑을 위한 2가지 모드를 가지고 있습니다: tuple과 xy.
tuple은 삽입 시 허용되며 조회 시 튜플로 매핑됩니다. 따라서 데이터베이스의 geometry는 drizzle에서 [1,2]로 타입이 지정됩니다.xy는 삽입 시 허용되며 조회 시 x, y 좌표를 가진 객체로 매핑됩니다. 따라서 데이터베이스의 geometry는 drizzle에서{ x: 1, y: 2 }로 타입이 지정됩니다.
type
현재 릴리즈는 PostgreSQL PostGIS 확장의 geometry(Point) 타입인 point라는 사전 정의된 타입을 가지고 있습니다. 다른 타입을 사용하고 싶다면 원하는 문자열을 지정할 수 있습니다.
인덱스
사용 가능한 Drizzle 인덱스 API를 사용하여 PostGIS를 위한 모든 인덱스를 작성할 수 있습니다.
예제
// CREATE INDEX custom_idx ON table USING GIST (geom);
const table = pgTable('table', {
geo: geometry({ type: 'point' }),
}, (table) => [
index('custom_idx').using('gist', table.geo)
])