DrizzleORM v0.31.0 版本发布
2024年5月31日

重大变更

注意:[email protected] 可与 [email protected] 或更高版本一起使用。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 Schema 中没有用于创建扩展的特定代码。我们假定如果您使用向量类型、索引和查询,则您已安装了带 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)}`;
}

您可以随意命名并更改操作符。此示例允许使用数字数组、字符串数组、字符串,甚至是一个 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!

🎉 新增 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: line('line_abc', { mode: 'abc' }),
});

🎉 基本支持 “postgis” 扩展

Drizzle Schema 中没有用于创建扩展的特定代码。我们假定如果您使用 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

类型

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

Drizzle Kit 更新:[email protected]

这里的发布说明部分复制自 [email protected]

新特性

🎉 支持新类型

Drizzle Kit 现在可以处理

🎉 drizzle.config 中的新参数 - extensionsFilters

PostGIS 扩展会在 public schema 中创建一些内部表。这意味着如果您有一个带有 PostGIS 扩展的数据库并使用 pushintrospect 命令,所有这些表都将包含在 diff 操作中。在这种情况下,您需要指定 tablesFilter,找到所有由该扩展创建的表,并在此参数中列出它们。

我们已经解决了这个问题,因此您无需执行所有这些步骤。只需使用所用扩展的名称指定 extensionsFilters,Drizzle 就会跳过所有必要的表。

目前,我们只支持 postgis 选项,但如果将来有其他扩展在 public schema 中创建表,我们计划添加更多支持。

postgis 选项将跳过 geography_columnsgeometry_columnsspatial_ref_sys 表。

import { defineConfig } from 'drizzle-kit'

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

改进

更新数据库凭证的 zod schema,并为所有正/负用例编写测试

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 中索引的任何属性的任何更改触发,因此此处没有限制。