DrizzleORM v0.31.0 릴리스
May 31, 2024

주요 변경사항

참고: drizzle-orm@0.31.0drizzle-kit@0.22.0 이상 버전과 함께 사용할 수 있습니다. Drizzle Kit도 마찬가지입니다. Drizzle Kit 명령어를 실행하면 필요한 경우 업그레이드를 확인하고 안내합니다. Drizzle Kit 업데이트는 아래에서 확인할 수 있습니다.

PostgreSQL 인덱스 API 변경

이전의 Drizzle+PostgreSQL 인덱스 API는 PostgreSQL 문서와 일치하지 않는 잘못된 구조였습니다. 다행히도 쿼리에서 사용되지 않았고, drizzle-kit도 인덱스의 모든 속성을 지원하지 않았습니다. 이는 이제 API를 올바르게 변경하고 drizzle-kit에서 완전한 지원을 제공할 수 있음을 의미합니다.

이전 API

// Index declaration reference
index('name')
  .on(table.column1, table.column2, ...) or .onOnly(table.column1, table.column2, ...)
  .concurrently()
  .using(sql``) // sql expression
  .asc() or .desc()
  .nullsFirst() or .nullsLast()
  .where(sql``) // sql expression

현재 API

// First example, with `.on()`
index('name')
  .on(table.column1.asc(), table.column2.nullsFirst(), ...) or .onOnly(table.column1.desc().nullsLast(), table.column2, ...)
  .concurrently()
  .where(sql``)
  .with({ fillfactor: '70' })

// Second Example, with `.using()`
index('name')
  .using('btree', table.column1.asc(), sql`lower(${table.column2})`, table.column1.op('text_ops'))
  .where(sql``) // sql expression
  .with({ fillfactor: '70' })

새로운 기능

🎉 “pg_vector” 확장 지원

Drizzle 스키마 내에서 확장을 생성하는 특별한 코드는 없습니다. vector 타입, 인덱스, 쿼리를 사용하는 경우 pg_vector 확장이 설치된 PostgreSQL 데이터베이스를 사용한다고 가정합니다.

