Become a Gold Sponsor
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

// 索引声明参考
index('name')
  .on(table.column1, table.column2, ...) 或 .onOnly(table.column1, table.column2, ...)
  .concurrently()
  .using(sql``) // sql 表达式
  .asc() 或 .desc()
  .nullsFirst() 或 .nullsLast()
  .where(sql``) // sql 表达式

当前 API

// 第一个示例,使用 `.on()`
index('name')
  .on(table.column1.asc(), table.column2.nullsFirst(), ...) 或 .onOnly(table.column1.desc().nullsLast(), table.column2, ...)
  .concurrently()
  .where(sql``)
  .with({ fillfactor: '70' })

// 第二个示例,使用 `.using()`
index('name')
  .using('btree', table.column1.asc(), sql`lower(${table.column2})`, table.column1.op('text_ops'))
  .where(sql``) // sql 表达式
  .with({ fillfactor: '70' })

新特性

🎉 支持 “pg_vector” 扩展

在 Drizzle 架构中没有创建扩展的具体代码。我们假设如果您使用向量类型、索引和查询,您已经安装了带有 pg_vector 扩展的 PostgreSQL 数据库。

现在您可以为 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('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 距离、汉明距离和 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 文档中获取几个 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)

// 以及更多!

🎉 新的 PostgreSQL 类型:point, line

现在您可以使用 PostgreSQL 几何类型 中的 pointline

类型 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: point('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 }),
});

模式 类型 geometry 具有 2 种模式用于数据库的映射:tuplexy

类型

本次发布具有预定义类型: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 扩展的数据库并使用 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 架构以支持数据库凭据并为所有正/负案例编写测试

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 的 libsql 和 better-sqlite3 驱动程序的 URL

这些驱动程序具有不同的文件路径模式,Drizzle Kit 将接受两者并为每一个创建适当的文件路径格式。

更新 MySQL 和 SQLite 的 index-as-expression 行为

在本次发布中,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"));

错误修复

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 将不会生成语句:

如果您使用 push 工作流并希望更改这些字段中的索引,您需要:

对于 generate 命令,任何在新 drizzle 索引 API 中对索引的属性的更改都会触发 drizzle-kit,因此在这里没有限制。