DrizzleORM v0.31.0 发布
May 31, 2024

重大变更

注意:drizzle-orm@0.31.0 可以与 drizzle-kit@0.22.0 或更高版本一起使用。Drizzle Kit 也是如此。如果您运行 Drizzle Kit 命令,它会检查并提示您进行升级(如果需要)。您可以检查是否有 Drizzle Kit 更新。下面

PostgreSQL 索引 API 已更改

Drizzle + PostgreSQL 的旧索引 API 是错误的,并且与 PostgreSQL 文档不匹配。好消息是它没有被用于查询,并且 Drizzle Kit 不支持索引的所有属性。这意味着我们现在可以将 API 更改为正确的版本,并且在 Drizzle Kit 中提供完全支持

旧 API

  • 没有在 .on 中定义 SQL 表达式的方法。
  • .using.on 在我们的情况下是同一件事,所以这里的 API 是错误的。
  • .asc(), .desc(), .nullsFirst(), 和 .nullsLast() 应该为每个列或表达式指定索引,而不是索引本身。
// 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 schema 中没有特定的代码来创建扩展。我们假设如果你在使用 vector 类型、索引和查询,那么你的 PostgreSQL 数据库有安装了 pg_vector 扩展。

你现在可以指定 pg_vector 的索引,并在 Drizzle 中使用 pg_vector 函数进行查询、排序等。

让我们从 pg_vector 文档中取几个索引的例子,并将它们翻译成 Drizzle

L2 距离,Inner product 和 Cosine 距离

// 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 距离,Hamming 距离和 Jaccard 距离 - 在 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)}`;
}

你可以给它命名,并且更改操作符。这个例子允许一个数字数组、字符串数组、字符串,甚至是选择查询。你也可以创建任何其他类型,甚至可以贡献并提交一个 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

你现在可以使用 pointline 来自 PostgreSQL Geometric Types

point 类型有2种映射数据库的方式:tuplexy

  • tuple 将接受插入并映射选择到元组。所以,数据库中的 Point(1,2) 将作为 [1,2] 在 Drizzle 中类型化。

  • xy 将接受插入并映射选择到具有 x、y 坐标的对象。所以,数据库中的 Point(1,2) 将作为 { x: 1, y: 2 } 在 Drizzle 中类型化

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

line 类型有2种映射数据库的方式:tupleabc

  • tuple 将接受插入并映射选择到元组。所以,数据库中的 Line3 将作为 [1,2,3] 在 Drizzle 中类型化。

  • abc 将接受插入并映射选择到对象,包含 Ax + By + C = 0 方程的 a、b 和 c 常数。所以,数据库中的 Line3 将作为 { a: 1, b: 2, c: 3 } 在 Drizzle 中类型化。

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

🎉 基础 “postgis” 扩展支持

Drizzle schema 中没有特定的代码来创建扩展。我们假设如果你在使用 postgis 类型、索引和查询,那么你的 PostgreSQL 数据库有安装了 postgis 扩展。

geometry 类型来自 PostgreSQL 的 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 种映射数据库的方式:tuplexy

  • tuple 将接受插入并映射选择到元组。所以,数据库中的 geometry 将作为 [1,2] 在 Drizzle 中类型化。
  • xy 将接受插入并映射选择到对象,包含 x、y 坐标。所以,数据库中的 geometry 将作为 { x: 1, y: 2 } 在 Drizzle 中类型化

type

当前版本有预定义类型:point,这是 PostgreSQL PostGIS 扩展中的 geometry(Point) 类型。你可以在那里指定任何字符串,如果你想要使用其他类型

Drizzle Kit 更新:drizzle-kit@0.22.0

发布笔记在这里部分复制自 drizzle-kit@0.22.0

新特性

🎉 对新类型的支持

Drizzle Kit 现在可以处理:

  • pointline 来自 PostgreSQL
  • vector 来自 PostgreSQL 的 pg_vector 扩展
  • geometry 来自 PostgreSQL 的 PostGIS 扩展

🎉 新参数在 drizzle.config - extensionsFilters

PostGIS 扩展在 public 模式中创建了一些内部表。这意味着如果你有一个安装了 PostGIS 扩展的 PostgreSQL 数据库,并且使用 pushintrospect,那么所有这些表都会包含在 diff 操作中。在这种情况下,你需要指定 tablesFilter,找到所有由扩展创建的表,并将它们列在这个参数中。

我们已经解决了这个问题,这样你就不需要采取所有这些步骤。只需指定 extensionsFilters 扩展名称,Drizzle 就会跳过所有必要的表。

目前我们只支持 postgis 选项,但我们计划添加更多扩展,如果它们在 public 模式中创建表。

postgis 选项会跳过 geography_columnsgeometry_columnsspatial_ref_sys

import { defineConfig } from 'drizzle-kit'

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

改进

更新 zod 数据库凭证 schema 和所有正/负案例的测试

  • 支持完整的 SSL 参数集到 kit 配置,提供来自 node:tls 连接的类型
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)
  }
})

规范化SQLite urls for libsql and better-sqlite3 drivers

这些驱动程序有不同的文件路径模式,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})`),
  }),
);
-- 以前
CREATE UNIQUE INDEX `emailUniqueIndex` ON `users` (`lower("users"."email")`);

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

Bug 修复

  • [BUG]: 多个约束没有添加(只有第一个被生成) - #2341
  • Drizzle Studio: 错误: 连接意外终止 - #435
  • 无法运行 SQLite 迁移 - #432
  • 错误: unknown option ‘—config’ - #423

pushgenerate 如何为索引工作

限制

如果你至少在一个表达式上有一个索引,你应该手动指定索引的名称

例子

index().on(table.id, table.email) // 会正常工作并且自动生成名称
index('my_name').on(table.id, table.email) // 会正常工作

// 但是

index().on(sql`lower(${table.email})`) // 错误
index('my_name').on(sql`lower(${table.email})`) // 会正常工作

push 不会为以下字段变更的索引生成语句:

  • .on().using() 中的表达式
  • .where() 语句
  • .op() 列上的操作符类

如果你在使用 push 工作流,并且想要更改索引中的这些字段,你需要:

  • 注释索引
  • 推送
  • 取消注释索引并更改这些字段
  • 再次推送

对于 generate 命令,Drizzle Kit 将由索引中的任何更改触发,对于 Drizzle 的新索引 API 中的任何属性,所以这里没有限制。

Become a Gold Sponsor