이제 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('embedding', { dimensions: 3 })
}, (table) => ({
    l2: index('l2_index').using('hnsw', table.embedding.op('vector_l2_ops'))
    ip: index('ip_index').using('hnsw', table.embedding.op('vector_ip_ops'))
    cosine: 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('embedding', { dimensions: 3 })
}, (table) => ({
    l1: index('l1_index').using('hnsw', table.embedding.op('vector_l1_ops'))
    hamming: index('hamming_index').using('hnsw', table.embedding.op('bit_hamming_ops'))
    bit: 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 문서의 몇 가지 쿼리 예제를 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!

🎉 새로운 PostgreSQL 타입: point, line

이제 PostgreSQL Geometric Typespointline을 사용할 수 있습니다.

point 타입은 데이터베이스 매핑을 위한 2가지 모드를 제공합니다: tuplexy.

const items = pgTable('items', {
 point: point('point'),
 pointObj: point('point_xy', { mode: 'xy' }),
});

line 타입은 데이터베이스 매핑을 위한 2가지 모드를 제공합니다: tupleabc.

const items = pgTable('items', {
 line: line('line'),
 lineObj: line('line_abc', { mode: 'abc' }),
});

🎉 기본 “postgis” 확장 지원

Drizzle 스키마 내에서 확장을 생성하는 특별한 코드는 없습니다. postgis 타입, 인덱스, 쿼리를 사용하는 경우 postgis 확장이 설치된 PostgreSQL 데이터베이스를 사용한다고 가정합니다.

postgis 확장의 geometry 타입:

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가지 모드를 제공합니다: tuplexy.

type

현재 릴리스에는 미리 정의된 point 타입이 있으며, 이는 PostgreSQL PostGIS 확장의 geometry(Point) 타입입니다. 다른 타입을 사용하려면 원하는 문자열을 지정할 수 있습니다.

Drizzle Kit 업데이트: drizzle-kit@0.22.0

여기의 릴리스 노트는 drizzle-kit@0.22.0에서 부분적으로 복제되었습니다.

새로운 기능

🎉 새로운 타입 지원

Drizzle Kit은 이제 다음을 처리할 수 있습니다:

🎉 drizzle.config의 새로운 파라미터 - extensionsFilters

PostGIS 확장은 public 스키마에 몇 가지 내부 테이블을 생성합니다. 이는 PostGIS 확장이 설치된 데이터베이스가 있고 push 또는 introspect를 사용하는 경우, 해당 테이블들이 모두 diff 작업에 포함된다는 것을 의미합니다. 이 경우 tablesFilter를 지정하고 확장에서 생성된 모든 테이블을 찾아서 이 파라미터에 나열해야 합니다.

이 문제를 해결하여 이러한 단계를 수행할 필요가 없도록 했습니다. 사용된 확장의 이름으로 extensionsFilters를 지정하기만 하면 Drizzle이 필요한 모든 테이블을 건너뜁니다.

현재는 postgis 옵션만 지원하지만, public 스키마에 테이블을 생성하는 확장이 있다면 더 많은 확장을 추가할 계획입니다.

postgis 옵션은 geography_columns, geometry_columns, spatial_ref_sys 테이블을 건너뜁니다.

import { defineConfig } from 'drizzle-kit'

export default defaultConfig({
  dialect: "postgresql",
  extensionsFilters: ["postgis"],
})

개선사항

데이터베이스 인증 정보를 위한 zod 스키마 업데이트 및 모든 긍정/부정 케이스 테스트 작성

import { defineConfig } from 'drizzle-kit'

export default defaultConfig({
  dialect: "postgresql",
  dbCredentials: {
    ssl: true, //"require" | "allow" | "prefer" | "verify-full" | options from node:tls
  }
})
import { defineConfig } from 'drizzle-kit'

export default defaultConfig({
  dialect: "mysql",
  dbCredentials: {
    ssl: "", // string | SslOptions (ssl options from mysql2 package)
  }
})

libsqlbetter-sqlite3 드라이버를 위한 정규화된 SQLite URL

이러한 드라이버는 서로 다른 파일 경로 패턴을 가지고 있으며, Drizzle Kit은 둘 다 허용하고 각각에 대해 적절한 파일 경로 형식을 생성합니다.

MySQL 및 SQLite 인덱스 표현식 동작 업데이트

이번 릴리스에서 MySQL과 SQLite는 표현식을 SQL 쿼리로 올바르게 매핑합니다. 표현식은 문자열에서 이스케이프되지 않지만 컬럼은 이스케이프됩니다.

export const users = sqliteTable(
  'users',
  {
    id: integer('id').primaryKey(),
    email: text('email').notNull(),
  },
  (table) => ({
    emailUniqueIndex: uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
  }),
);
-- before
CREATE UNIQUE INDEX `emailUniqueIndex` ON `users` (`lower("users"."email")`);

-- now
CREATE UNIQUE INDEX `emailUniqueIndex` ON `users` (lower("email"));

버그 수정

인덱스에 대한 pushgenerate 작동 방식

제한사항

하나 이상의 표현식에 인덱스가 있는 경우 인덱스 이름을 수동으로 지정해야 합니다

예제

index().on(table.id, table.email) // will work well and name will be autogeneretaed
index('my_name').on(table.id, table.email) // will work well

// but

index().on(sql`lower(${table.email})`) // error
index('my_name').on(sql`lower(${table.email})`) // will work well

기존 인덱스에서 다음 필드(아래 목록)가 변경된 경우 Push는 문을 생성하지 않습니다:

push 워크플로우를 사용하고 인덱스에서 이러한 필드를 변경하려는 경우 다음을 수행해야 합니다:

generate 명령의 경우, drizzle-kit은 새로운 drizzle 인덱스 API의 모든 속성에 대한 인덱스 변경 사항에 의해 트리거되므로 여기에는 제한사항이 없습니다.